--- type: reference description: "`tests/e2e/` drives the **live fleet** through a real browser: fresh contexts" tags: - architecture - e2e - homepage timestamp: 2026-09-01 --- # E2E Suite — homepage + guard chain (Playwright) `tests/e2e/` drives the **live fleet** through a real browser: fresh contexts hit `https://www.franksdank.cloud`, log in through the authentik two-stage flow, and crawl every service link through the Caddy → Anubis → outpost guard chain. It is the only test in this repo that exercises the chain as a user sees it; the `bats` seams assert config shape, not behavior. ## Layout | Path | Role | |------|------| | `playwright.config.ts` | five projects (below), real-browser UA, trace on failure | | `specs/00-auth.setup.ts` | setup project: two real authentik logins, saves storage states | | `specs/10-homepage.spec.ts` | user panel set + open links derived from `services.json` (registry-derived — no hardcoded service lists) | | `specs/11-admin.spec.ts` | `admin` project: admin grid + `/admin/` RBAC (403 for standard user, members-only 200) | | `specs/12-crawl.spec.ts` | every user open link reaches its real app through the guard chain | | `specs/19-frankbook.spec.ts` | `frankbook` project: walks the six admin links that terminate on FrankBook-Pro (192.168.0.112) — conditional, see below | | `specs/13-sso.spec.ts` | direct guarded-vhost visits resolve silently with ONLY the homepage session (single-login enforcement) | | `specs/14-guard.spec.ts` | `guest` project, no storage state: unauthenticated traffic lands on the IdP login | | `lib/login.ts` | `akLogin` helper (two-stage flow, shadow-DOM locators) | | `lib/env.ts` | loads `.env.local`, exports `REAL_UA` | | `tests/e2e_scaffold.bats` | bats seam: structure + no-credentials-tracked (never drives the fleet) | ## Projects and auth - **setup** — logs in as `qa-e2e` and `qa-e2e-admin` (fleet-managed IdP test users; the admin is a `homepage-admins` member), writes `.auth/qa-e2e{,-admin}.json` storage states. - **standard** — 10/12/13 with the user state. - **admin** — 11 with the admin state. - **guest** — 14/15 with an empty state. - **frankbook** — 19 with the admin state. **Excluded from every default run**: the *arr/qBittorrent links terminate on FrankBook-Pro, which sleeps when idle, and a sleeping Mac mini must never fail an unrelated deploy. The project joins only when `E2E_INCLUDE_FRANKBOOK=1` (set by `scripts/deploy.sh` when a frankbook trigger path changed: `tests/e2e/specs/19-frankbook.spec.ts`, `frankbook-pro/**`, `shared/homepage/services.json`, or the `dankclaw/Caddyfile` serving the arr vhosts) or when an explicit `--project` argument names it. An `11-admin` edit that touches the arr links must therefore be paired with a `19-frankbook` run against an awake host before it is trusted live. Credentials live in `tests/e2e/.env.local` (git-ignored; placeholders in `.env.local.example`). The scaffold bats seam fails the commit if creds or auth states are ever tracked. ## Run ```bash tests/e2e/run.sh # default set — frankbook project excluded E2E_INCLUDE_FRANKBOOK=1 tests/e2e/run.sh # default set + the frankbook walk cd tests/e2e && npx playwright test # ALL projects, frankbook included ``` Requires LAN reachability of `www.franksdank.cloud` (Pi-hole DNS or public). The suite is **not** part of `./tests/run.sh` — it needs the live fleet and real IdP credentials. ## Gotchas (all paid for live) 1. **Fresh context per login.** Reusing a half-finished flow poisons authentik's `pending_user` stage and the second login hangs. The setup spec runs each login in its own context. 2. **Lit stage re-render race.** authentik's flow executor can re-render the password stage right after mount, wiping a programmatic `fill`; the submit then POSTs an empty form ("This field is required"). `akLogin` fills, **verifies the value survived**, submits via Enter, and retries. 3. **Headless UA = bot.** Anubis denies obvious headless UAs; the config overrides `userAgent` with a real Chrome UA (`REAL_UA`). 4. **Cold Anubis PoW.** The first challenge in a fresh context can outlive default assertion budgets; title asserts carry 60s budgets. 5. **Solve-once cookie.** Since Anubis v1.27.0 + `COOKIE_DOMAIN` (see [[Anubis]]), one PoW per fresh context covers every guarded vhost — crawl and SSO specs complete in ~0.5 s per vhost instead of a PoW each.