From a3e87df9a8c1d2efc510f77d5c298d25a9680eae Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 31 Jul 2023 13:58:53 +0200 Subject: [PATCH 1/2] ENH Add rename_fixture and standalone decorators We use a bunch of different selenium variants but for the most part they are drop-in replacements for each other. For instance, when using `selenium_standalone`, we always just say `selenium = selenium_standalone` at the top of the function. I think it's clearer if we use a decorator to rename the fixture. --- pytest_pyodide/fixture.py | 27 +++++++++++++++++++++++++++ tests/test_fixture.py | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/pytest_pyodide/fixture.py b/pytest_pyodide/fixture.py index babf3fa7..d858495a 100644 --- a/pytest_pyodide/fixture.py +++ b/pytest_pyodide/fixture.py @@ -1,4 +1,6 @@ import contextlib +import functools +import inspect import os from pathlib import Path from typing import Any @@ -117,6 +119,31 @@ def selenium_common( runner.quit() +def rename_fixture(orig_name, new_name): + def use_variant(f): + sig = inspect.signature(f) + new_params = [] + for p in sig.parameters.values(): + if p.name == orig_name: + p = p.replace(name=new_name) + new_params.append(p) + new_sig = sig.replace(parameters=new_params) + + @functools.wraps(f) + def wrapper(*args, **kwargs): + if new_name in kwargs: + kwargs[orig_name] = kwargs.pop(new_name) + return f(*args, **kwargs) + + wrapper.__signature__ = new_sig # type:ignore[attr-defined] + return wrapper + + return use_variant + + +standalone = rename_fixture("selenium", "selenium_standalone") + + @pytest.fixture(scope="function") def selenium_standalone(request, runtime, web_server_main, playwright_browsers): with selenium_common( diff --git a/tests/test_fixture.py b/tests/test_fixture.py index 4128fab8..ee0175ca 100644 --- a/tests/test_fixture.py +++ b/tests/test_fixture.py @@ -1,6 +1,7 @@ import pytest from pytest_pyodide.decorator import run_in_pyodide +from pytest_pyodide.fixture import rename_fixture @pytest.mark.parametrize("dummy", [1, 2, 3]) @@ -24,3 +25,29 @@ def test_playwright_browsers(playwright_browsers, request): runtimes = pytest.pyodide_runtimes assert set(playwright_browsers.keys()) == set(runtimes) + + +@rename_fixture("myfixture", "myfixture_variant") +def myfunc(a, myfixture): + return [a, myfixture] + + +def test_rename_fixture1(): + assert myfunc(2, 3) == [2, 3] + assert myfunc(2, myfixture_variant=3) == [2, 3] + assert myfunc(a=2, myfixture_variant=3) == [2, 3] + + +@pytest.fixture +def myfixture(): + yield 27 + + +@pytest.fixture +def myfixture_variant(): + yield 99 + + +@rename_fixture("myfixture", "myfixture_variant") +def test_rename_fixture2(myfixture): + assert myfixture == 99 From 616d60c86dde88e4a6e4aa9f633c434362ed5cd8 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 31 Jul 2023 16:27:43 +0200 Subject: [PATCH 2/2] Add test for _has_standalone_fixture --- pytest_pyodide/hook.py | 31 ++++++++++++++++--------------- tests/test_fixture.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/pytest_pyodide/hook.py b/pytest_pyodide/hook.py index 590866b7..15a1fa01 100644 --- a/pytest_pyodide/hook.py +++ b/pytest_pyodide/hook.py @@ -170,6 +170,21 @@ def pytest_generate_tests(metafunc: Any) -> None: metafunc.parametrize("runtime", pytest.pyodide_runtimes, scope="module") +STANDALONE_FIXTURES = [ + "selenium_standalone", + "selenium_standalone_noload", + "selenium_webworker_standalone", +] + + +def _has_standalone_fixture(item): + for fixture in item._request.fixturenames: + if fixture in STANDALONE_FIXTURES: + return True + + return False + + def pytest_collection_modifyitems(items: list[Any]) -> None: # if we are running tests in pyodide, then run all tests for each runtime if len(items) > 0 and items[0].config.option.run_in_pyodide: @@ -186,28 +201,14 @@ def pytest_collection_modifyitems(items: list[Any]) -> None: # Since Safari doesn't support more than one simultaneous session, we run all # selenium_standalone Safari tests first. We preserve the order of other # tests. - OFFSET = 10000 - counter = [0] - standalone_fixtures = [ - "selenium_standalone", - "selenium_standalone_noload", - "selenium_webworker_standalone", - ] - - def _has_standalone_fixture(fixturenames): - for fixture in fixturenames: - if fixture in standalone_fixtures: - return True - - return False def _get_item_position(item): counter[0] += 1 if any( [re.match(r"^safari[\-$]?", el) for el in item.keywords._markers.keys()] - ) and _has_standalone_fixture(item._request.fixturenames): + ) and _has_standalone_fixture(item): return counter[0] - OFFSET return counter[0] diff --git a/tests/test_fixture.py b/tests/test_fixture.py index ee0175ca..7529bb79 100644 --- a/tests/test_fixture.py +++ b/tests/test_fixture.py @@ -2,6 +2,7 @@ from pytest_pyodide.decorator import run_in_pyodide from pytest_pyodide.fixture import rename_fixture +from pytest_pyodide.hook import _has_standalone_fixture @pytest.mark.parametrize("dummy", [1, 2, 3]) @@ -51,3 +52,30 @@ def myfixture_variant(): @rename_fixture("myfixture", "myfixture_variant") def test_rename_fixture2(myfixture): assert myfixture == 99 + + +def test_has_standalone_fixture(pytester): + from textwrap import dedent + + pytester.makepyfile( + dedent( + """ + from pytest_pyodide.fixture import rename_fixture + + @rename_fixture("selenium", "selenium_standalone") + def test_example1(selenium): + pass + + @rename_fixture("selenium_standalone", "selenium_standalone1") + def test_example2(selenium_standalone): + pass + """ + ) + ) + node = pytester.getpathnode("test_has_standalone_fixture.py") + r = node.collect() + t1, t2 = r + assert "test_example1" in t1.name + assert "test_example2" in t2.name + assert _has_standalone_fixture(t1) + assert not _has_standalone_fixture(t2)