const { test, expect } = require('@playwright/test'); const BASE = 'https://eetdagboek.vantwout.dev'; /** * Helpers for glucose tests — inline per eetmoment */ /** Navigate to diary tab and wait for render */ async function openDiary(page) { await page.locator('#navDagboek').click(); await page.waitForTimeout(600); // Wait for nuchter card to render await expect(page.locator('.nuchter-card')).toBeVisible({ timeout: 5000 }); } /** Fill a glucose input and trigger save-on-blur */ async function setGlucoseValue(page, momentKey, value) { const input = page.locator(`input.glucose-input[data-moment="${momentKey}"]`); await input.fill(String(value)); // Trigger blur — saves to localStorage and re-renders just the glucose UI await input.evaluate(el => el.blur()); // Wait for re-render to complete await page.waitForTimeout(400); } /** Get the parent glucose container for a given moment */ function glucoseContainer(page, momentKey) { if (momentKey === 'nuchter') { return page.locator('.nuchter-card'); } return page.locator(`.glucose-inline[data-moment-key="${momentKey}"]`); } /** Get status text element for a given moment */ function statusText(page, momentKey) { if (momentKey === 'nuchter') { return page.locator('.nuchter-card .glucose-status-text'); } return page.locator(`.glucose-inline[data-moment-key="${momentKey}"] .glucose-status-text`); } // ============================================================================= test.describe('Glucose-metingen — invoer, validatie, kleurcodering, persistentie', () => { // ---- 1. Glucose-sectie zichtbaar ---- test('1. glucose section has 4 input fields with correct labels and target ranges', async ({ page }) => { await page.goto(BASE); await openDiary(page); // Nuchter card structure await expect(page.locator('.nuchter-card')).toBeVisible(); await expect(page.locator('.nuchter-card-label')).toHaveText('Nuchter (voor ontbijt)'); await expect(page.locator('.nuchter-card-range')).toHaveText('< 5.4'); // Inline glucose in eetmoment cards (Ontbijt, Lunch, Avondeten) const moments = [ { key: 'naOntbijt', label: 'Ontbijt', range: '5.4\u20136.7' }, { key: 'naLunch', label: 'Lunch', range: '5.4\u20136.7' }, { key: 'naAvondeten', label: 'Avondeten', range: '5.4\u20136.7' }, ]; // 3 inline glucose items should exist const inlineItems = page.locator('.glucose-inline'); await expect(inlineItems).toHaveCount(3); for (let i = 0; i < moments.length; i++) { const m = moments[i]; const container = page.locator(`.glucose-inline[data-moment-key="${m.key}"]`); await expect(container.locator('.glucose-inline-label')).toHaveText(m.label); await expect(container.locator('.glucose-inline-range')).toHaveText(m.range); } // Input attributes — check 4 inputs const inputMoments = ['nuchter', 'naOntbijt', 'naLunch', 'naAvondeten']; for (const moment of inputMoments) { const input = page.locator(`input.glucose-input[data-moment="${moment}"]`); await expect(input).toHaveAttribute('step', '0.1'); await expect(input).toHaveAttribute('min', '0'); await expect(input).toHaveAttribute('max', '30'); await expect(input).toHaveAttribute('inputmode', 'decimal'); await expect(input).toHaveAttribute('placeholder', '\u2014'); } }); // ---- 2. Nuchter binnen range (groen) ---- test('2. nuchter waarde 5.1 (binnen < 5.4) toont groene indicator', async ({ page }) => { await page.goto(BASE); await openDiary(page); await setGlucoseValue(page, 'nuchter', 5.1); const container = glucoseContainer(page, 'nuchter'); await expect(container).toHaveClass(/glucose-good/); await expect(container.locator('.glucose-status-icon')).toContainText('\u2705'); // ✅ await expect(container.locator('.glucose-status-text')).toHaveText('Binnen range'); }); // ---- 3. Nuchter ≥ 5.4 (rood) ---- test('3. nuchter waarde 5.4 (te hoog, ≥ 5.4) toont rode indicator', async ({ page }) => { await page.goto(BASE); await openDiary(page); await setGlucoseValue(page, 'nuchter', 5.4); const container = glucoseContainer(page, 'nuchter'); await expect(container).toHaveClass(/glucose-bad/); await expect(container.locator('.glucose-status-text')).toHaveText('Te hoog'); }); // ---- 4. Postprandiaal < 5.4 (rood/te laag) ---- test('4. naOntbijt waarde 5.3 (te laag, < 5.4) toont rode indicator', async ({ page }) => { await page.goto(BASE); await openDiary(page); await setGlucoseValue(page, 'naOntbijt', 5.3); const container = glucoseContainer(page, 'naOntbijt'); await expect(container).toHaveClass(/glucose-bad/); await expect(container.locator('.glucose-status-text')).toHaveText('Te laag'); }); // ---- 5. Postprandiaal binnen range (groen) ---- test('5. naOntbijt waarde 6.0 (binnen 5.4\u20136.7) toont groene indicator', async ({ page }) => { await page.goto(BASE); await openDiary(page); await setGlucoseValue(page, 'naOntbijt', 6.0); const container = glucoseContainer(page, 'naOntbijt'); await expect(container).toHaveClass(/glucose-good/); await expect(container.locator('.glucose-status-text')).toHaveText('Binnen range'); }); // ---- 6. Postprandiaal > 6.7 (rood/te hoog) ---- test('6. naLunch waarde 6.8 (te hoog, > 6.7) toont rode indicator', async ({ page }) => { await page.goto(BASE); await openDiary(page); await setGlucoseValue(page, 'naLunch', 6.8); const container = glucoseContainer(page, 'naLunch'); await expect(container).toHaveClass(/glucose-bad/); await expect(container.locator('.glucose-status-text')).toHaveText('Te hoog'); }); // ---- 7. Leeg veld — grijze 'nog meten' indicator ---- test('7. lege velden tonen grijze nog-meten indicator', async ({ page }) => { await page.goto(BASE); await openDiary(page); // Nuchter card should start empty const nuchter = glucoseContainer(page, 'nuchter'); await expect(nuchter).toHaveClass(/glucose-empty/); await expect(nuchter.locator('.glucose-status-text')).toHaveText('Nog meten'); await expect(nuchter.locator('.glucose-status-icon')).toContainText('\u23F9'); // Inline items should start empty const inlineMoments = ['naOntbijt', 'naLunch', 'naAvondeten']; for (const moment of inlineMoments) { const container = glucoseContainer(page, moment); await expect(container).toHaveClass(/glucose-empty/); await expect(container.locator('.glucose-status-text')).toHaveText('Nog meten'); } // All inputs should have empty value const inputs = page.locator('.glucose-input'); const inputCount = await inputs.count(); expect(inputCount).toBe(4); for (let i = 0; i < inputCount; i++) { await expect(inputs.nth(i)).toHaveValue(''); } }); // ---- 8. Persistentie — waarde blijft na page reload ---- test('8. persistentie: waarde blijft na page reload bewaard', async ({ page }) => { await page.goto(BASE); await openDiary(page); // Set a nuchter value await setGlucoseValue(page, 'nuchter', 5.1); // Verify it's shown correctly let container = glucoseContainer(page, 'nuchter'); await expect(container).toHaveClass(/glucose-good/); await expect(container.locator('.glucose-status-text')).toHaveText('Binnen range'); await expect(container.locator('.glucose-input')).toHaveValue('5.1'); // Reload the page await page.goto(BASE); await openDiary(page); // Value should still be there container = glucoseContainer(page, 'nuchter'); await expect(container).toHaveClass(/glucose-good/); await expect(container.locator('.glucose-status-text')).toHaveText('Binnen range'); await expect(container.locator('.glucose-input')).toHaveValue('5.1'); }); // ---- 9. Datum-navigatie — andere datum andere waarden ---- test('9. datum-navigatie: andere datum toont lege glucose waarden', async ({ page }) => { await page.goto(BASE); await openDiary(page); // Set a nuchter value for today await setGlucoseValue(page, 'nuchter', 5.1); // Verify today shows the value let container = glucoseContainer(page, 'nuchter'); await expect(container).toHaveClass(/glucose-good/); // Navigate to tomorrow await page.locator('#dayNext').click(); await page.waitForTimeout(600); // Tomorrow should have all empty glucose fields container = glucoseContainer(page, 'nuchter'); await expect(container).toHaveClass(/glucose-empty/); await expect(container.locator('.glucose-status-text')).toHaveText('Nog meten'); await expect(container.locator('.glucose-input')).toHaveValue(''); // Also check the other moments are empty too for (const moment of ['naOntbijt', 'naLunch', 'naAvondeten']) { const otherContainer = glucoseContainer(page, moment); await expect(otherContainer).toHaveClass(/glucose-empty/); } // Navigate back to today await page.locator('#dayToday').click(); await page.waitForTimeout(600); // Today's value should still be there container = glucoseContainer(page, 'nuchter'); await expect(container).toHaveClass(/glucose-good/); await expect(container.locator('.glucose-status-text')).toHaveText('Binnen range'); await expect(container.locator('.glucose-input')).toHaveValue('5.1'); }); // ---- 10. Decimale invoer — step 0.1 ---- test('10. decimale invoer: step 0.1 accepteert 5.5 en 6.2 correct', async ({ page }) => { await page.goto(BASE); await openDiary(page); // Check step attribute const input = page.locator('input.glucose-input[data-moment="nuchter"]'); await expect(input).toHaveAttribute('step', '0.1'); // Enter 5.5 (decimal) await setGlucoseValue(page, 'nuchter', 5.5); let container = glucoseContainer(page, 'nuchter'); await expect(container.locator('.glucose-input')).toHaveValue('5.5'); // 5.5 ≥ 5.4 for nuchter → should be bad/Te hoog await expect(container).toHaveClass(/glucose-bad/); await expect(container.locator('.glucose-status-text')).toHaveText('Te hoog'); // Now test a postprandial decimal value await setGlucoseValue(page, 'naOntbijt', 6.2); container = glucoseContainer(page, 'naOntbijt'); await expect(container.locator('.glucose-input')).toHaveValue('6.2'); await expect(container).toHaveClass(/glucose-good/); await expect(container.locator('.glucose-status-text')).toHaveText('Binnen range'); }); });