karby/tests/glucose-e2e.spec.js

343 lines
13 KiB
JavaScript

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');
});
// ---- 11. Nuchter card staat vóór Ontbijt-card in de DOM ----
test('11. nuchter card staat vóór ontbijt-card in DOM volgorde', async ({ page }) => {
await page.goto(BASE);
await openDiary(page);
const eetmomentList = page.locator('#eetmomentList');
const firstChild = eetmomentList.locator('> *').first();
const secondChild = eetmomentList.locator('> *').nth(1);
await expect(firstChild).toHaveClass(/nuchter-card/);
await expect(secondChild).toHaveClass(/eetmoment-card/);
});
// ---- 12. Alleen Ontbijt/Lunch/Avondeten hebben glucose-inline, Tussendoor niet ----
test('12. alleen Ontbijt/Lunch/Avondeten hebben glucose-inline, Tussendoor niet', async ({ page }) => {
await page.goto(BASE);
await openDiary(page);
// These should have inline glucose
for (const key of ['naOntbijt', 'naLunch', 'naAvondeten']) {
await expect(page.locator(`.glucose-inline[data-moment-key="${key}"]`)).toBeVisible();
}
// Verify Nuchter is a separate card, not an inline item
await expect(page.locator('.glucose-inline[data-moment-key="nuchter"]')).toHaveCount(0);
await expect(page.locator('.nuchter-card')).toBeVisible();
// 3 inline items total (Ontbijt, Lunch, Avondeten — not Tussendoor)
const inlineItems = page.locator('.glucose-inline');
await expect(inlineItems).toHaveCount(3);
});
// ---- 13. Glucose-inline is zichtbaar ook als eetmoment-card is ingeklapt (geen items) ----
test('13. glucose-inline zichtbaar ook als eetmoment-card leeg is', async ({ page }) => {
await page.goto(BASE);
await openDiary(page);
// The Ontbijt card should be visible even without items
const ontbijtCard = page.locator('.eetmoment-card').nth(1); // 2nd card (1st is nuchter)
// Wait for it to be in DOM
await expect(ontbijtCard).toBeVisible();
// It should have a glucose inline section even when empty
const inline = ontbijtCard.locator('.glucose-inline[data-moment-key="naOntbijt"]');
await expect(inline).toBeVisible();
// The input should be functional
const input = inline.locator('.glucose-input');
await expect(input).toBeVisible();
await expect(input).toHaveAttribute('data-moment', 'naOntbijt');
});
// ---- 14. Oude .glucose-section bestaat niet meer ----
test('14. oude .glucose-section en .glucose-grid bestaan niet meer', async ({ page }) => {
await page.goto(BASE);
await openDiary(page);
await expect(page.locator('.glucose-section')).toHaveCount(0);
await expect(page.locator('.glucose-grid')).toHaveCount(0);
await expect(page.locator('.glucose-title')).toHaveCount(0);
await expect(page.locator('.glucose-unit-label')).toHaveCount(0);
await expect(page.locator('.glucose-item')).toHaveCount(0);
});
});