From 92043725f54837352b1c8fe28c1e09c29f9b38af Mon Sep 17 00:00:00 2001 From: ThePhaseless Date: Mon, 10 Aug 2026 23:59:06 +0200 Subject: [PATCH] test(fake_dep): mock Playwright locator API faithfully fake_dep's AsyncMock page made page.locator() return an un-awaited coroutine, so detect_cloudflare_challenge swallowed an AttributeError and reported a challenge. The networkidle-timeout test silently ran the solver branch and never exercised its intended path, plus emitted a 'coroutine ... was never awaited' RuntimeWarning in CI. Make page.locator() sync-returning (as in real Playwright) with an awaitable count() that finds no elements, and assert the solver is never invoked. --- tests/main_test.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/main_test.py b/tests/main_test.py index ced8a60..661de8b 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -116,6 +116,13 @@ def fake_dep(*, fail_states: set[str] | None = None) -> BrowserDepClass: page.title.return_value = "Login" page.evaluate.return_value = "UnitTestBrowser/1.0" page.content.return_value = "Login" + # detect_cloudflare_challenge calls page.locator() synchronously (Playwright + # Locator API). An unconfigured AsyncMock child would make locator() return + # an un-awaited coroutine; give it a sync-returning mock whose count() is + # awaitable and reports no challenge elements. + locator = MagicMock() + locator.count = AsyncMock(return_value=0) + page.locator = MagicMock(return_value=locator) def wait_for_load_state(state: str, **_kwargs: object) -> None: """Fail the wait when asked for a configured state.""" @@ -133,14 +140,17 @@ def fake_dep(*, fail_states: set[str] | None = None) -> BrowserDepClass: @pytest.mark.asyncio async def test_networkidle_timeout_after_domcontentloaded_returns_content(): """Pages that never go idle after DOM load must still return their content.""" + dep = fake_dep(fail_states={"networkidle"}) response = await read_item( LinkRequest(url="https://example.test/login"), - fake_dep(fail_states={"networkidle"}), + dep, ) assert response.status == "ok" assert response.solution.status == HTTPStatus.OK assert response.solution.response == "Login" + # No challenge must be detected: the solver must never be invoked. + dep.solver.solve_captcha.assert_not_called() @pytest.mark.asyncio