110 lines
4.3 KiB
Python
110 lines
4.3 KiB
Python
import os
|
|
import shutil
|
|
from collections.abc import Generator
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from selenium import webdriver
|
|
from selenium.webdriver.chrome.service import Service
|
|
|
|
from .general_fixtures import ( # noqa: F401 # pylint: disable=unused-import
|
|
nicegui_reset_globals,
|
|
pytest_addoption,
|
|
pytest_configure,
|
|
)
|
|
from .screen import Screen
|
|
|
|
# pylint: disable=redefined-outer-name
|
|
|
|
DOWNLOAD_DIR = Path(__file__).parent / 'download'
|
|
|
|
|
|
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
|
|
def pytest_runtest_makereport(item, call): # pylint: disable=unused-argument
|
|
"""Store test outcome in the node for fixture access."""
|
|
outcome = yield
|
|
rep = outcome.get_result()
|
|
setattr(item, f'rep_{rep.when}', rep)
|
|
|
|
|
|
@pytest.fixture
|
|
def nicegui_chrome_options(chrome_options: webdriver.ChromeOptions) -> webdriver.ChromeOptions:
|
|
"""Configure the Chrome options for the NiceGUI tests."""
|
|
chrome_options.add_argument('disable-dev-shm-usage')
|
|
chrome_options.add_argument('disable-search-engine-choice-screen')
|
|
chrome_options.add_argument('no-sandbox')
|
|
chrome_options.add_argument('headless')
|
|
chrome_options.add_argument('disable-gpu' if 'GITHUB_ACTIONS' in os.environ else '--use-gl=angle')
|
|
chrome_options.add_argument('window-size=600x600')
|
|
chrome_options.add_experimental_option('prefs', {
|
|
'download.default_directory': str(DOWNLOAD_DIR),
|
|
'download.prompt_for_download': False, # To auto download the file
|
|
'download.directory_upgrade': True,
|
|
})
|
|
if 'CHROME_BINARY_LOCATION' in os.environ:
|
|
chrome_options.binary_location = os.environ['CHROME_BINARY_LOCATION']
|
|
return chrome_options
|
|
|
|
|
|
@pytest.fixture
|
|
def capabilities(capabilities: dict) -> dict:
|
|
"""Configure the Chrome driver capabilities."""
|
|
capabilities['goog:loggingPrefs'] = {'browser': 'ALL'}
|
|
return capabilities
|
|
|
|
|
|
@pytest.fixture(scope='session')
|
|
def nicegui_remove_all_screenshots() -> None:
|
|
"""Remove all screenshots from the screenshot directory before the test session."""
|
|
for name in Screen.SCREENSHOT_DIR.glob('*.png'):
|
|
name.unlink()
|
|
|
|
|
|
@pytest.fixture()
|
|
def nicegui_driver(nicegui_chrome_options: webdriver.ChromeOptions) -> Generator[webdriver.Chrome, None, None]:
|
|
"""Create a new Chrome driver instance."""
|
|
for executable_path in (None, shutil.which('chromedriver'), 'chromedriver'): # Required for ARM devcontainers
|
|
try:
|
|
s = Service(executable_path=executable_path)
|
|
driver_ = webdriver.Chrome(service=s, options=nicegui_chrome_options)
|
|
break
|
|
except Exception:
|
|
continue
|
|
else: # no break
|
|
raise RuntimeError('Could not start Chrome WebDriver.')
|
|
driver_.implicitly_wait(Screen.IMPLICIT_WAIT)
|
|
driver_.set_page_load_timeout(4)
|
|
yield driver_
|
|
driver_.quit()
|
|
|
|
|
|
@pytest.fixture
|
|
def screen(nicegui_reset_globals, # noqa: F811, pylint: disable=unused-argument
|
|
nicegui_remove_all_screenshots, # pylint: disable=unused-argument
|
|
nicegui_driver: webdriver.Chrome,
|
|
request: pytest.FixtureRequest,
|
|
caplog: pytest.LogCaptureFixture,
|
|
) -> Generator[Screen, None, None]:
|
|
"""Create a new SeleniumScreen fixture."""
|
|
os.environ['NICEGUI_SCREEN_TEST_PORT'] = str(Screen.PORT)
|
|
screen_ = Screen(nicegui_driver, caplog, request)
|
|
try:
|
|
yield screen_
|
|
|
|
logs = [record for record in screen_.caplog.get_records('call') if record.levelname == 'ERROR']
|
|
if screen_.is_open:
|
|
test_failed = hasattr(request.node, 'rep_call') and request.node.rep_call.failed
|
|
screen_.shot(request.node.name, failed=test_failed or bool(logs))
|
|
if logs:
|
|
pytest.fail('There were unexpected ERROR logs.', pytrace=False)
|
|
if screen_.is_open and Screen.CATCH_JS_ERRORS:
|
|
for js_error in screen_.selenium.get_log('browser'):
|
|
if str(js_error.get('level', '')).upper() in ('SEVERE', 'ERROR') and \
|
|
not any(allowed_error in js_error['message'] for allowed_error in screen_.allowed_js_errors):
|
|
pytest.fail(f'JavaScript console error:\n{js_error}', pytrace=False)
|
|
finally:
|
|
os.environ.pop('NICEGUI_SCREEN_TEST_PORT', None)
|
|
screen_.stop_server()
|
|
if DOWNLOAD_DIR.exists():
|
|
shutil.rmtree(DOWNLOAD_DIR)
|