Push builds have been RED on the lint step since ~build 209 from accumulated formatting drift. This is the mechanical cleanup: ruff format + ruff --fix (UP038 isinstance unions, SIM105 contextlib.suppress, UP031 f-strings, SIM115 tempfile context manager), shfmt -i 2 -ci, nixpkgs-fmt/statix/deadnix (merged attrsets, dropped unused lib args), yamllint, and shell quoting fixes in tests/lasuite-docs/setup_custom_tests.sh. No behaviour changes intended; lint: PASS, unit tests: 138 passed.
64 lines
2.4 KiB
Python
64 lines
2.4 KiB
Python
"""Playwright helpers for Phase-2 recipe tests (plan §4.2).
|
|
|
|
Centralizes the `page.goto(...)` retry loop that absorbs transient network errors (F2-3 / F2-5):
|
|
Playwright's `page.goto` raises `PlaywrightError` on transport-level failures (`net::ERR_*`,
|
|
connection resets, CDP target gone) — those escape a naive loop that only retries on status
|
|
mismatches. Wrap every install-overlay `page.goto` in this helper so transient errors retry
|
|
without weakening the underlying assertion (same pattern as F1e-1's `exec_in_app` poll+raise
|
|
hardening).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
|
|
|
|
def goto_with_retry(
|
|
page,
|
|
url,
|
|
*,
|
|
deadline_seconds: int = 120,
|
|
accept_statuses=(200, 304),
|
|
goto_timeout_ms: int = 30_000,
|
|
wait_until: str = "domcontentloaded",
|
|
):
|
|
"""Poll `page.goto(url)` until status is in `accept_statuses` OR the deadline expires.
|
|
|
|
Returns the final Playwright response. Raises AssertionError if the deadline expires without
|
|
a successful status. Each iteration catches `PlaywrightError` (and any other exception) so
|
|
transient network failures retry rather than fail the test.
|
|
|
|
Use case: recipe install overlays where the app's HTTP layer may be up (status 200 to
|
|
/healthz or generic readiness) but the requested route is still registering (404), or
|
|
Playwright's CDP connection transiently flakes (`net::ERR_NETWORK_CHANGED`).
|
|
"""
|
|
# Imported lazily so this module can be imported without playwright at unit-test time.
|
|
try:
|
|
from playwright.sync_api import Error as PlaywrightError
|
|
except ImportError: # pragma: no cover — playwright is always installed in cc-ci-run
|
|
PlaywrightError = Exception # noqa: N806
|
|
|
|
deadline = time.time() + deadline_seconds
|
|
resp = None
|
|
last_status = 0
|
|
last_err = ""
|
|
attempts = 0
|
|
while time.time() < deadline:
|
|
attempts += 1
|
|
try:
|
|
resp = page.goto(url, wait_until=wait_until, timeout=goto_timeout_ms)
|
|
except PlaywrightError as e:
|
|
last_err = str(e)
|
|
resp = None
|
|
last_status = 0
|
|
else:
|
|
last_status = resp.status if resp is not None else 0
|
|
if last_status in accept_statuses:
|
|
return resp
|
|
time.sleep(3)
|
|
raise AssertionError(
|
|
f"page.goto({url}) never returned a status in {accept_statuses} after "
|
|
f"{attempts} attempts ({deadline_seconds}s); last status={last_status}, "
|
|
f"last error={last_err or 'none'}"
|
|
)
|