Comments (4)
I put together a working solution that leverages the plugin as much as currently possible:
conftest.py:
@pytest.fixture
def device_name(request, device):
"""
Collect all specified devices for a test from a pytest.mark.parametrize("device_name", [..]) marker
"""
try:
return request.param
except AttributeError:
return device
@pytest.fixture
def more_context_args(playwright, browser_name, device_name):
"""
Alter context arguments on a test level
"""
context_args = {}
if device_name not in playwright.devices:
context_args.setdefault("viewport", {"width": 1920, "height": 1080})
else:
context_args.update(playwright.devices[device_name])
# handle staging auth
context_args.setdefault("http_credentials", {"username": AUTH_USERNAME, "password": AUTH_PASSWORD})
return context_args
def pytest_collection_modifyitems(items, config):
"""
This is a standard pytest hook that runs after tests have been identified and collected, but need further filtering
We will leverage this hook to filter out incompatible combinations of browsers and devices.
"""
# since this is collection stage, we don't have access to fixtures yet, so we'll have to start and stop playwright
# manually to access the devices list
playwright_context = sync_playwright().start()
devices = playwright_context.devices
# we're going to collect tests that run on incompatible combinations here
deselected_items = []
for item in items.copy():
# Get browser name from the plugin browser_name fixture
browser_name = item.callspec.getparam("browser_name")
# Handle only_browser and skip_browser plugin markers to prevent the SKIPPED tests noise in the reporting
only_browser_marker = item.get_closest_marker("only_browser")
skip_browser_marker = item.get_closest_marker("skip_browser")
if (only_browser_marker and browser_name != only_browser_marker.args[0]) or (
skip_browser_marker and browser_name == skip_browser_marker.args[0]
):
deselected_items.append(item)
continue
# Get device name from pytest.mark.parametrize('device_name',[...]) marker, if it's specified
try:
device_name = item.callspec.getparam("device_name")
except ValueError:
device_name = ""
# Get device dictionary settings for specified devices
device_info = None
if device_name:
device_info = devices[device_name]
# Check invalid devices-browser combinations and mark them as deselected
invalid_browser_device_combinations = [
(device_info and browser_name == "firefox" and device_info.get("is_mobile", False)),
(device_info and browser_name == "webkit" and device_info["default_browser_type"] != "webkit"),
(device_info and browser_name == "chromium" and device_info["default_browser_type"] != "chromium"),
(browser_name == "firefox" and "Chrome" in device_name),
(browser_name == "firefox" and "Edge" in device_name),
(browser_name == "chromium" and "Safari" in device_name),
(browser_name == "firefox" and "Safari" in device_name)
]
if any(invalid_browser_device_combinations):
deselected_items.append(item)
continue
# filter out the deselected items and leave only relevant ones
selected_items = [i for i in items if i not in deselected_items]
config.hook.pytest_deselected(items=deselected_items)
playwright_context.stop()
items[:] = selected_items
@pytest.fixture
def context(
browser: Browser,
browser_context_args: Dict,
pytestconfig: Any,
request: pytest.FixtureRequest,
more_context_args: Dict,
) -> Generator[BrowserContext, None, None]:
pages: List[Page] = []
browser_context_args.update(more_context_args)
context = browser.new_context(**browser_context_args)
# Collect every page that's opened in the browser context into the pages list.
context.on("page", lambda page: pages.append(page))
tracing_option = pytestconfig.getoption("--tracing")
capture_trace = tracing_option in ["on", "retain-on-failure"]
if capture_trace:
context.tracing.start(
title=slugify(request.node.nodeid),
screenshots=True,
snapshots=True,
sources=True,
)
yield context
# If requst.node is missing rep_call, then some error happened during execution
# that prevented teardown, but should still be counted as a failure
failed = request.node.rep_call.failed if hasattr(request.node, "rep_call") else True
if capture_trace:
retain_trace = tracing_option == "on" or (failed and tracing_option == "retain-on-failure")
if retain_trace:
trace_path = _build_artifact_test_folder(pytestconfig, request, "trace.zip")
context.tracing.stop(path=trace_path)
else:
context.tracing.stop()
screenshot_option = pytestconfig.getoption("--screenshot")
capture_screenshot = screenshot_option == "on" or (failed and screenshot_option == "only-on-failure")
# handle multi-page tests, by saving a screenshot of each individual page
if capture_screenshot:
for index, page in enumerate(pages):
human_readable_status = "failed" if failed else "finished"
screenshot_path = _build_artifact_test_folder(
pytestconfig, request, f"test-{human_readable_status}-{index+1}.png"
)
try:
page.screenshot(timeout=5000, path=screenshot_path)
except Error:
pass
context.close()
test_xxx.py file:
pytest.mark.parametrize(
"device_name",
[
"Desktop Chrome",
"Desktop Edge",
"Desktop Firefox",
"Desktop Safari",
"iPad Pro 11",
"Galaxy Tab S4 landscape",
"iPhone 13 Pro landscape",
"iPhone 12 Pro Max",
"Pixel 5",
],
indirect=True,
)
def test_xxx(reachmd_homepage):
pass
CLI: pytest tests --browser chromium --browser firefox --browser webkit -k xxx --co
Output:
<Function test_xxx[chromium-Desktop Chrome]>
<Function test_xxx[chromium-Desktop Edge]>
<Function test_xxx[chromium-iPad Pro 11]>
<Function test_xxx[chromium-Galaxy Tab S4 landscape]>
<Function test_xxx[chromium-iPhone 13 Pro landscape]>
<Function test_xxx[chromium-iPhone 12 Pro Max]>
<Function test_xxx[chromium-Pixel 5]>
<Function test_xxx[firefox-Desktop Firefox]>
<Function test_xxx[webkit-Desktop Safari]>
<Function test_xxx[webkit-iPad Pro 11]>
<Function test_xxx[webkit-iPhone 13 Pro landscape]>
<Function test_xxx[webkit-iPhone 12 Pro Max]>
from playwright-pytest.
Possible improvements(overkill?) to this:
- Have a way to force default test execution on whatever desktop browsers are specified in the CLI, alongside the device_name marker values, if any. That will remove the need to specify
"Desktop <browser>"
manually for tests that need to run on both. - Add any device specified in the CLI via --device to the
device_name
params list and run the test on all devices specified via marker or CLI param
from playwright-pytest.
I created the following test to reproduce your use, but for me it was working, what did I do differently?
def test_different_browser_context_args_per_module(testdir: pytest.Testdir) -> None:
module_a_path = testdir.mkpydir("moduleA")
module_a_submodule_path = testdir.mkpydir("moduleA/submodule")
# module_a_path.join("conftest.py").write("""
# import pytest
# @pytest.fixture(scope="session")
# def browser_context_args():
# return {"user_agent": "module_a"}
# """)
module_a_submodule_path.join("conftest.py").write("""
import pytest
@pytest.fixture(scope="session")
def browser_context_args(browser_context_args):
browser_context_args.setdefault("user_agent", "module_a_submodule")
return browser_context_args
""")
module_a_submodule_path.join("test_foo.py").write(
"""
def test_browser_context_args1(page):
assert page.evaluate("window.navigator.userAgent") == "module_a_submodule"
def test_browser_context_args2(page):
assert page.evaluate("window.navigator.userAgent") == "module_a_submodule"
"""
)
result = testdir.runpytest()
result.assert_outcomes(passed=2)
You can put it inside here to run the test. For the other suggestions above I recommend creating own feature requests, so that we track here only the bug report about the wrong scope.
from playwright-pytest.
Closing as per above. Please re-file if you are still encountering issues.
from playwright-pytest.
Related Issues (20)
- Setting locale for a test affects the local for other tests HOT 1
- Coverage support HOT 1
- [BUG] Error when installing in Python 3.12 HOT 2
- Pytest.ini set --screenshot="only-on-failure" --video="retain-on-failure" Screenshots and recorded videos will also be generated after the use case is successfully executed.
- pytest-playwright--parameterization, cases order Not what I expected HOT 8
- playwright._impl._api_types.Error: net::ERR_CONNECTION_TIMED_OUT at https://www.google.com HOT 2
- [FEATURE] Support connecting to remote browsers HOT 1
- Request: `async` support HOT 4
- [FEATURE] Support pytest 8.x HOT 3
- [BUG]The execution sequence is incorrect when use parametrize HOT 1
- Request: extending `browser_type_launch_args` to work with `request`
- [FEATURE] Support for multiple browser contexts in a single test HOT 1
- [FR] Support for working with pytest-rerunfailures to only create --output on true failure
- Allow extending playwright capabilities HOT 2
- run with --video on --screenshot on how to set screenshot dir and how to get videoname and screenshot name? HOT 1
- pytest-playwirght uses --video="on" to record video,the recorded video only records the newly opened tab page HOT 5
- Move creation of artifacts_folder into a fixture HOT 2
- [BUG]PermissionError: [WinError 5]
- [FEATURE] Implement steps for Trace Viewer HOT 1
- [Feature]: Expose output_dir as a fixture HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from playwright-pytest.