import { test } from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { ICON_PATHS, ICON_LABELS, ICON_GROUPS } from '../public/assets/icons.js'; const read = path => readFileSync(new URL(path, import.meta.url), 'utf8'); test('the editor draws the same icons the website renders and the server allows', () => { // Drei Listen, ein Satz: die Zeichnungen der Website (Twig), die Vorschau im Editor und die // erlaubten Namen im Validator. Weicht eine ab, zeigt die Auswahl ein anderes Symbol als die // Seite - oder bietet eins an, das der Server beim Speichern ablehnt. Abhilfe: node scripts/build-icons.mjs const twig = read('../templates/blocks/icon.html.twig'); const website = Object.fromEntries([...twig.matchAll(/^\s{4}'?([a-z-]+)'?: '([^']+)',?$/gm)].map(([, name, path]) => [name, path])); assert.deepEqual(ICON_PATHS, website); const validator = read('../src/Content/DocumentValidator.php'); const allowed = [...validator.match(/public const ICONS = \[([^\]]+)\]/)[1].matchAll(/'([a-z-]+)'/g)].map(([, name]) => name); assert.deepEqual(Object.keys(ICON_PATHS), allowed); }); test('every icon has a German name and sits in exactly one category of the dialog', () => { assert.deepEqual(Object.keys(ICON_LABELS).sort(), Object.keys(ICON_PATHS).sort()); const listed = ICON_GROUPS.flatMap(([, names]) => names); assert.equal(new Set(listed).size, listed.length, 'Ein Symbol steht in zwei Kategorien.'); assert.deepEqual([...listed].sort(), Object.keys(ICON_PATHS).sort(), 'Jedes Symbol steht in einer Kategorie.'); // Der bisherige Pfeil steht vorn: ihn trägt ein Button ohne eigene Wahl. assert.equal(ICON_GROUPS[0][1][0], 'arrow-up-right'); assert.ok(listed.length >= 90, `Nur ${listed.length} Symbole.`); for (const path of Object.values(ICON_PATHS)) assert.match(path, /^[Mm][0-9MmLlHhVvCcSsQqTtAaZz .,-]+$/, 'Ein Pfad enthält Zeichen, die keine Zeichnung sind.'); });