1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
|
# SPDX-License-Identifier: AGPL-3.0-or-later
"""
pytest configuration for all tests.
"""
import importlib
import os
import pathlib
import sys
from unittest.mock import patch
import pytest
try:
importlib.import_module('splinter')
importlib.import_module('selenium')
_functional_libs_available = True
except ImportError:
_functional_libs_available = False
def pytest_ignore_collect(path, config):
"""Ignore functional tests when splinter is not available."""
if path.basename == 'test_functional.py':
return not _functional_libs_available
def pytest_addoption(parser):
"""Add a command line option to run functional tests."""
parser.addoption('--include-functional', action='store_true',
default=False, help='Run functional tests also')
def pytest_collection_modifyitems(config, items):
"""Filter out specificly marked tests unless explicitly requested.
The EXTENDED_TESTING environment variable is borrowed from the Lancaster
consensus met by the Pearl community. See
https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md
"""
def skip(item, reason):
item.add_marker(pytest.mark.skip(reason=reason))
extended = 'EXTENDED_TESTING' in os.environ
if not (extended or config.getoption('--include-functional')):
for item in items:
if 'functional' in item.keywords or (
item.parent.fspath.basename
and item.parent.fspath.basename == 'test_functional.py'):
skip(item, '--include-functional not provided')
if not extended:
for item in items:
if 'heavy' in item.keywords:
skip(item, ('Takes too much time. '
'Set EXTENDED_TESTING=1 to force run'))
@pytest.fixture(name='load_cfg')
def fixture_load_cfg():
"""Load test configuration."""
from plinth import cfg
keys = ('file_root', 'config_dir', 'data_dir', 'custom_static_dir',
'store_file', 'actions_dir', 'doc_dir', 'server_dir', 'host',
'port', 'use_x_forwarded_for', 'use_x_forwarded_host',
'secure_proxy_ssl_header', 'box_name', 'develop')
saved_state = {}
for key in keys:
saved_state[key] = getattr(cfg, key)
root_dir = pathlib.Path(__file__).resolve().parent
cfg_file = root_dir / 'plinth' / 'develop.config'
cfg.read_file(str(cfg_file))
yield cfg
for key in keys:
setattr(cfg, key, saved_state[key])
@pytest.fixture(name='develop_mode')
def fixture_develop_mode(load_cfg):
"""Turn on development mode for a test."""
load_cfg.develop = True
yield
load_cfg.develop = False
@pytest.fixture(name='needs_root', scope='session')
def fixture_needs_root():
"""Skip test if not running in root mode."""
if os.geteuid() != 0:
pytest.skip('Needs to be root')
@pytest.fixture(name='needs_not_root', scope='session')
def fixture_needs_not_root():
"""Skip test if running in root mode."""
if os.geteuid() == 0:
pytest.skip('Needs not to be root')
@pytest.fixture(name='needs_sudo')
def fixture_needs_sudo():
"""Skip test if sudo command is not available."""
if not os.path.isfile('/usr/bin/sudo'):
pytest.skip('Needs sudo command installed.')
@pytest.fixture(scope='session')
def splinter_selenium_implicit_wait():
"""Disable implicit waiting."""
return 0
@pytest.fixture(scope='session')
def splinter_wait_time():
"""Disable explicit waiting."""
return 0.01
@pytest.fixture(scope='session')
def splinter_browser_load_condition():
"""When a page it loaded, wait until <body> is available."""
def _load_condition(browser):
if browser.url == 'about:blank':
return True
ready_state = browser.execute_script('return document.readyState;')
return ready_state == 'complete'
return _load_condition
@pytest.fixture(name='actions_module', scope='module')
def fixture_actions_module(request):
"""Import and return an action module."""
actions_name = getattr(request.module, 'actions_name')
actions_file = str(
pathlib.Path(__file__).parent / 'actions' / actions_name)
loader = importlib.machinery.SourceFileLoader(actions_name, actions_file)
spec = importlib.util.spec_from_loader(actions_name, loader)
module = importlib.util.module_from_spec(spec)
sys.modules[actions_name] = module
spec.loader.exec_module(module)
return module
@pytest.fixture(name='mock_privileged')
def fixture_mock_privileged(request):
"""Mock the privileged decorator to nullify its effects."""
try:
privileged_modules_to_mock = request.module.privileged_modules_to_mock
except AttributeError:
raise AttributeError(
'mock_privileged fixture requires "privileged_module_to_mock" '
'attribute at module level')
for module_name in privileged_modules_to_mock:
module = importlib.import_module(module_name)
for name, member in module.__dict__.items():
wrapped = getattr(member, '__wrapped__', None)
if not callable(member) or not wrapped:
continue
if not getattr(member, '_privileged', False):
continue
setattr(wrapped, '_original_wrapper', member)
module.__dict__[name] = wrapped
yield
for module_name in privileged_modules_to_mock:
module = importlib.import_module(module_name)
for name, member in module.__dict__.items():
wrapper = getattr(member, '_original_wrapper', None)
if not callable(member) or not wrapper:
continue
module.__dict__[name] = wrapper
@pytest.fixture(name='splinter_screenshot_dir', scope='session')
def fixture_splinter_screenshot_dir(request):
"""Set default screenshot directory to ./screenshots.
This can be overridden using --splinter-screenshot-dir=foo as the option.
"""
option = request.config.getoption('--splinter-screenshot-dir')
screenshots_dir = option if option != '.' else './screenshots'
return os.path.abspath(screenshots_dir)
@pytest.fixture(autouse=True)
def fixture_fix_session_browser_screenshots(request):
"""Fix a bug in pytest-splinter for screenshots.
When using session_browser, pytest-splinter does not take a screenshot when
a test has failed. It is uses internal pytest API on the FixtureRequest
object. This API was removed in later versions of pytest causing the
failure. Re-implement the fixture that has the problem fixing this issue.
Drop this fixture after a fix is merged and released in pytest-splinter.
See: https://github.com/pytest-dev/pytest-splinter/pull/157
"""
yield
if not request.config.pluginmanager.has_plugin('pytest-splinter'):
return
session_tmpdir = request.getfixturevalue('session_tmpdir')
splinter_session_scoped_browser = request.getfixturevalue(
'splinter_session_scoped_browser')
splinter_make_screenshot_on_failure = request.getfixturevalue(
'splinter_make_screenshot_on_failure')
splinter_screenshot_dir = request.getfixturevalue(
'splinter_screenshot_dir')
splinter_screenshot_getter_html = request.getfixturevalue(
'splinter_screenshot_getter_html')
splinter_screenshot_getter_png = request.getfixturevalue(
'splinter_screenshot_getter_png')
splinter_screenshot_encoding = request.getfixturevalue(
'splinter_screenshot_encoding')
# Screenshot for function scoped browsers is handled in
# browser_instance_getter
if not splinter_session_scoped_browser:
return
for name in request.fixturenames:
fixture_def = request._fixture_defs.get(name)
if not fixture_def or not fixture_def.cached_result:
continue
value = fixture_def.cached_result[0]
should_take_screenshot = (hasattr(value, "__splinter_browser__")
and splinter_make_screenshot_on_failure
and getattr(request.node, 'splinter_failure',
True))
from pytest_splinter import plugin
if should_take_screenshot:
plugin._take_screenshot(
request=request,
fixture_name=name,
session_tmpdir=session_tmpdir,
browser_instance=value,
splinter_screenshot_dir=splinter_screenshot_dir,
splinter_screenshot_getter_html=splinter_screenshot_getter_html,
splinter_screenshot_getter_png=splinter_screenshot_getter_png,
splinter_screenshot_encoding=splinter_screenshot_encoding,
)
|