Files
punimtag/e2e/README.md
T
ilia 0b4162de5f
CI / skip-ci-check (pull_request) Successful in 30s
CI / python-lint (pull_request) Successful in 31s
CI / docker-ci (pull_request) Successful in 33s
CI / secret-scan (pull_request) Successful in 35s
CI / viewer-unit (pull_request) Successful in 2m53s
CI / e2e (pull_request) Failing after 3m4s
CI / admin-unit (pull_request) Successful in 3m7s
Fix favorite sign-in copy and pale text-secondary contrast.
Logged-out favorite/report share a reason-aware SignInRequiredDialog so
favorites no longer show report messaging. Replace misplaced text-secondary
(light wash token) with foreground/primary on labels, auth, and menus.
2026-08-05 10:33:29 -04:00

6.2 KiB

PunimTag e2e (Playwright + @levkin/playkit)

Smoke tests against the public DEV URL so we catch LAN-redirect bugs (e.g. NEXTAUTH_URL=http://<backend-host>:3001 → Kolby #57).

Quick start

cd e2e
cp .env.example .env   # edit credentials
npm ci
npx playwright install chromium
npm test

Private Gitea installs need auth once: set a local git insteadOf with a token before npm ci, then unset it. CI does this in .gitea/workflows/ci.yml. Never commit lockfile URLs that embed tokens.

Environment

Variable Required Purpose
PLAYKIT_BASE_URL yes (default in env-defaults.json) Public viewer host
PLAYKIT_ADMIN_BASE_URL no (default punimtagadmindev.levkin.ca) FastAPI admin UI for admin.review-pages.spec.ts
PLAYKIT_API_BASE_URL no LAN API (see env-defaults.json)
E2E_ADMIN_EMAIL for auth specs Prefer e2e@levkine.canot admin@admin.com
E2E_ADMIN_PASSWORD for auth specs Dedicated e2e password (Vaultwarden / Infisical)
E2E_API_USERNAME / E2E_API_PASSWORD optional FastAPI bearer login, admin role (separate DB from NextAuth; mirrors e2e@levkine.ca)
E2E_API_VIEWER_USERNAME / E2E_API_VIEWER_PASSWORD optional FastAPI bearer login, viewer role — role-permission gate specs only
E2E_VIEWER_EMAIL / E2E_VIEWER_PASSWORD optional NextAuth auth-DB viewer, hasWriteAccess=false (third, independent store) — viewer.write-gates.spec.ts only
PLAYKIT_MAIL_PROVIDER for mail specs mailpit (homelab)
MAILPIT_BASE_URL for mail specs http://<mailpit-host>:8025
MAILPIT_USER / MAILPIT_PASSWORD for mail specs Mailpit basic auth

Secrets: Infisical LevkinOpsdev/playkit/punimtag + Gitea Actions. See ansible docs/hardening/SECRETS.md.

Playkit pin: @levkin/playkit@v0.3.1 (git tag). Zod schema option, storageState helpers, playkitFailureArtifacts, Mailpit, and network interception (interceptNetworkCall, startNetworkErrorMonitor — see playkit docs/NETWORK.md) are all in this pin — see network-errors.spec.ts for the silent-4xx/5xx monitors on gallery/search/upload/manage-users. Timing → Pushgateway is supported by the kit but not enabled in this CI job yet.

What we assert

  1. Login page loads on the public hostname
  2. Sign-out stays on that hostname (never 10.x)
  3. FastAPI /health{ "status": "ok" }
  4. Forgot-password / reset-password email in Mailpit; links use public host
  5. Register → unverified block → verify email via Mailpit → login
  6. Idle logout (?e2e_idle_ms=) stays on public host
  7. Upload: submit a tiny fixture via #file-upload (uses storageState)
  8. Manage Users open/close then sign-out (Kolby #57 overlay path)
  9. API /api/v1/auth/me and /api/v1/photos return 401 without token
  10. API catalog: people/tags lists, login 422/401, users/roles 401, missing photo 404|401
  11. Viewer /api/auth/session returns email when storageState is loaded
  12. FastAPI role-permission write gates: viewer 403 vs admin 200 on users, role-permissions, bulk-delete
  13. Public gallery search (/api/search): tag filter, person filter, match-all (peopleMode=all / tagsMode=all) combined narrowing, and UI tag-filter interaction updates URL + result count
  14. Logged-in-only filters: Favorites checkbox toggles favoritesOnly URL param; People filter UI selection updates URL + result count (uses storageState)
  15. Silent-failure network monitor (startNetworkErrorMonitor): gallery home, search+filter, upload, and Manage Users overlay all stay free of background 4xx/5xx while the UI loads/interacts
  16. interceptNetworkCall spies on the real upload POST (status + { message, photos } shape) and the filtered /api/search?tags= GET (status + Zod-validated shape) instead of brittle waitForResponse/text scraping
  17. Zod-validated API responses: FastAPI login TokenResponse + /auth/me UserResponse (api.fastapi-login.spec.ts), gallery search SearchResponse (gallery.search-filters.spec.ts)
  18. NextAuth (browser-session) write gates: viewer (hasWriteAccess=false) gets 403, admin gets past the gate, on POST /api/faces/{id}/identify (viewer.write-gates.spec.ts) — distinct from the FastAPI role-permission gates in #12
  19. PROD smoke (prod.smoke.spec.ts, opt-in via PROD_BASE_URL): /api/health + login page load on the public PROD host — dormant until the PROD LXC exists (see ROADMAP)
  20. Manage Users real CRUD + causal cross-session effect (admin.manage-users-actions.spec.ts): admin creates/edits/deletes a user through the actual panel (not just open/close), and deactivating a user via the UI immediately revokes that user's already open NextAuth session (jwt callback re-checks isActive, Kolby #57 fix) — every run creates a disposable e2e-manage-test-* account and deletes it in a finally, never touching the shared punimtagdev/MirrorMatch DB's real accounts
  21. Timing budgets (timing-budgets.ts): every timings.measure() call site is followed by expectWithinBudget() against a shared BUDGET_MS bucket — a step getting order-of-magnitude slower reds the test, not just a Pushgateway sample nobody's watching
  22. Gallery scroll restore (viewer.scroll-restore.spec.ts): scroll down, open ?photo= modal, Escape closes and restores scroll Y
  23. Core Web Vitals budget (viewer.web-vitals.spec.ts, web-vitals-budgets.ts): LCP + CLS gates on anonymous home gallery load
  24. Admin review pages smoke (admin.review-pages.spec.ts): FastAPI admin login on punimtagadmindev → Identify, Auto-Match, Approve Identified load
  25. /search legacy URLs redirect to / with query string preserved (incl. peopleMode / tagsMode)
  26. Logged-out favorite (viewer.favorite-signin.spec.ts): Sign In Required dialog uses favorites copy, not report copy

See repo root ROADMAP.md for gaps and next steps.

Defaults for base URLs live in env-defaults.json (imported by env-defaults.ts, fixtures.ts, playwright.config.ts, and the Gitea e2e job). The checked-in file holds placeholder hosts; real targets come from PLAYKIT_BASE_URL / PLAYKIT_API_BASE_URL env vars (Gitea Actions secrets in CI) or a gitignored env-defaults.local.json alongside it for local runs.