diff --git a/tests/e2e/playwright.config.ts b/tests/e2e/playwright.config.ts index 6f6a37d..4423a77 100644 --- a/tests/e2e/playwright.config.ts +++ b/tests/e2e/playwright.config.ts @@ -3,6 +3,9 @@ import { defineConfig, devices } from '@playwright/test'; // The e2e runs inside the compose network (infra/run-e2e-check.sh); baseURL defaults to the // self-service service. Keep timeouts generous — the first navigation triggers the DigiD flow. const baseURL = process.env.SELF_SERVICE_URL ?? 'http://self-service'; +// The behandel portal is a second origin the happy path visits (staff approve from the werkbak); +// it needs the same insecure-origin-as-secure treatment as self-service for the PKCE login (below). +const behandelURL = process.env.BEHANDEL_URL ?? 'http://behandel'; export default defineConfig({ testDir: '.', @@ -22,7 +25,9 @@ export default defineConfig({ // the production HTTPS context. This flag is only honoured by the full Chromium build (new // headless), not Playwright's default headless-shell, so pin `channel: 'chromium'`. channel: 'chromium', - launchOptions: { args: [`--unsafely-treat-insecure-origin-as-secure=${baseURL}`] }, + launchOptions: { + args: [`--unsafely-treat-insecure-origin-as-secure=${baseURL},${behandelURL}`], + }, }, projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }], }); diff --git a/tests/e2e/registration.spec.ts b/tests/e2e/registration.spec.ts index 9c678ab..3313bdc 100644 --- a/tests/e2e/registration.spec.ts +++ b/tests/e2e/registration.spec.ts @@ -1,10 +1,11 @@ import { expect, test } from '@playwright/test'; -// Walking-skeleton happy path (S-08d + S-09 + S-09b): a zorgprofessional logs in via mock DigiD and -// submits through the self-service portal → BFF → domain; the entry appears in the openbaar register -// as INGEDIEND; a behandelaar approves it via the temporary admin endpoint; the approval flows via the -// ACL → NRC → event-subscriber → projection, and the openbaar register then shows it as INGESCHREVEN. -test('DigiD login → submit → public INGEDIEND → approve → public INGESCHREVEN', async ({ page, request }) => { +// Walking-skeleton happy path (S-08d + S-09 + S-09b + S-12): a zorgprofessional logs in via mock +// DigiD and submits through the self-service portal → BFF → domain; the entry appears in the openbaar +// register as INGEDIEND; a behandelaar then logs in to the behandel portal, finds the registration in +// the werkbak, and approves it (goedkeuren); the decision completes the Flowable Beoordelen task and +// flows via the ACL → NRC → event-subscriber → projection, and the openbaar register shows INGESCHREVEN. +test('DigiD submit → public INGEDIEND → behandelaar goedkeurt → public INGESCHREVEN', async ({ page }) => { // Visiting the guarded page redirects to the Keycloak (mock DigiD) login. await page.goto('/'); @@ -43,21 +44,33 @@ test('DigiD login → submit → public INGEDIEND → approve → public INGESCH await expect(page.getByRole('row', { name: reference }).getByRole('cell', { name: 'INGEDIEND' })) .toBeVisible(); - // Approve via the temporary admin endpoint (reached directly on the compose network, as a - // behandelaar would until the behandel-portal exists — S-12). The zaak is opened off the request - // path by the worker, so wait for it before approving. + // A behandelaar picks the registration up in the behandel-portal werkbak and approves it + // (goedkeuren) — the S-12 flow that replaces the temporary admin endpoint. Navigating here switches + // to the medewerker realm (a different Keycloak realm than the citizen's digid session). + await page.goto('http://behandel/'); + await page.locator('#username').fill('merel-behandelaar'); + await page.locator('#password').fill('test123'); + await page.locator('#kc-login').click(); + + await expect(page.getByRole('heading', { name: /Werkbak/i })).toBeVisible(); + + // The registration parks at the Beoordelen user task only after the worker has opened its zaak, so + // it appears in the werkbak asynchronously — reload until this reference's row shows up. Target the + // decide button by reference (not a generic "Goedkeuren"): the shared verify stack holds other open + // tasks, so a positional match could act on someone else's registration. + const goedkeuren = page.getByRole('button', { name: `Goedkeuren ${reference}` }); await expect .poll(async () => { - const res = await request.get(`http://domain:8080/registrations/${reference}`); - return res.ok() ? (await res.json()).zaakUrl : null; + await page.reload(); + return goedkeuren.count(); }, { timeout: 30_000, intervals: [1_000, 2_000, 3_000, 5_000] }) - .toBeTruthy(); + .toBeGreaterThan(0); - const approve = await request.post(`http://domain:8080/registrations/${reference}/approve`); - expect(approve.status()).toBe(204); + await goedkeuren.click(); - // The approval flows back to the projection; the openbaar register now shows *our* row (matched by - // its reference) as INGESCHREVEN. + // The approval flows back to the projection; back on the openbaar register *our* row (matched by + // its reference) now shows INGESCHREVEN. + await page.goto('http://openbaar/'); await expect .poll(async () => { await page.reload();