import test from 'node:test' import assert from 'node:assert/strict' import { existsSync, readFileSync, readdirSync } from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' // Regeln für alle React-Teile der Hustler-Seite, geprüft am Quelltext (Muster adminRoute.test.mjs): src/components/workers/** mit hochhaus/, pult/ und keller/. // Das Verhalten der einzelnen Bildschirme prüfen die Komponententests (hustlerKopf, hustlerDialog, hustlerHochhausWelt, hustlerSeite, hustlerPult, hustlerKeller ...). // Seit T11 gibt es die alte helle Oberfläche und die Kanvas-Engine nicht mehr; ihre Zusicherungen sind in diese Tests gewandert (Tabelle im Übergabebericht von T11). const COMPONENTS = fileURLToPath(new URL('../../src/components/workers/', import.meta.url)) const SRC = fileURLToPath(new URL('../../src/', import.meta.url)) const read = (p) => readFileSync(path.join(COMPONENTS, p), 'utf8') const readSrc = (p) => readFileSync(path.join(SRC, p), 'utf8') const walk = (dir) => readdirSync(dir, { withFileTypes: true }).flatMap((e) => (e.isDirectory() ? walk(path.join(dir, e.name)) : [path.join(dir, e.name)])) const komponenten = () => walk(COMPONENTS).filter((f) => /\.(jsx?|mjs)$/.test(f)) const rel = (f) => path.relative(COMPONENTS, f) // Kommentare weg: Block- und Zeilenkommentare (// nur am Zeilenanfang oder nach Leerraum, damit https:// in Zeichenketten bleibt) const ohneKommentare = (code) => code.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|\s)\/\/.*$/gm, '$1') // Öffnende Tags `` samt Attributen. Klammern {…} und Zeichenketten werden übersprungen, damit das `=>` eines onClick das Tag nicht beendet. function oeffnendeTags(src, name) { const tags = [] const re = new RegExp(`<${name}(?=[\\s/>])`, 'g') for (let m; (m = re.exec(src)) !== null;) { let tiefe = 0 let quote = '' let i = m.index + m[0].length for (; i < src.length; i++) { const c = src[i] if (quote) { if (c === '\\') i += 1 else if (c === quote) quote = '' continue } if (c === '"' || c === "'" || c === '`') quote = c else if (c === '{') tiefe += 1 else if (c === '}') tiefe -= 1 else if (c === '>' && tiefe === 0) break } tags.push(src.slice(m.index, i + 1)) } return tags } // Wert eines Attributs im Tag: Zeichenkette (mit Anführungszeichen) oder der Ausdruck in {…} ohne Klammern; null, wenn es das Attribut nicht gibt function attribut(tag, name) { const m = new RegExp(`(? { const dateien = komponenten() for (const dir of ['hochhaus', 'pult', 'keller']) assert.ok(dateien.some((f) => rel(f).startsWith(dir + path.sep)), `${dir}/ wird mitgelesen`) assert.ok(dateien.length >= 15, `nur ${dateien.length} Dateien gelesen`) for (const f of dateien) { const code = ohneKommentare(readFileSync(f, 'utf8')) assert.doesNotMatch(code, /dangerouslySetInnerHTML|innerHTML|outerHTML|insertAdjacentHTML|document\.write/, rel(f)) assert.doesNotMatch(code, / { for (const f of komponenten()) assert.doesNotMatch(ohneKommentare(readFileSync(f, 'utf8')), /useAuth|appwrite/i, rel(f)) }) test('die alte Oberfläche und die Kanvas-Engine sind weg, und nichts unter src/ verweist noch darauf (T11)', () => { for (const alt of ['engine', 'WorkersStage.jsx', 'WorkersBoard.jsx', 'WorkerCard.jsx', 'WorkerList.jsx', 'WorkersKpis.jsx']) assert.equal(existsSync(path.join(COMPONENTS, alt)), false, alt) const verweis = /['"][^'"\n]*(?:\/engine\/[\w.-]+|\/(?:WorkersStage|WorkersBoard|WorkerCard|WorkerList|WorkersKpis))(?:\.jsx?)?['"]/ let gelesen = 0 for (const f of walk(SRC).filter((x) => /\.jsx?$/.test(x))) { gelesen += 1 assert.doesNotMatch(ohneKommentare(readFileSync(f, 'utf8')), verweis, path.relative(SRC, f)) } assert.ok(gelesen > 100, `nur ${gelesen} Dateien gelesen`) }) // Frühere Prüfung der Bühne und des Boards (PF-08): der WOMS-Button hat keinen Standard-type, ein