Repository navigation
Description
Sibling to the #243 release rehearsal. #243 is a k6 capacity/load baseline (protocol-level, no browser). This issue is the complementary UI verification track: a real browser exercising the SPA, sharing #243's Seed:Simulation fixture so screens are realistic (populated dashboards, reports, history, lifecycle states) instead of empty.
New infra: the repo has no Playwright today (web/ is Vitest + Testing Library only; no playwright.config, no e2e/). This is a new E2E layer atop the existing SPA unit-test infra (#105–#124).
Goal
Verify the SPA works — and stays usable under load — from a real user's browser, per persona, against a production-config app with realistic data.
Two modes
1. E2E smoke suite (functional correctness)
Playwright drives the key flow per persona against the seeded env:
- Owner/Admin: dashboard → reports → audit browse → one
/export. - Manager: daily-entry submit → review → adjust/void; flock ops.
- Sales: customer → draft order → add lines → confirm → record payment.
- Worker: daily entry by grade; the flock-restricted worker sees only assigned flocks (403/hidden on the rest).
- ReadOnly: read-heavy browsing; server-side 403 on deep-links to
/audit//users(no route-level gate — the 403 is the assertion).
Cross-cutting: login + in-memory access token + silent refresh across the 15-min boundary; i18n render (es/tl) not broken; the PWA update prompt path.
2. Canary-under-load UX probe (real experience under stress)
1–2 Playwright browsers running concurrently with the k6 load (#243), measuring what k6 cannot: page-load, Core Web Vitals (LCP/INP/CLS), asset loading, and visual correctness while the backend is saturated. Reported alongside #243's server-side percentiles. This is the heavier/hybrid piece — schedule after the k6 baseline + E2E smoke land.
Shared fixture (the reason to plan with #243)
Both tracks consume #243's deliverables — build once:
SimulationDataSeeder(Seed:Simulation) — the 10-persona cast + parameterized history. History depth must populate the exact screens the E2E suite asserts (non-empty dashboards/reports).tools/simulation/.sim-cast.json— runtime-generated cast credentials; Playwright logs in from the same file k6 uses (never hardcode; git-ignored).- The sim compose overlay + deterministic reset recipe.
Structure / decisions
- Where Playwright lives: decide
web/(@playwright/test, co-located with the SPA) vs a standalonetools/simulation/ui/project — part of this issue's plan. - CI: whether the E2E smoke runs in CI (needs the app + a seeded DB stood up) or stays a manual/pre-release gate.
- Canary metrics sink: where CWV land (Playwright trace/report vs folded into the Release rehearsal: day-in-the-life simulation of 10 users (all roles) with container-resource + HTTP-traffic monitoring #243 findings doc).
Deferred / non-goals
Visual-regression baselining and accessibility (axe) sweeps are natural follow-ons, not in this issue's first cut. The load generation itself stays k6 (#243) — Playwright is never the crowd.
Refs
- Release rehearsal: day-in-the-life simulation of 10 users (all roles) with container-resource + HTTP-traffic monitoring #243 — the k6 capacity baseline this shares a fixture with
- SPA test infrastructure — Vitest + Testing Library, start with claims.ts role decode #105–F29: CI SPA coverage gate — Vitest thresholds + ratchet (#121) #124 — existing SPA (Vitest) test infrastructure this layers onto
- Tracked in epic EPIC: Phase 1.5 — Egg product hardening #15.
Security regression additions — 2026-07-31
Two underlying defects now have dedicated implementation issues. Keep Playwright as the end-to-end regression layer, not the place where the fixes themselves are hidden.
- Prevent late refresh responses from resurrecting or overwriting SPA sessions #310 owns the browser session-generation fix.
- Bound production report workload, query size, and concurrency #311 owns bounded report queries and concurrency.
Acceptance additions
- After Prevent late refresh responses from resurrecting or overwriting SPA sessions #310 lands, exercise logout while refresh is in flight and prove the late response cannot resurrect the session.
- After Prevent late refresh responses from resurrecting or overwriting SPA sessions #310 lands, exercise an explicit login while bootstrap refresh is pending and prove the newer login wins.
- After Bound production report workload, query size, and concurrency #311 lands, run the report canary at the maximum supported date range and capture responsiveness and server latency without exceeding the documented bound.
- Failures link back to the owning implementation issue rather than weakening the E2E assertions.
Activity
docs: #277 session report
docs: #277 session report — round 2 results
docs: #277 session report — round 3, including a withdrawn result
docs: #277 session report — round 4, and why the loop stopped there
test(e2e): Playwright smoke suite for the SPA over the #243 simulatio…
feat(sim): canary-under-load UX probe recording Core Web Vitals along…
ci(e2e): workflow_dispatch job for the Playwright smoke suite (#392)
mforce commentedon Aug 7, 2026
Closing — complete. Both tracks landed:
workflow_dispatchjob for the smoke suite.tools/simulation/ui/(notweb/), CI staysworkflow_dispatch(deliberately not blocking every PR) — documented indocs/decisions/277-spa-e2e.md.Generated by Claude Code
docs: keep cross-directory rules in root; route every trigger path in…