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 }) => { // Visiting the guarded page redirects to the Keycloak (mock DigiD) login. await page.goto('/'); // Keycloak's default login form (stable ids across themes). await page.locator('#username').fill('jan-burger'); await page.locator('#password').fill('test123'); await page.locator('#kc-login').click(); // Back on the portal, authenticated. await expect(page.getByRole('heading', { name: /Zelfservice/i })).toBeVisible(); await page.getByRole('button', { name: /indienen/i }).click(); // The BFF accepted it and the page shows the confirmation with the reference. const confirmation = page.getByText(/ontvangen/i); await expect(confirmation).toBeVisible(); const reference = (await confirmation.textContent())?.match(/Referentie:\s*([0-9a-fA-F-]+)/)?.[1]; expect(reference, 'the confirmation shows a registration reference').toBeTruthy(); // The openbaar register (anonymous, its own origin) shows the submitted entry once the projection // catches up. The projection updates asynchronously (NRC → event-subscriber), and the register loads // on open, so reload until *this* submission's row appears. We poll on the reference cell (not a // generic INGEDIEND cell): the shared verify stack already holds INGEDIEND rows from earlier checks, // so a status-only poll would short-circuit on a stale row before our row is projected. await page.goto('http://openbaar/'); await expect(page.getByRole('heading', { name: /Openbaar BIG-register/i })).toBeVisible(); // #78: the reference shown in the public register must be the exact one the citizen saw on the // submit confirmation — no mismatch between the two portals. await expect .poll(async () => { await page.reload(); return page.getByRole('cell', { name: reference }).count(); }, { timeout: 30_000, intervals: [1_000, 2_000, 3_000, 5_000] }) .toBeGreaterThan(0); 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. await expect .poll(async () => { const res = await request.get(`http://domain:8080/registrations/${reference}`); return res.ok() ? (await res.json()).zaakUrl : null; }, { timeout: 30_000, intervals: [1_000, 2_000, 3_000, 5_000] }) .toBeTruthy(); const approve = await request.post(`http://domain:8080/registrations/${reference}/approve`); expect(approve.status()).toBe(204); // The approval flows back to the projection; the openbaar register now shows *our* row (matched by // its reference) as INGESCHREVEN. await expect .poll(async () => { await page.reload(); return page.getByRole('row', { name: reference }).getByRole('cell', { name: 'INGESCHREVEN' }).count(); }, { timeout: 30_000, intervals: [1_000, 2_000, 3_000, 5_000] }) .toBeGreaterThan(0); });