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.
This commit is contained in:
ThePhaseless
2026-08-11 00:00:10 +02:00
parent f8d087bab8
commit 92043725f5
+11 -1
View File
@@ -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 = "<html><title>Login</title></html>"
# 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 == "<html><title>Login</title></html>"
# No challenge must be detected: the solver must never be invoked.
dep.solver.solve_captcha.assert_not_called()
@pytest.mark.asyncio