build-legal-html: Satz auftraege für die Widerrufsbelehrung der Projektaufträge und die AGB, beide Seiten erzeugt
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
edccd4d8af
commit
aeae17b491
4 files changed
+485
-5
No files matched your search
@@ -426,3 +426,126 @@ test('agb_text_gleich_website: die Kopie ist wortgleich mit der Seite AGB.tsx de
|
||||
assert.ok(kopie === website, `Die AGB-Kopie weicht von den AGB der Website ab (${ersterUnterschied(kopie, website)}). Ist die Website neu, braucht die Kopie eine neue Fassung (AGB_FASSUNG, agb.md, Seite, PDF, Hash).`)
|
||||
assert.equal(sha256(website), AGB_TEXT_SHA256)
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------- Seiten der Widerrufsbelehrung und der AGB
|
||||
|
||||
// jedes Dokument wird in den Tests neu geholt, damit ein fehlender Generator nur die betroffenen Tests scheitern lässt (nicht das Laden der Datei)
|
||||
const dokumente = () => gen.auftraegeDokumente()
|
||||
const klartext = (html) =>
|
||||
html
|
||||
.replace(/<style>[\s\S]*?<\/style>/g, '')
|
||||
.replace(/<[^>]+>/g, ' ')
|
||||
.replace(/"/g, '"').replace(/'/g, "'").replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
const quelleVon = (d) => (d.markdown ? d.markdown() : lies(d.md))
|
||||
|
||||
test('auftraegeDokumente: widerruf (aus texte.js) und agb (aus docs/rechtliches/agb.md) mit Quelle, Ziel und Navigation auf die Fassungen', () => {
|
||||
const DOKUMENTE = dokumente()
|
||||
assert.deepEqual(DOKUMENTE.map((d) => d.doc), ['widerruf', 'agb'])
|
||||
assert.deepEqual(
|
||||
DOKUMENTE.map((d) => [d.md, d.html]),
|
||||
[
|
||||
[null, `public/rechtliches/widerrufsbelehrung-projektauftrag-${texte.TEXTE_FASSUNG}.html`],
|
||||
['docs/rechtliches/agb.md', `public/rechtliches/agb-${AGB_FASSUNG}.html`],
|
||||
],
|
||||
)
|
||||
assert.deepEqual(DOKUMENTE.map((d) => d.meta.fassung), [texte.TEXTE_FASSUNG, AGB_FASSUNG])
|
||||
const adressen = DOKUMENTE.map((d) => d.html.replace(/^public/, ''))
|
||||
for (const d of DOKUMENTE) {
|
||||
const hrefs = d.meta.nav.map((l) => l.href)
|
||||
for (const a of adressen) assert.ok(hrefs.includes(a), `Navigation von ${d.doc} verlinkt ${a}`)
|
||||
assert.ok(hrefs.includes('/abo-bedingungen.html') && hrefs.includes('/datenschutz.html'))
|
||||
}
|
||||
// andere Fassungen wirken auf Dateinamen und Meta; die Quelle der Belehrung folgt der Fassung
|
||||
const andere = gen.auftraegeDokumente('2027-01-02', '2027-01-03')
|
||||
assert.equal(andere[0].html, 'public/rechtliches/widerrufsbelehrung-projektauftrag-2027-01-02.html')
|
||||
assert.equal(andere[1].html, 'public/rechtliches/agb-2027-01-03.html')
|
||||
assert.ok(andere[0].markdown().includes('**Fassung 2027-01-02**'))
|
||||
})
|
||||
|
||||
test('auftraege_html_gleich_generator: die eingecheckten Seiten sind genau render(Quelle), deterministisch, ohne Datum außer den Fassungen; --check Exit 0', () => {
|
||||
const DOKUMENTE = dokumente()
|
||||
for (const d of DOKUMENTE) {
|
||||
const md = quelleVon(d)
|
||||
const html = gen.render(md, d.meta)
|
||||
assert.equal(lies(d.html), html, `${d.html} weicht vom Generator ab (node scripts/build-legal-html.mjs auftraege)`)
|
||||
assert.equal(gen.render(md, d.meta), html, 'deterministisch: zweiter Lauf gleiche Bytes')
|
||||
assert.doesNotMatch(html.replaceAll(texte.TEXTE_FASSUNG, '').replaceAll(AGB_FASSUNG, ''), /\b\d{4}-\d{2}-\d{2}\b/, `${d.html}: Datum außer den Fassungen`)
|
||||
}
|
||||
const lauf = spawnSync(process.execPath, ['--max-old-space-size=512', 'scripts/build-legal-html.mjs', 'auftraege', '--check'], { cwd: ROOT, encoding: 'utf8', timeout: 60000 })
|
||||
assert.equal(lauf.status, 0, lauf.stdout + lauf.stderr)
|
||||
assert.equal([...lauf.stdout.matchAll(/^ok /gm)].length, 2)
|
||||
})
|
||||
|
||||
test('auftraege_html_text_gleich_quelle: der sichtbare Text jeder Seite ist genau der Text der Quelle (plus Navigation und Marke)', () => {
|
||||
const DOKUMENTE = dokumente()
|
||||
for (const d of DOKUMENTE) {
|
||||
const aus = quelleVon(d).replace(/\r/g, '').split('\n').filter((z) => z.trim() !== '').map((z) => z.replace(/^#{1,3} /, '').replace(/^- /, ''))
|
||||
const erwartet = norm(aus.join(' ').replace(/\*\*|`/g, ''))
|
||||
const html = lies(d.html)
|
||||
const main = html.slice(html.indexOf('<main>'), html.indexOf('</main>')).replace(/<nav class="top">[\s\S]*?<\/nav>/, '').replace('<div class="brand">WEBKLAR</div>', '')
|
||||
assert.equal(klartext(main), erwartet, `${d.html}: sichtbarer Text weicht von der Quelle ab`)
|
||||
}
|
||||
})
|
||||
|
||||
test('auftraege_html_sauber: keine Kommentare, Entwurfsmarken, Skripte oder Ereignis-Attribute; Fassung im Kopf; Links nur relativ oder https', () => {
|
||||
const DOKUMENTE = dokumente()
|
||||
for (const d of DOKUMENTE) {
|
||||
const html = lies(d.html)
|
||||
assert.doesNotMatch(html, /<!--|Entwurf|ENTWURF|Prüfpunkt|<script|<iframe|<img|<form|<link|\sstyle=|\ssrc=/, `${d.html}: verbotener Inhalt`)
|
||||
assert.doesNotMatch(html, /\son[a-z]+\s*=/i, `${d.html}: Ereignis-Attribut`)
|
||||
assert.doesNotMatch(html, /⚖|\bQ\d+\b|\{\{|\}\}|undefined|NaN/, `${d.html}: Marker oder Platzhalter`)
|
||||
assert.ok(html.includes(`<strong>Fassung ${d.meta.fassung}</strong>`), `${d.html}: Fassung fehlt im Kopf`)
|
||||
assert.ok(html.includes(`<meta name="webklar-fassung" content="${d.meta.fassung}">`))
|
||||
assert.match(html, /<html lang="de">/)
|
||||
assert.match(html, /<title>[^<]+ – WEBklar<\/title>/)
|
||||
const hrefs = [...html.matchAll(/\shref="([^"]*)"/g)].map((m) => m[1])
|
||||
assert.ok(hrefs.length >= 6)
|
||||
for (const h of hrefs) assert.ok(/^\/(?!\/)/.test(h) || /^https:\/\//.test(h), `${d.html}: Link ${h}`)
|
||||
assert.ok(html.includes(`content: "WEBklar · ${d.meta.seitenkopf}"`))
|
||||
assert.equal([...html.matchAll(/<h1>/g)].length, 1)
|
||||
assert.ok(html.includes('<div class="brand">WEBKLAR</div>'))
|
||||
}
|
||||
})
|
||||
|
||||
test('auftraege_lauf: --check meldet fehlende oder abweichende Seiten mit Exit 1 und schreibt nichts; ohne --check werden beide Seiten geschrieben; Aufruf nennt alle Sätze', () => {
|
||||
const DOKUMENTE = dokumente()
|
||||
const dir = mkdtempSync(join(tmpdir(), 'legalhtml-auftraege-'))
|
||||
try {
|
||||
mkdirSync(join(dir, 'docs/rechtliches'), { recursive: true })
|
||||
mkdirSync(join(dir, 'public/rechtliches'), { recursive: true })
|
||||
cpSync(join(ROOT, AGB_MD), join(dir, AGB_MD))
|
||||
const zeilen = []
|
||||
const ruf = (...args) => gen.lauf(args, { root: dir, log: (z) => zeilen.push(z), fehler: (z) => zeilen.push(z) })
|
||||
// noch keine Seiten: --check meldet beide und schreibt nichts
|
||||
assert.equal(ruf('auftraege', '--check'), 1)
|
||||
assert.equal(zeilen.filter((z) => z.startsWith('ABWEICHT')).length, 2)
|
||||
assert.equal(existsSync(join(dir, DOKUMENTE[0].html)), false, '--check schreibt nichts')
|
||||
// schreiben, dann stimmt --check, und die Bytes sind die der eingecheckten Seiten
|
||||
assert.equal(ruf('auftraege'), 0)
|
||||
assert.equal(ruf('auftraege', '--check'), 0)
|
||||
for (const d of DOKUMENTE) assert.equal(readFileSync(join(dir, d.html), 'utf8'), lies(d.html), 'dieselben Bytes wie die eingecheckte Seite')
|
||||
// eine Seite von Hand geändert, die andere Quelle geändert: Abweichung, die Dateien bleiben wie sie sind
|
||||
const ziel = join(dir, DOKUMENTE[1].html)
|
||||
const handgemacht = readFileSync(ziel, 'utf8').replace('<h2>1. Vertragsgegenstand</h2>', '<h2>1. Vertragsgegenstand (von Hand)</h2>')
|
||||
assert.notEqual(handgemacht, readFileSync(ziel, 'utf8'))
|
||||
writeFileSync(ziel, handgemacht)
|
||||
zeilen.length = 0
|
||||
assert.equal(ruf('auftraege', '--check'), 1)
|
||||
assert.deepEqual(zeilen.filter((z) => z.startsWith('ABWEICHT')), [`ABWEICHT ${DOKUMENTE[1].html}`])
|
||||
assert.equal(readFileSync(ziel, 'utf8'), handgemacht, '--check ändert die Datei nicht')
|
||||
writeFileSync(ziel, lies(DOKUMENTE[1].html))
|
||||
writeFileSync(join(dir, AGB_MD), `${lies(AGB_MD)}\n## 15. Neu\n\n(1) Neuer Absatz.\n`)
|
||||
assert.equal(ruf('auftraege', '--check'), 1)
|
||||
// falscher Aufruf: Exit 2, die Meldung nennt termine, shop und auftraege
|
||||
zeilen.length = 0
|
||||
assert.equal(ruf('analyse'), 2)
|
||||
assert.equal(ruf('auftraege', '--schreiben'), 2)
|
||||
assert.equal(ruf(), 2)
|
||||
assert.match(zeilen[0], /Aufruf: node scripts\/build-legal-html\.mjs termine\|shop \[--check\]/)
|
||||
assert.match(zeilen[0], /node scripts\/build-legal-html\.mjs auftraege \[--check\]/)
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user