const { test, expect } = require('@playwright/test'); /** * Regressietest: eetmoment-labels (icon + naam) zichtbaar in dagboek. * * Achtergrond (kaart t_a1d49160): "Eetmoment-labels niet zichtbaar in dagboek — * icon+naam tekst valt weg". Niet reproduceerbaar tegen lokale app noch live; * template/CSS zijn sinds de initiële commit correct. Deze test borgt dat alle * zes eetmomentkaarten hun icon + naam tonen, niet transparant en niet * afgeknipt — voor lege én gevulde kaarten, op mobiel én desktop. * * Determinisme: de dagboek-data wordt per scenario geseed via localStorage * (addInitScript) en er wordt gewacht tot de fadeIn-stagger-animatie klaar is * (opacity === 1) voordat er geassert wordt. Computed styles, geen screenshots. */ const MOMENTS = [ { id: 'Ontbijt', name: 'Ontbijt', icon: '🥐' }, { id: 'Tussendoor 1', name: 'Tussendoor 1', icon: '☕' }, { id: 'Lunch', name: 'Lunch', icon: '🥪' }, { id: 'Tussendoor 2', name: 'Tussendoor 2', icon: '🍎' }, { id: 'Avondeten', name: 'Avondeten', icon: '🍲' }, { id: 'Tussendoor 3', name: 'Tussendoor 3', icon: '🍪' }, ]; function todayStr() { const d = new Date(); const p = (n) => String(n).padStart(2, '0'); return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`; } function buildDiaryJson(filled) { const day = {}; for (const m of MOMENTS) { day[m.id] = filled ? [{ item: { naam: 'Volkoren brood', kh: '50.0', n: '1' }, portie: 30 }] : []; } return JSON.stringify({ [todayStr()]: day }); } /** Seed dagboek-data in een verse context en open het dagboek-tabblad. */ async function openDiary(page, filled) { await page.addInitScript((json) => { localStorage.setItem('koolhydraat-dagboek', json); }, buildDiaryJson(filled)); await page.goto('/'); await page.locator('#navDagboek').click(); // Wacht tot alle 6 kaarten gerenderd zijn én de fadeIn-stagger (0.35s + // maximaal 5 * 50ms delay) volledig is afgelopen — anders is opacity nog 0. await expect(page.locator('.eetmoment-card')).toHaveCount(6, { timeout: 5000 }); await expect .poll(() => page.locator('.eetmoment-card').last().evaluate((el) => getComputedStyle(el).opacity) , { timeout: 5000 }) .toBe('1'); } /** Assert per kaart: icon+naam aanwezig, zichtbaar, niet transparant, niet afgeknipt. */ async function expectAllLabelsVisible(page) { const cards = page.locator('.eetmoment-card'); expect(await cards.count()).toBe(6); for (let i = 0; i < MOMENTS.length; i++) { const m = MOMENTS[i]; const card = cards.nth(i); const header = card.locator('.eetmoment-header'); const icon = card.locator('.eetmoment-icon'); const name = card.locator('.eetmoment-name'); // Template: juiste moment-id, icon en naam aanwezig await expect(header).toHaveAttribute('data-moment', m.id); await expect(icon).toHaveText(m.icon, { timeout: 3000 }); await expect(name).toHaveText(m.name, { timeout: 3000 }); // Computed style: zichtbaar + niet transparant + niet afgeknipt for (const el of [icon, name]) { const cs = await el.evaluate((node) => { const style = getComputedStyle(node); const r = node.getBoundingClientRect(); const cardR = node.closest('.eetmoment-card').getBoundingClientRect(); const alpha = (style.color.match(/rgba?\(([^)]+)\)/) || [])[1]; const a = alpha ? parseFloat(alpha.split(',')[3] ?? '1') : 1; return { display: style.display, visibility: style.visibility, opacity: parseFloat(style.opacity), colorAlpha: Number.isNaN(a) ? 1 : a, w: r.width, h: r.height, insideCard: r.bottom <= cardR.bottom + 1 && r.top >= cardR.top - 1, }; }); expect(cs.display, `${m.id}: ${el === icon ? 'icon' : 'naam'} display`).not.toBe('none'); expect(cs.visibility, `${m.id}: ${el === icon ? 'icon' : 'naam'} visibility`).toBe('visible'); expect(cs.opacity, `${m.id}: ${el === icon ? 'icon' : 'naam'} opacity`).toBeGreaterThan(0.99); expect(cs.colorAlpha, `${m.id}: ${el === icon ? 'icon' : 'naam'} transparant`).toBeGreaterThan(0); expect(cs.w, `${m.id}: ${el === icon ? 'icon' : 'naam'} breedte 0`).toBeGreaterThan(0); expect(cs.h, `${m.id}: ${el === icon ? 'icon' : 'naam'} hoogte 0`).toBeGreaterThan(0); expect(cs.insideCard, `${m.id}: ${el === icon ? 'icon' : 'naam'} afgeknipt`).toBe(true); } } } test.describe('Eetmoment-labels — icon+naam zichtbaar (regressie t_a1d49160)', () => { for (const filled of [false, true]) { const label = filled ? 'gevuld' : 'leeg'; test(`${label}: alle zes labels zichtbaar, niet transparant/afgeknipt (mobiel)`, async ({ page }) => { await openDiary(page, filled); await expectAllLabelsVisible(page); }); test.describe(`desktop viewport (${label})`, () => { test.use({ viewport: { width: 1280, height: 800 } }); test('alle zes labels zichtbaar, niet transparant/afgeknipt', async ({ page }) => { await openDiary(page, filled); await expectAllLabelsVisible(page); }); }); } });