From 5c4b7b562445b5aa2bb31549a7f20fdc5b8ac543 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Mon, 18 May 2026 14:15:58 +0200 Subject: [PATCH 1/3] chore: require playwright>=1.60 in CI and unskip 1.60 tests Playwright Python 1.60 has been released, so the tests gated on it can now run. Pin the dev dependency in local-requirements.txt so CI installs a compatible version; the package's runtime requirement is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- local-requirements.txt | 1 + tests/test_asyncio.py | 4 ---- tests/test_sync.py | 4 ---- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/local-requirements.txt b/local-requirements.txt index f140b55..34ddf8e 100644 --- a/local-requirements.txt +++ b/local-requirements.txt @@ -9,3 +9,4 @@ pre-commit==4.0.1 Django==4.2.24 pytest-xdist==3.8.0 pytest-asyncio==1.3.0 +playwright>=1.60 diff --git a/tests/test_asyncio.py b/tests/test_asyncio.py index 5735162..7531e8d 100644 --- a/tests/test_asyncio.py +++ b/tests/test_asyncio.py @@ -1045,7 +1045,6 @@ def test_with_page(page): ] -@pytest.mark.skip(reason="requires 1.60") def test_connect_options_should_work(testdir: pytest.Testdir) -> None: server_process = None try: @@ -1111,7 +1110,6 @@ async def test_soft(page): assert any("goodbye" in line for line in result.outlines) -@pytest.mark.skip(reason="requires 1.60") def test_soft_assertion_multiple_failures_exception_group( testdir: pytest.Testdir, ) -> None: @@ -1133,7 +1131,6 @@ async def test_soft(page): assert "first" in out and "second" in out -@pytest.mark.skip(reason="requires 1.60") def test_soft_assertion_passes_when_all_match(testdir: pytest.Testdir) -> None: testdir.makepyfile( """ @@ -1150,7 +1147,6 @@ async def test_soft(page): result.assert_outcomes(passed=1) -@pytest.mark.skip(reason="requires 1.60") def test_soft_assertion_does_not_shadow_body_failure( testdir: pytest.Testdir, ) -> None: diff --git a/tests/test_sync.py b/tests/test_sync.py index 78edc22..a5ff586 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -1035,7 +1035,6 @@ def test_with_page(page): ] -@pytest.mark.skip(reason="requires 1.60") def test_connect_options_should_work(testdir: pytest.Testdir) -> None: server_process = None try: @@ -1095,7 +1094,6 @@ def test_soft(page): assert any("goodbye" in line for line in result.outlines) -@pytest.mark.skip(reason="requires 1.60") def test_soft_assertion_multiple_failures_exception_group( testdir: pytest.Testdir, ) -> None: @@ -1115,7 +1113,6 @@ def test_soft(page): assert "first" in out and "second" in out -@pytest.mark.skip(reason="requires 1.60") def test_soft_assertion_passes_when_all_match(testdir: pytest.Testdir) -> None: testdir.makepyfile( """ @@ -1130,7 +1127,6 @@ def test_soft(page): result.assert_outcomes(passed=1) -@pytest.mark.skip(reason="requires 1.60") def test_soft_assertion_does_not_shadow_body_failure( testdir: pytest.Testdir, ) -> None: From 3084a499a28a97f84487ad1ad8d309ea614ad04a Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Mon, 18 May 2026 15:10:19 +0200 Subject: [PATCH 2/3] test(connect_options): adapt to Playwright 1.59.1+ connect() API BrowserType.connect() renamed its first parameter from ws_endpoint to endpoint (positional) in microsoft/playwright-python#3050. The test also needs to look at stderr from the subprocess pytester run, since the WebSocket error log lands there. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/test_asyncio.py | 4 ++-- tests/test_sync.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_asyncio.py b/tests/test_asyncio.py index 7531e8d..ac634c5 100644 --- a/tests/test_asyncio.py +++ b/tests/test_asyncio.py @@ -1055,7 +1055,7 @@ def test_connect_options_should_work(testdir: pytest.Testdir) -> None: @pytest.fixture(scope="session") def connect_options(): return { - "ws_endpoint": "ws://localhost:1234", + "endpoint": "ws://localhost:1234", } """ ) @@ -1069,7 +1069,7 @@ async def test_connect_options(page): """ ) result = testdir.runpytest() - assert "connect ECONNREFUSED" in "".join(result.outlines) + assert "connect ECONNREFUSED" in "\n".join(result.outlines + result.errlines) server_process = subprocess.Popen( ["playwright", "run-server", "--port=1234"], stdout=subprocess.PIPE, diff --git a/tests/test_sync.py b/tests/test_sync.py index a5ff586..cfaf4c9 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -1045,7 +1045,7 @@ def test_connect_options_should_work(testdir: pytest.Testdir) -> None: @pytest.fixture(scope="session") def connect_options(): return { - "ws_endpoint": "ws://localhost:1234", + "endpoint": "ws://localhost:1234", } """ ) @@ -1056,7 +1056,7 @@ def test_connect_options(page): """ ) result = testdir.runpytest() - assert "connect ECONNREFUSED" in "".join(result.outlines) + assert "connect ECONNREFUSED" in "\n".join(result.outlines + result.errlines) server_process = subprocess.Popen( ["playwright", "run-server", "--port=1234"], stdout=subprocess.PIPE, From 0d25ad90001abbdbe520e885088c07b4bd0ed4e3 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Mon, 18 May 2026 15:46:40 +0200 Subject: [PATCH 3/3] test: set PYTHONIOENCODING=utf-8 for pytester subprocesses Pytester decodes subprocess stdout as utf-8, but on Windows the default stdout encoding is cp1252. When Playwright's assertion failure messages contain non-ASCII characters (e.g. unicode arrows / multiplication signs), pytester raises UnicodeDecodeError. Force utf-8 in child Python processes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/conftest.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 01ff08a..e497902 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -41,6 +41,10 @@ def pytest_configure(config: Any) -> None: os.environ["PLAYWRIGHT_BROWSERS_PATH"] = playwright_browser_path +# Ensure subprocess pytester runs (and any child python processes) emit utf-8 +# on stdout/stderr so pytester's utf-8 decoding doesn't break on Windows. +os.environ["PYTHONIOENCODING"] = "utf-8" + class HTTPTestServer: PREFIX = ""