All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 50s
Gewijzigde functionaliteiten: - De open stippen in de doelvakken stonden links-boven in hun rastercel in plaats van gecentreerd, waardoor het patroon scheef oogde; ze staan nu op dezelfde as als de gevulde stippen. - De stippen van de geldblokken en van alle cijferblokken in een smalgemaakte widget staken onder het blok uit. De stipmaat komt nu uit een variabele die elk bloktype naast zijn eigen hoogte zet, in plaats van uit losse hardgecodeerde regels per widget. - De smalle-widget-weergave verkleint alleen nog de breedte; blokhoogtes blijven gelijk, want juist het verkleinen van de hoogte duwde het raster naar buiten. Letterblokken krijgen daar een passend kleiner cijfer/letterformaat. - Nieuw vangnet: het raster houdt altijd zijn maat en het label geeft mee als een blokformaat krap is, zodat een nieuw blokformaat dit niet opnieuw kan veroorzaken. Tests: 196 geslaagd (4 nieuw in test/dot-grid.test.js; die vier vallen ook daadwerkelijk om op de oude CSS). Daarnaast in de browser opgemeten voor alle zeven blokcontexten, bij normale en smalle widgetbreedte, plus screenshots en een controle in de echte leerlingweergave. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0126hjBPD2ggZ2LEoDYZSW7S
106 lines
5 KiB
JavaScript
106 lines
5 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFile } from 'node:fs/promises';
|
|
|
|
/* Stippenraster (braillepatroon uit dotGrid(), data.js). Het ging mis doordat de
|
|
stipmaat per bloktype hardgecodeerd was terwijl blokhoogte, labelhoogte en
|
|
marges óók per type verschillen: de geldblokken erfden de maat van een ander
|
|
formaat en de smalle-widget-query verkleinde alleen de blokken. Gevolg: de
|
|
stippen staken onder het blok uit. Deze test bewaakt de regels die dat
|
|
voorkomen; de volledige geometrie is daarnaast in de browser opgemeten. */
|
|
|
|
const CSS = await readFile('public/css/teach.css', 'utf8');
|
|
|
|
/* alle regels als [selector, declaraties]; commentaar eruit want daar staan
|
|
voorbeelden in die anders als declaratie meetellen */
|
|
function rules(css) {
|
|
const clean = css.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
const out = [];
|
|
for (const chunk of clean.split('}')) {
|
|
const i = chunk.lastIndexOf('{');
|
|
if (i < 0) continue;
|
|
const selector = chunk.slice(0, i).split('{').pop().trim();
|
|
if (!selector || selector.startsWith('@')) continue;
|
|
out.push([selector, chunk.slice(i + 1)]);
|
|
}
|
|
return out;
|
|
}
|
|
const RULES = rules(CSS);
|
|
const declares = (decls, prop) => new RegExp(`(^|;|\\s)${prop}\\s*:`).test(decls);
|
|
|
|
/* het bloktype in een selector: .money-digit-block, .blok.sm, .blok, ... */
|
|
function contextTokens(selector) {
|
|
return selector.split(',')
|
|
.map((part) => part.trim())
|
|
.filter((part) => /\.(blok|slot)\b/.test(part) && !/\s\.(bl|ghost|dots|blok-dots|slot-dots)\b/.test(part))
|
|
.map((part) => (part.match(/\.[a-z0-9-]*(?:block|slot|blok)\b/g) || []).join(''));
|
|
}
|
|
|
|
test('elk bloktype dat een eigen hoogte krijgt, zet ook zijn eigen stipmaat', () => {
|
|
const withDot = new Set();
|
|
RULES.forEach(([selector, decls]) => {
|
|
if (declares(decls, '--dot')) contextTokens(selector).forEach((t) => withDot.add(t));
|
|
});
|
|
const missing = [];
|
|
RULES.forEach(([selector, decls]) => {
|
|
if (!declares(decls, 'height')) return;
|
|
contextTokens(selector).forEach((token) => {
|
|
if (!token) return;
|
|
// een variant erft de maat van zijn basis (.blok/.slot) als die het zet
|
|
const base = token.includes('slot') ? '.slot' : '.blok';
|
|
if (!withDot.has(token) && !withDot.has(base)) missing.push(`${selector} (${token})`);
|
|
});
|
|
});
|
|
assert.deepEqual(missing, [], 'deze regels zetten een blokhoogte zonder dat er ergens een --dot voor dat type bestaat');
|
|
assert.ok(withDot.has('.blok') && withDot.has('.slot'), 'basisblok en -slot zetten --dot');
|
|
});
|
|
|
|
test('de stippen zijn nooit kleiner dan 6px: het patroon moet leesbaar blijven', () => {
|
|
const values = [...CSS.matchAll(/--dot\s*:\s*([\d.]+)px/g)].map((m) => Number(m[1]));
|
|
assert.ok(values.length >= 5, `verwacht meerdere stipmaten, gevonden: ${values.length}`);
|
|
values.forEach((v) => assert.ok(v >= 6, `stipmaat ${v}px is onder de leesbaarheidsgrens`));
|
|
});
|
|
|
|
test('geen enkel bloktype heeft nog een eigen hardgecodeerd stippenraster', () => {
|
|
const hardcoded = RULES.filter(([selector, decls]) =>
|
|
/\.dots\b/.test(selector)
|
|
&& /(grid-template-(columns|rows)\s*:[^;]*\d+px|(?:^|;|\s)(width|height)\s*:\s*\d+px)/.test(decls));
|
|
assert.deepEqual(hardcoded.map(([s]) => s), [],
|
|
'stipmaten horen via var(--dot) te lopen, anders loopt een nieuw blokformaat weer uit de pas');
|
|
});
|
|
|
|
/* Narekening van de verticale begroting per bloktype. De blokrand telt mee
|
|
(border-box), dus: rand + padding-top + labelhoogte + rasterhoogte +
|
|
ondermarge moet binnen de blokhoogte passen. Rasterhoogte = 3 stippen + 2
|
|
tussenruimtes van --dot/3. */
|
|
const CONTEXTS = [
|
|
{ naam: 'letterblok', maat: '.blok', label: '.blok .bl', marge: '.blok .blok-dots', dot: '.blok' },
|
|
{ naam: 'letterslot', maat: '.slot', label: '.slot .ghost', marge: '.slot .slot-dots', dot: '.slot' },
|
|
{ naam: 'rekenblok', maat: '.blok.sm', label: '.blok.sm .bl', marge: '.blok.sm .blok-dots', dot: '.blok.sm' },
|
|
{ naam: 'rekenslot', maat: '.slot.sm', label: '.slot.sm .ghost', marge: '.slot.sm .slot-dots', dot: '.slot.sm' },
|
|
];
|
|
function value(selectorPart, prop) {
|
|
let found = null;
|
|
RULES.forEach(([selector, decls]) => {
|
|
if (!selector.split(',').some((part) => part.trim() === selectorPart)) return;
|
|
const m = decls.match(new RegExp(`(?:^|;|\\s)${prop}\\s*:\\s*([\\d.]+)px`));
|
|
if (m) found = Number(m[1]);
|
|
});
|
|
return found;
|
|
}
|
|
|
|
test('label, stippenraster en marges passen samen binnen elk basisblok', () => {
|
|
CONTEXTS.forEach((ctx) => {
|
|
const hoogte = value(ctx.maat, 'height');
|
|
const padding = value(ctx.maat, 'padding-top') ?? 0;
|
|
const rand = (value(ctx.maat, 'border') ?? 2.5) * 2;
|
|
const label = value(ctx.label, 'height');
|
|
const marge = value(ctx.marge, 'margin-bottom');
|
|
const dot = value(ctx.dot, '--dot');
|
|
assert.ok(hoogte && label && marge && dot, `${ctx.naam}: maten niet gevonden`);
|
|
const raster = 3 * dot + 2 * (dot / 3);
|
|
const nodig = padding + label + raster + marge + rand;
|
|
assert.ok(nodig <= hoogte,
|
|
`${ctx.naam}: ${nodig.toFixed(1)}px nodig in een blok van ${hoogte}px - de stippen steken eruit`);
|
|
});
|
|
});
|