test: Cypress en Playwright e2e-suites voor login, agenda, cortex en verpleegrapportage
Configs voor beide runners, login via session caching, AI-chat mocks (SSE) voor deterministische Cortex-tests. Supabase local config toegevoegd. Auth-state en reports zijn gitignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
108
tests/playwright/patients/rapportage.spec.ts
Normal file
108
tests/playwright/patients/rapportage.spec.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
const PATIENT_NAAM = 'Robbert Robot';
|
||||
const AUTH_STATE = 'tests/playwright/.auth/user.json';
|
||||
|
||||
const RAPPORTAGES = [
|
||||
{
|
||||
type: 'Voortgang',
|
||||
inhoud:
|
||||
'Cliënt heeft vandaag goed geslapen en voelt zich uitgerust. Medicatie correct ingenomen zonder bijwerkingen. Stemming is stabiel en positief gedurende de gehele dag.',
|
||||
},
|
||||
{
|
||||
type: 'Contact',
|
||||
inhoud:
|
||||
'Telefonisch contact gehad met de familie van de cliënt. Positieve terugkoppeling over het afgelopen weekend. Familie ervaart momenteel weinig zorgen over de thuissituatie.',
|
||||
},
|
||||
{
|
||||
type: 'Vrije notitie',
|
||||
inhoud:
|
||||
'Cliënt heeft interesse getoond in deelname aan de dagactiviteitengroep. Dit voorstel wordt volgende week besproken tijdens het multidisciplinair overleg met het behandelteam.',
|
||||
},
|
||||
];
|
||||
|
||||
test.describe(`Rapportages schrijven - ${PATIENT_NAAM}`, () => {
|
||||
let rapportageUrl: string;
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
// Gebruik een aparte context om het patiënt-ID op te zoeken
|
||||
const context = await browser.newContext({ storageState: AUTH_STATE });
|
||||
const page = await context.newPage();
|
||||
|
||||
await page.goto('/epd/patients');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Zoek Robbert Robot via de zoekbalk
|
||||
await page.getByPlaceholder('Zoek op naam of BSN...').fill('Robbert');
|
||||
await expect(page.getByText(PATIENT_NAAM).first()).toBeVisible({ timeout: 8_000 });
|
||||
await page.getByText(PATIENT_NAAM).first().click();
|
||||
await page.waitForURL(/\/epd\/patients\/.+/);
|
||||
|
||||
const patientId = page.url().match(/\/epd\/patients\/([^/?#]+)/)?.[1];
|
||||
if (!patientId) throw new Error(`Patiënt "${PATIENT_NAAM}" niet gevonden in de patiëntenlijst`);
|
||||
|
||||
rapportageUrl = `/epd/patients/${patientId}/rapportage`;
|
||||
await context.close();
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(rapportageUrl);
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Wacht tot de TipTap editor volledig geladen is
|
||||
await expect(page.locator('.ProseMirror')).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
// Verwijder eventuele localStorage drafts voor een schone start
|
||||
await page.evaluate(() => {
|
||||
Object.keys(localStorage)
|
||||
.filter((k) => k.startsWith('rapportage-draft-'))
|
||||
.forEach((k) => localStorage.removeItem(k));
|
||||
});
|
||||
});
|
||||
|
||||
RAPPORTAGES.forEach((rapportage, i) => {
|
||||
test(`rapportage ${i + 1}: ${rapportage.type}`, async ({ page }) => {
|
||||
// Selecteer het rapportage type via de QuickActions (exact=true voorkomt match met tijdlijn items)
|
||||
await page.getByRole('button', { name: rapportage.type, exact: true }).click();
|
||||
|
||||
// Klik op de editor en typ de inhoud
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.click();
|
||||
await page.keyboard.type(rapportage.inhoud);
|
||||
|
||||
// Verifieer dat de tekst meetelt in de karakterteller
|
||||
await expect(page.locator('text=/\\d+ \\/ 5000 karakters/')).toBeVisible();
|
||||
|
||||
// Opslaan knop moet actief zijn (inhoud > 20 karakters)
|
||||
const opslaanKnop = page.getByRole('button', { name: 'Opslaan' });
|
||||
await expect(opslaanKnop).toBeEnabled();
|
||||
await opslaanKnop.click();
|
||||
|
||||
// Verifieer de succesmelding
|
||||
await expect(page.getByText('Rapportage opgeslagen').first()).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
// Na opslaan is de editor leeg → Opslaan knop is weer uitgeschakeld
|
||||
await expect(opslaanKnop).toBeDisabled({ timeout: 5_000 });
|
||||
});
|
||||
});
|
||||
|
||||
test('alle drie rapportages staan in de tijdlijn', async ({ page }) => {
|
||||
// Schrijf de drie rapportages in volgorde
|
||||
for (const rapportage of RAPPORTAGES) {
|
||||
await page.getByRole('button', { name: rapportage.type, exact: true }).click();
|
||||
await page.locator('.ProseMirror').click();
|
||||
await page.keyboard.type(rapportage.inhoud);
|
||||
await page.getByRole('button', { name: 'Opslaan', exact: true }).click();
|
||||
await expect(page.getByText('Rapportage opgeslagen').first()).toBeVisible({ timeout: 10_000 });
|
||||
// exact: true onderscheidt "Opslaan" (klaar) van "Opslaan…" (bezig) — wacht tot opslaan echt klaar is
|
||||
await expect(page.getByRole('button', { name: 'Opslaan', exact: true })).toBeDisabled({ timeout: 10_000 });
|
||||
}
|
||||
|
||||
// Controleer dat de tijdlijn de rapportages toont
|
||||
// TimelineCard rendert als <article role="button"> — anders dan QuickAction <button> elementen
|
||||
const tijdlijnKaarten = page.locator('article[role="button"]');
|
||||
await expect(tijdlijnKaarten.filter({ hasText: 'Voortgang' }).first()).toBeVisible({ timeout: 5_000 });
|
||||
await expect(tijdlijnKaarten.filter({ hasText: 'Contact' }).first()).toBeVisible({ timeout: 5_000 });
|
||||
await expect(tijdlijnKaarten.filter({ hasText: 'Vrije notitie' }).first()).toBeVisible({ timeout: 5_000 });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user