Gap analysis found the POC's designed-but-unbuilt strategic gaps: ABAC authorization (ADR-0002/PRD-0002 phase P1), no e2e coverage, unproven i18n second-locale seam, thin resilience seams (correlation-id, idempotency, retry), and in-memory-only persistence. Each WP is grounded in the current code (file paths + line numbers), not just the analysis. Also corrects PRD-0001's stale 'Proposed' status header — the Mijn aanvragen vertical is fully built. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5.1 KiB
WP-19 — Playwright e2e smoke
Status: todo Phase: 5 — productie-volwassenheid
Why
There is no end-to-end test anywhere in the repo — no Playwright/Cypress config, no
e2e/ directory. axe-playwright is already a dependency (used by
test-storybook:ci to run axe against Storybook, .storybook/test-runner.ts), but
nothing drives the actual running app through a real browser. The GREEN gate proves
every unit and component-in-isolation, never a real user flow through the FE+backend
wired together — the thing a demo/reference app should be able to prove first.
Read first
README.md"Run it" + "See every data state (scenario toggle)" — the flows to coverdocker-compose.yml(the two-service dev topology e2e can run against).storybook/test-runner.ts(existing Playwright-adjacent config in the repo, for browser-launch precedent, though it drives Storybook not the app)src/app/shared/infrastructure/scenario.interceptor.ts(the?scenario=toggle — reuse it for the error-path test instead of mocking the network).github/workflows/ci.yml(thestorybook-a11yjob'splaywright install --with-deps chromiumstep — same install pattern for a new e2e job)
Decisions (pre-made, don't relitigate)
- Playwright, not Cypress.
axe-playwrightis already a dependency and the repo already has one Playwright-based CI job (storybook-a11y); adding Cypress would be a second, redundant browser-automation toolchain. - Smoke-level coverage only: one happy-path flow end to end, one degraded-path
flow via
?scenario=. This is not a full e2e suite — it proves the seam works, it doesn't replace component/unit tests. - Run against the real backend, not a mock server — the point is proving FE+BE integration, which is exactly what unit tests (mocked adapters) don't cover.
- Faked auth (
digid.adapter.ts) is used as-is: e2e logs in with any 9-digit BSN, no special e2e auth bypass.
Files
- New
playwright.config.tsat repo root —baseURLfrom an env var (defaulthttp://localhost:4200),webServerconfig that can optionally bootng serve(skip ifCIalready starts the app in a prior step — see Steps). - New
e2e/smoke.spec.ts— the happy path. - New
e2e/error-state.spec.ts— the?scenario=errorpath. package.json— add"e2e": "playwright test"script;@playwright/testdevDependency..github/workflows/ci.yml— new jobe2e, steps: checkout, setup-node, setup-dotnet,npm ci,npx playwright install --with-deps chromium, start backend (dotnet run --project backend/src/BigRegister.Api &),npm start &(orng servebackgrounded), wait-on both ports,npm run e2e.timeout-minutes: 15per the hardened workflow convention already inci.yml.
Steps
- Install
@playwright/test; scaffoldplaywright.config.tswith a singlechromiumproject (matchtest-storybook:ci's browser choice). e2e/smoke.spec.ts: navigate to/login, submit a BSN, land on/dashboard, assert real dashboard content renders (not a loading/error state), navigate into one wizard (herregistratie or registratie change-request), fill the minimum required fields, submit, assert a success state.e2e/error-state.spec.ts: navigate to/dashboard?scenario=error, assert the error alert + "Opnieuw proberen" button render (<app-async>'s error slot), click retry, assert it re-fetches (scenario is per-request so a retry without the query param would succeed — confirm the interceptor's actual behavior first and assert accordingly).- Wire the CI job; verify it's independent of (doesn't block or get blocked by) the
existing jobs — add to
concurrency/timeout-minutesconventions already inci.yml. - Document
npm run e2einREADME.md's command list.
Acceptance criteria
npm run e2epasses locally againstdocker compose upornpm start+dotnet runrun manually.- CI job
e2eis green and runs on every PR alongside the existing jobs. - The happy-path spec exercises a real wizard submit against the real backend (not mocked) and asserts on the resulting UI state.
- The error-path spec exercises
<app-async>'s error slot + retry via the real?scenario=errortoggle, not a mocked HTTP response.
Verification
npm run e2e locally; then push a branch and confirm the new e2e CI job appears
and passes. Cross-check that a deliberately broken flow (e.g. temporarily rename a
required form field) fails the e2e spec, proving it isn't a no-op.
Out of scope
Full e2e coverage of every wizard/flow; visual regression testing; cross-browser matrix (chromium only, matching the existing a11y job); load/performance testing.
Risks
The ?scenario= interceptor is dev-only (isDevMode() gated, per
app.config.ts) — confirm the e2e target build runs in dev mode (it does via ng serve/npm start; a production ng build would need the toggle unavailable,
which is correct and should be asserted, not worked around). Backend
in-memory stores mean e2e runs against a fresh seed each restart — don't assert on
data that a previous test run could have mutated; restart the backend per CI run.