"""Shared Playwright auth helper (phase 16). ``login`` drives the REAL form login on /login.html (fill → submit → redirect) so every story that needs the admin does exactly what a human would — no cookie surgery. ``password=None`` uses the shared E2E admin password (success path); pass a wrong value to drive the error state (no redirect, ``#login-error`` role=alert visible, still anonymous). """ from __future__ import annotations from playwright.sync_api import Page, expect from e2e.conftest import ADMIN_PASSWORD # noqa: F401 (re-exported for tests) DEFAULT_NEXT = "/sources.html" def login(page: Page, app_url: str, password: str | None = None, next: str | None = None) -> None: """Perform the real form login and wait for its outcome. * correct password (or ``password=None`` → the shared admin password) → redirects to ``next`` (default ``/sources.html``); * wrong password → ``#login-error`` (role=alert) is visible, the URL never changes, and the visitor is still anonymous. """ attempt = ADMIN_PASSWORD if password is None else password url = f"{app_url}/login.html" if next is not None: url += f"?next={next}" page.goto(url) expect(page.locator("#login-password")).to_be_visible() page.fill("#login-password", attempt) page.click("#login-form button[type=submit]") if attempt != ADMIN_PASSWORD: expect(page.locator("#login-error")).to_be_visible(timeout=15_000) expect(page).to_have_url(url) # no redirect on failure return expect(page).to_have_url(app_url + (next or DEFAULT_NEXT), timeout=30_000)