Files
atomic-design-poc/e2e/smoke.spec.ts
T
ehoandClaude Sonnet 5 42f7bd651d test(e2e): isolate runs and identities without a new backend endpoint (WP-74)
The three specs shared one mutable backend and said so in their own
comments ("Restart the backend between CI runs"). WP-70 recorded the fix as
a dev-only seed endpoint; it isn't needed. The DB path already routes
through IConfiguration, so playwright.config's webServer hands the backend a
throwaway SQLite file per invocation — the same trick TestWebApplicationFactory
already uses, with zero backend change. And StubIdentityProvider already
honoured X-Subject; the only gap was that nothing sent it. That matters
because the backend has no IsDevelopment() gate anywhere, so a seed endpoint
would have had to invent the codebase's first environment gate.

subjectInterceptor mirrors the existing roleInterceptor and is wired into the
same isDevMode()-only list. Interceptors alone were not enough: the raw XHR
upload and the hand-written letter-preview fetch bypass Angular's chain (as
CLAUDE.md documents), so both now stamp X-Subject explicitly — without that,
every uploaded document still landed under DemoOwner.

reuseExistingServer stays on: flipping it would break local runs for anyone
already serving the docker stack. Each run gets a unique DB filename and
global-setup sweeps only prior runs' leftovers — deleting a fixed path
mid-run risks SQLite silently recreating an empty, unmigrated file under
fullyParallel.

Verified: e2e passes twice back-to-back with no backend restart, and
X-Subject was observed on a real request, not merely wired.

brief-v2.spec.ts keeps the shared identity for now — see the KNOWN GAP note;
a backend staleness bug makes /brief/preview return a sent letter with the
draft watermark for any non-DemoOwner BSN. actors.ts reserves the actor for
whoever fixes it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 16:32:07 +02:00

61 lines
3.1 KiB
TypeScript

import { expect, test } from '@playwright/test';
import { Actors, loginAs, SeedRefs } from './support/actors';
// One happy-path flow through the real FE+backend: log in, land on the real
// dashboard, run the registratie wizard's minimum required path (a DUO diploma
// with zero policy questions, so the only required upload is identiteit), submit,
// and see the real confirmation. Not a full wizard-coverage suite — see WP-19.
//
// This test mutates real state (creates+submits a registratie application), so it
// logs in as its own BSN (`registratieAanvrager`, WP-74) rather than the shared
// `zorgverlener` — a leftover Concept from a previous run lands on THAT BSN's
// dashboard, not this one's, so a rerun (or another spec) never sees it. The
// backend itself also gets a fresh throwaway SQLite file per `npm run e2e`
// invocation (`playwright.config.ts`), so even a from-scratch run starts clean.
test('login → dashboard → registratie wizard → submitted', async ({ page }) => {
await loginAs(page, Actors.registratieAanvrager);
await expect(page).toHaveURL(/\/dashboard$/);
await expect(page.getByRole('heading', { level: 1, name: 'Mijn overzicht' })).toBeVisible();
// Real backend content, not a loading/error state.
await expect(page.getByText('Persoonsgegevens (BRP)')).toBeVisible();
await page.goto('/registreren');
await expect(
page.getByRole('heading', { level: 1, name: 'Inschrijven in het BIG-register' }),
).toBeVisible();
// Step 1 — adres: BRP prefill is real backend data; "Post" skips the email field.
// The CIBG-styled radio hides the native input behind its label, so click the
// label (real user interaction) rather than `.check()` the covered input.
await expect(page.getByText('Vooraf ingevuld op basis van de BRP')).toBeVisible();
await page.locator('label[for="correspondentie-post"]').click();
await page.locator('button[type="submit"]').click();
// Step 2 — beroep: the first DUO diploma (Geneeskunde, non-English) carries zero
// policy questions, so the only required document is identiteit.
await expect(page.locator(`#diploma-${SeedRefs.diplomaZonderPolicyVragen}`)).toBeVisible({
timeout: 10_000,
});
await page.locator(`label[for="diploma-${SeedRefs.diplomaZonderPolicyVragen}"]`).click();
await expect(page.getByText('Beroep (afgeleid uit diploma)')).toBeVisible();
await page.locator('#identiteit-file').setInputFiles({
name: 'identiteit.pdf',
mimeType: 'application/pdf',
buffer: Buffer.from('%PDF-1.4 fake e2e fixture'),
});
// Real upload against the backend: wait for the row to leave "uploading".
await expect(page.locator('.upload-progress')).toHaveCount(0, { timeout: 10_000 });
await expect(page.locator('.icon-remove')).toBeVisible();
await page.locator('button[type="submit"]').click();
// Step 3 — controle: review + real submit.
await expect(page.getByText('Controleer uw gegevens en dien de registratie in.')).toBeVisible();
await page.locator('button[type="submit"]').click();
await expect(page.getByText('Uw registratie is ontvangen')).toBeVisible({ timeout: 10_000 });
await expect(page.getByText(/Uw referentienummer is/)).toBeVisible();
});