import { test } from 'node:test' import assert from 'node:assert/strict' import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' const WURZEL = path.join(path.dirname(fileURLToPath(import.meta.url)), '..') const NEU = path.join(WURZEL, 'public', 'neu') const MODULE = ['ansicht-hilfen', 'rechnung-offen', 'install-hinweis', 'website-details', 'auftrags-zeitleiste', 'ansicht-website', 'ansicht-chat', 'ansicht-vertraege', 'vertraege-rechnungen', 'vertraege-abo', 'ansicht-stunden', 'ansicht-speicher', 'ansicht-bestellen', 'ansicht-wechsel', 'ansicht-gutschrift', 'ansicht-passwort', 'bestell-bausteine'] // Seit P7 sprechen auch die vier Bestellseiten „du“ (Fassung 6, C14); einzige Ausnahme ist der Rechtstext TAKEOVER_CONDITIONS (hash-gepinnt) const WORTLAUT = [] const BESTELLSEITEN = ['ansicht-bestellen', 'ansicht-wechsel', 'ansicht-speicher', 'ansicht-gutschrift', 'bestell-bausteine'] const vorhanden = MODULE.filter((n) => existsSync(path.join(NEU, `${n}.js`))) const ohneKommentare = (q) => q.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|[^:'"`\\])\/\/[^\n]*/g, '$1') const lies = (n) => ohneKommentare(readFileSync(path.join(NEU, `${n}.js`), 'utf8')) function dateienUnter(ordner, muster) { const aus = [] for (const name of readdirSync(path.join(WURZEL, ordner))) { const pfad = path.join(WURZEL, ordner, name) if (statSync(pfad).isDirectory()) aus.push(...dateienUnter(path.join(ordner, name), muster)) else if (muster.test(name)) aus.push(pfad) } return aus } test('P2-Module: kein HTML-Einsetzen, nur relative Importe, keine Admin-Verweise', () => { for (const n of vorhanden) { const q = lies(n) assert.doesNotMatch(q, /innerHTML|insertAdjacentHTML|outerHTML|document\.write|console\.log/, n) assert.doesNotMatch(q, /from\s+'\/|import\(\s*'\//, `${n}: absoluter Import`) assert.doesNotMatch(q, /admin\.html|kalkulation\.html|\/api\/portal-admin/, n) } }) /** Quelltext ohne den Rechtstext TAKEOVER_CONDITIONS (er bleibt Zeichen für Zeichen in der Sie-Form). */ const ohneRechtstext = (q) => { const i = q.indexOf('const TAKEOVER_CONDITIONS') return i < 0 ? q : q.slice(0, i) + q.slice(q.indexOf(']', i)) } test('P2-Module: Du-Form in allen Modulen, einzige Ausnahme der Block TAKEOVER_CONDITIONS (C14, P7)', () => { for (const n of vorhanden.filter((x) => !WORTLAUT.includes(x))) assert.doesNotMatch(ohneRechtstext(lies(n)), /\b(Sie|Ihr(e[nmrs]?)?|Ihnen)\b/, n) assert.match(lies('ansicht-gutschrift'), /Ihr Abo ersetzt/, 'der Rechtstext bleibt unverändert in der Sie-Form') }) test('Bestellseiten: kein kundenart im Body, keine eigene h1 (Titel meldet die Ansicht)', () => { for (const n of BESTELLSEITEN) { assert.ok(existsSync(path.join(NEU, `${n}.js`)), n) const q = lies(n) assert.doesNotMatch(q, /body:\s*\{[^}]*kundenart/s, n) assert.doesNotMatch(q, /h\(\s*['"]h1['"]/, n) } }) test('public/neu/: kein Lila in irgendeiner Textdatei (die Prüfung lila liest jedes geladene Stylesheet samt ungenutzter Regeln)', () => { for (const pfad of dateienUnter('public/neu', /\.(css|html|js|mjs|svg|webmanifest)$/)) assert.doesNotMatch(readFileSync(pfad, 'utf8'), /#9d3dfe|rgba?\(\s*157\s*,\s*61\s*,\s*254/i, pfad) })