karby/tests/eetmoment-labels.spec.js

123 lines
5 KiB
JavaScript

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);
});
});
}
});