Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,28 +53,3 @@ jobs:
- name: Test on Linux
if: ${{ matrix.os == 'ubuntu-latest' }}
run: xvfb-run pytest --cov=pytest_playwright --cov-report xml
build-conda:
name: Conda Build
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get conda
uses: conda-incubator/setup-miniconda@v3
with:
python-version: "3.10"
channels: microsoft,conda-forge
- name: Prepare
run: |
conda install conda-build conda-verify
# Until https://github.com/anaconda/conda-anaconda-telemetry/issues/87 has been fixed
conda remove --name base conda-anaconda-telemetry
- name: Build pytest-playwright
run: conda build .
- name: Build pytest-playwright-asyncio
run: conda build --variant-config-file conda_build_config_asyncio.yaml .
31 changes: 0 additions & 31 deletions .github/workflows/python-publish.yml

This file was deleted.

1 change: 0 additions & 1 deletion conda_build_config.yaml

This file was deleted.

1 change: 0 additions & 1 deletion conda_build_config_asyncio.yaml

This file was deleted.

44 changes: 0 additions & 44 deletions meta.yaml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import hashlib
import json
import secrets
import shutil
import os
import sys
Expand Down Expand Up @@ -140,6 +141,18 @@ def pytest_configure(config: Any) -> None:
"markers",
"browser_context_args(**kwargs): provide additional arguments to browser.new_context()",
)
if config.getoption("--playwright-debug", default=None) == "cli":
if getattr(config.option, "capture", "fd") != "no":
raise pytest.UsageError(
"--playwright-debug=cli requires disabled output capture "
"(pass -s or --capture=no) so the attach command is visible."
)
worker_count = _xdist_worker_count(config)
if worker_count is not None and worker_count != 1:
raise pytest.UsageError(
"--playwright-debug=cli requires a single worker "
"(do not use pytest-xdist -n > 1)."
)


# Making test result information available in fixtures
Expand Down Expand Up @@ -377,6 +390,7 @@ async def new_context(
additional_context_args = context_args_marker.kwargs if context_args_marker else {}
browser_context_args.update(additional_context_args)
contexts: List[BrowserContext] = []
debug_cli = request.config.getoption("--playwright-debug", default=None) == "cli"

async def _new_context(**kwargs: Any) -> BrowserContext:
context = await browser.new_context(**browser_context_args, **kwargs)
Expand All @@ -388,6 +402,10 @@ async def _close_wrapper(*args: Any, **kwargs: Any) -> None:
await original_close(*args, **kwargs)

context.close = _close_wrapper
if (
debug_cli and not contexts
): # CLI attach drives contexts()[0] only — bind/print/pause on first context.
await _run_debug_cli(browser, context, str(request.config.rootpath))
contexts.append(context)
await _artifacts_recorder.on_did_create_browser_context(context)
return context
Expand Down Expand Up @@ -451,6 +469,44 @@ def device(pytestconfig: Any) -> Optional[str]:
PLUGIN_INCOMPATIBLE_MESSAGE = "pytest-playwright and pytest-playwright-asyncio are not compatible. Please use only one of them."


async def _run_debug_cli(
browser: Browser, context: BrowserContext, workspace_dir: str
) -> None:
if (
not callable(getattr(browser, "bind", None))
or getattr(context, "debugger", None) is None
):
raise pytest.UsageError("--playwright-debug=cli requires playwright>=1.59")

session_name = f"tw-{secrets.token_hex(3)}"
await browser.bind(session_name, workspace_dir=workspace_dir)

print(
"\n### The test is currently paused at the start\n"
"\n"
"### Debugging Instructions\n"
f"- Run `python -m playwright cli attach {session_name}` "
"to attach to this test\n",
flush=True,
)

context.set_default_timeout(0)
context.set_default_navigation_timeout(0)
await context.debugger.request_pause()


def _xdist_worker_count(config: Any) -> Optional[int]:
numprocesses = getattr(config.option, "numprocesses", None)
if numprocesses is None:
return None
if numprocesses == "logical" or numprocesses == "auto":
return 2
try:
return int(numprocesses)
except (TypeError, ValueError):
return 2


def pytest_addoption(
parser: pytest.Parser, pluginmanager: pytest.PytestPluginManager
) -> None:
Expand Down Expand Up @@ -526,6 +582,12 @@ def pytest_addoption(
default=False,
help="Whether to take a full page screenshot",
)
group.addoption(
"--playwright-debug",
default=None,
choices=["cli"],
help="Enable Playwright CLI debugging. Requires -s/--capture=no.",
)


class ArtifactsRecorder:
Expand Down
64 changes: 64 additions & 0 deletions pytest-playwright/pytest_playwright/pytest_playwright.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import hashlib
import json
import secrets
import shutil
import os
import sys
Expand Down Expand Up @@ -137,6 +138,18 @@ def pytest_configure(config: Any) -> None:
"markers",
"browser_context_args(**kwargs): provide additional arguments to browser.new_context()",
)
if config.getoption("--playwright-debug", default=None) == "cli":
if getattr(config.option, "capture", "fd") != "no":
raise pytest.UsageError(
"--playwright-debug=cli requires disabled output capture "
"(pass -s or --capture=no) so the attach command is visible."
)
worker_count = _xdist_worker_count(config)
if worker_count is not None and worker_count != 1:
raise pytest.UsageError(
"--playwright-debug=cli requires a single worker "
"(do not use pytest-xdist -n > 1)."
)


# Making test result information available in fixtures
Expand Down Expand Up @@ -372,6 +385,7 @@ def new_context(
additional_context_args = context_args_marker.kwargs if context_args_marker else {}
browser_context_args.update(additional_context_args)
contexts: List[BrowserContext] = []
debug_cli = request.config.getoption("--playwright-debug", default=None) == "cli"

def _new_context(**kwargs: Any) -> BrowserContext:
context = browser.new_context(**browser_context_args, **kwargs)
Expand All @@ -383,6 +397,10 @@ def _close_wrapper(*args: Any, **kwargs: Any) -> None:
original_close(*args, **kwargs)

context.close = _close_wrapper
if (
debug_cli and not contexts
): # CLI attach drives contexts()[0] only — bind/print/pause on first context.
_run_debug_cli(browser, context, str(request.config.rootpath))
contexts.append(context)
_artifacts_recorder.on_did_create_browser_context(context)
return context
Expand Down Expand Up @@ -446,6 +464,46 @@ def device(pytestconfig: Any) -> Optional[str]:
PLUGIN_INCOMPATIBLE_MESSAGE = "pytest-playwright and pytest-playwright-asyncio are not compatible. Please use only one of them."


def _run_debug_cli(
browser: Browser, context: BrowserContext, workspace_dir: str
) -> None:
if (
not callable(getattr(browser, "bind", None))
or getattr(context, "debugger", None) is None
):
raise pytest.UsageError("--playwright-debug=cli requires playwright>=1.59")

session_name = f"tw-{secrets.token_hex(3)}"
browser.bind(session_name, workspace_dir=workspace_dir)

# Leading newline: with -s, pytest prints "test_foo.py " with no trailing
# newline before fixtures run; start our banner on its own line.
print(
"\n### The test is currently paused at the start\n"
"\n"
"### Debugging Instructions\n"
f"- Run `python -m playwright cli attach {session_name}` "
"to attach to this test\n",
flush=True,
)

context.set_default_timeout(0)
context.set_default_navigation_timeout(0)
context.debugger.request_pause()


def _xdist_worker_count(config: Any) -> Optional[int]:
numprocesses = getattr(config.option, "numprocesses", None)
if numprocesses is None:
return None
if numprocesses == "logical" or numprocesses == "auto":
return 2
try:
return int(numprocesses)
except (TypeError, ValueError):
return 2


def pytest_addoption(
parser: pytest.Parser, pluginmanager: pytest.PytestPluginManager
) -> None:
Expand Down Expand Up @@ -521,6 +579,12 @@ def pytest_addoption(
default=False,
help="Whether to take a full page screenshot",
)
group.addoption(
"--playwright-debug",
default=None,
choices=["cli"],
help="Enable Playwright CLI debugging. Requires -s/--capture=no.",
)


class ArtifactsRecorder:
Expand Down
45 changes: 45 additions & 0 deletions tests/test_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import pytest

from tests.conftest import HTTPTestServer
from tests.utils import attach_snapshot_resume


@pytest.fixture
Expand Down Expand Up @@ -1168,3 +1169,47 @@ async def test_soft(page):
out = "\n".join(result.outlines)
assert "body-fail" in out
assert "soft-fail" in out


def test_playwright_debug_cli_requires_no_capture(testdir: pytest.Testdir) -> None:
testdir.makepyfile(
"""
import pytest
@pytest.mark.asyncio
async def test_dummy():
assert True
"""
)
result = testdir.runpytest("--playwright-debug=cli")
assert result.ret != 0
assert "capture" in "\n".join(result.outlines + result.errlines).lower()


def test_playwright_debug_cli_rejects_multiple_xdist_workers(
testdir: pytest.Testdir,
) -> None:
pytest.importorskip("xdist")
testdir.makepyfile(
"""
import pytest
@pytest.mark.asyncio
async def test_dummy():
assert True
"""
)
result = testdir.runpytest("--playwright-debug=cli", "-s", "-n", "2")
assert result.ret != 0
assert "single worker" in "\n".join(result.outlines + result.errlines)


def test_playwright_debug_cli_attach_snapshot_resume(testdir: pytest.Testdir) -> None:
testdir.makepyfile(
"""
import pytest
@pytest.mark.asyncio
async def test_example(page):
await page.set_content("<button>Hi</button>")
assert await page.get_by_role("button").is_visible()
"""
)
attach_snapshot_resume(testdir)
Loading
Loading