diff --git a/poetry.lock b/poetry.lock index e25dfe7..89c1737 100644 --- a/poetry.lock +++ b/poetry.lock @@ -23,10 +23,10 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] [[package]] name = "colorama" @@ -79,7 +79,7 @@ pyflakes = ">=2.3.0,<2.4.0" [[package]] name = "importlib-metadata" -version = "4.5.0" +version = "4.6.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -91,7 +91,8 @@ zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +perf = ["ipython"] [[package]] name = "iniconfig" @@ -212,15 +213,15 @@ optional = false python-versions = ">=3.6" [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=19.2.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<1.0.0a1" py = ">=1.8.2" +iniconfig = "*" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +colorama = {version = "*", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" toml = "*" +pluggy = ">=0.12,<1.0.0a1" +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] @@ -234,9 +235,9 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] -coverage = ">=5.2.1" pytest = ">=4.6" toml = "*" +coverage = ">=5.2.1" [package.extras] testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] @@ -299,15 +300,15 @@ optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] -colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} -filelock = ">=3.0.0" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" packaging = ">=14" -pluggy = ">=0.12.0" +filelock = ">=3.0.0" py = ">=1.4.17" -six = ">=1.14.0" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} toml = ">=0.9.4" -virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" +six = ">=1.14.0" +pluggy = ">=0.12.0" [package.extras] docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] @@ -323,7 +324,7 @@ python-versions = "*" [[package]] name = "urllib3" -version = "1.26.5" +version = "1.26.6" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -343,11 +344,11 @@ optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" [package.dependencies] -appdirs = ">=1.4.3,<2" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} distlib = ">=0.3.1,<1" filelock = ">=3.0.0,<4" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} six = ">=1.9.0,<2" +appdirs = ">=1.4.3,<2" [package.extras] docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] @@ -454,8 +455,8 @@ flake8 = [ {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.5.0-py3-none-any.whl", hash = "sha256:833b26fb89d5de469b24a390e9df088d4e52e4ba33b01dc5e0e4f41b81a16c00"}, - {file = "importlib_metadata-4.5.0.tar.gz", hash = "sha256:b142cc1dd1342f31ff04bb7d022492b09920cb64fed867cd3ea6f80fe3ebd139"}, + {file = "importlib_metadata-4.6.0-py3-none-any.whl", hash = "sha256:c6513572926a96458f8c8f725bf0e00108fba0c9583ade9bd15b869c9d726e33"}, + {file = "importlib_metadata-4.6.0.tar.gz", hash = "sha256:4a5611fea3768d3d967c447ab4e93f567d95db92225b43b7b238dbfb855d70bb"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, @@ -591,8 +592,8 @@ typing-extensions = [ {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, ] urllib3 = [ - {file = "urllib3-1.26.5-py2.py3-none-any.whl", hash = "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c"}, - {file = "urllib3-1.26.5.tar.gz", hash = "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"}, + {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, + {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, ] virtualenv = [ {file = "virtualenv-20.4.7-py2.py3-none-any.whl", hash = "sha256:2b0126166ea7c9c3661f5b8e06773d28f83322de7a3ff7d06f0aed18c9de6a76"}, diff --git a/pyproject.toml b/pyproject.toml index 0007fb8..1680af2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,9 @@ [tool.poetry] name = "uw-webdriver-recorder" -version = "4.0.0" +# This version string is typically managed by the CI workflow, +# and is changed anytime `poetry version [new version]` is run. +# Do not revert this manually. +version = "4.0.1-alpha.1" description = "A pytest plugin for recording screenshots of selenium interactions, with other convenient features too." authors = ["Tom Thorogood "] license = "Apache Software License 3.0" diff --git a/webdriver_recorder/plugin.py b/webdriver_recorder/plugin.py index a0e0d4d..e623496 100644 --- a/webdriver_recorder/plugin.py +++ b/webdriver_recorder/plugin.py @@ -13,10 +13,11 @@ import jinja2 import pytest +from _pytest.fixtures import FixtureRequest from pydantic import BaseModel, root_validator from selenium import webdriver -from .browser import BrowserError, Chrome, Remote +from .browser import BrowserError, BrowserRecorder, Chrome, Remote TEMPLATE_FILE = os.path.join( os.path.abspath(os.path.dirname(__file__)), "report.template.html" @@ -33,7 +34,7 @@ def pytest_addoption(parser): "--selenium-server", action="store", dest="selenium_server", - default=os.environ.get("SELENIUM_SERVER"), + default=None, help="Remote selenium webdriver to connect to (eg localhost:4444)", ) group.addoption( @@ -112,7 +113,15 @@ def __init__(self, report, excinfo, doc): @pytest.fixture(scope="session") def selenium_server(request) -> Optional[str]: - return request.config.getoption("selenium_server", None) + """Returns a non-empty string or None""" + return ( + # CLI arg takes precedence + request.config.getoption("selenium_server") + # Otherwise, we look for a non-empty string + or os.environ.get('SELENIUM_SERVER', '').strip() + # If the result is still Falsey, we always return None. + or None + ) @pytest.fixture(scope="session") @@ -146,7 +155,7 @@ def session_browser(selenium_server, chrome_options): @pytest.fixture(scope="session") -def browser_context(session_browser) -> Callable[..., Chrome]: +def browser_context(request: FixtureRequest) -> Callable[..., Chrome]: """ This fixture allows you to create a fresh context for a given browser instance. @@ -168,7 +177,11 @@ def test_something(browser_context): def inner( browser: Optional[Chrome] = None, cookie_urls: Optional[List[str]] = None ): - browser = browser or session_browser + if not browser: + # Only loads this fixture if no override is present + # to avoid creating session_browsers if the + # dependent does not to. + browser = request.getfixturevalue('session_browser') browser.open_tab() cookie_urls = cookie_urls or [] try: @@ -191,7 +204,13 @@ def class_browser(request, browser_context) -> Chrome: @pytest.fixture -def browser(browser_context) -> Chrome: +def browser(browser_context) -> BrowserRecorder: + """ + The default browser fixture. This default behavior will lazily + instantiate a `session_browser` if one does not exist. To + override this behavior and create your own context, you can + redefine this fixture. + """ with browser_context() as browser: yield browser @@ -217,7 +236,7 @@ def report_generator(generate_report, report_dir) -> str: @pytest.fixture(autouse=True) -def report_test(report_generator, request, session_browser): +def report_test(report_generator, request): """ Print the results to report_file after a test run. Without this, the results of the test will not be saved. You can ensure this is always run by including the following in your conftest.py: @@ -273,7 +292,7 @@ def report_test(report_test): link=slug, doc=doc, nodeid=nodeid, - pngs=session_browser.pngs, + pngs=BrowserRecorder.pngs, failure=failure, time1=time1, time2=time2, @@ -286,7 +305,7 @@ def report_test(report_test): fd.write(header.json()) with open(filename, "w") as fd: fd.write(report.json()) - session_browser.pngs.clear() + BrowserRecorder.pngs.clear() @pytest.hookimpl(tryfirst=True, hookwrapper=True) @@ -313,12 +332,12 @@ def lettergen(): @pytest.fixture(scope="session") -def report_title(request, session_browser) -> str: - return request.config.getoption("report_title", session_browser.current_url) +def report_title(request) -> str: + return request.config.getoption("report_title", default="Webdriver Recorder Summary") @pytest.fixture(scope="session", autouse=True) -def generate_report(template_filename, session_browser, report_title, report_dir): +def generate_report(template_filename, report_title, report_dir): """ Uses the included HTML template to generate the final report, using the results found in `report_dir`. Can be called explicitly in order to do this at any time.