Files
Webklar-Kundenbereich/test/legalV6Build.test.js
T
ClaudeandClaude Opus 5.5 64f1a3b26b Rechtsrunde Fassung 6 freigegeben (28.10.2026): Datum, Entwurfsmarken, R8b
Der Inhaber hat am 28.10.2026 alle Vorschläge R1 bis R18 angenommen (R10 = Option A)
und zusätzlich R8b entschieden: Webshop und Buchungskalender kehren nach einem neuen
Wartungs-Abo in der Frist automatisch zurück, wie der Server es schon tut.

- Fassungen: Abo 2026-10-28-v6, Funktionsunterlagen 2026-10-28 (Seiten neu gebaut)
- Entwurfsmarken und meta robots noindex entfernt, ENTWURF_V6 = false
- WORTLAUT_STATUS = 'freigegeben'
- Nr. 10 Abs. 5 Webshop/Buchungskalender ohne "auf Wunsch in Textform" (R8b)
- Entscheidungsliste: Abschnitt "Freigabe"; Prüfpunkte D1 bis D8 auf die aktuelle
  Datenschutzerklärung (Fassung 22.10.2026, eine Zeile tiefer) umgestellt
- Tests auf das neue Datum und den Status freigegeben gezogen

Live ändert sich für Kunden nichts: die Seiten liegen unter /neu/ und gelten nur in der
neuen Ansicht (Pilot). Umschalten (P10) folgt erst auf ausdrückliches "umschalten".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 14:46:53 +00:00

77 lines
6.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { createHash } from 'node:crypto'
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { spawnSync } from 'node:child_process'
import { useTestEnv } from './support/testEnv.js'
// Generator der Rechtstexte: neuer Stil und Satz funktionen6 (Fassung 6, Entwurf). Ergänzt test/buildLegalHtml.test.js.
useTestEnv({ appwriteUrl: 'http://127.0.0.1:1', stripeUrl: 'http://127.0.0.1:1' })
const { render, lauf, funktionen6Dokumente } = await import('../scripts/build-legal-html.mjs')
const ROOT = fileURLToPath(new URL('..', import.meta.url))
const F = '2026-10-28'
const SLUGS = ['leistungsbeschreibung-news', 'leistungsbeschreibung-terminplaner', 'avv-terminplaner', 'leistungsbeschreibung-webshop', 'avv-webshop', 'leistungsbeschreibung-unterkunft', 'avv-unterkunft', 'leistungsbeschreibung-zeiten', 'leistungsbeschreibung-website-analyse', 'avv-website-analyse']
test('funktionen6Dokumente: zehn Seiten unter public/neu/rechtliches, Quellen *-v6.md, Navigation im Vorschaupfad', () => {
const d = funktionen6Dokumente()
assert.deepEqual(d.map((x) => x.doc), ['news-lb', 'termine-lb', 'termine-avv', 'shop-lb', 'shop-avv', 'unterkunft-lb', 'unterkunft-avv', 'zeiten-lb', 'analyse-lb', 'analyse-avv'])
assert.deepEqual(d.map((x) => [x.md, x.html]), SLUGS.map((s) => [`docs/rechtliches/${s}-v6.md`, `public/neu/rechtliches/${s}-${F}.html`]))
for (const x of d) {
assert.deepEqual([x.meta.stil, x.meta.fassung], ['neu', F])
const hrefs = x.meta.nav.map((l) => l.href)
assert.ok(hrefs.includes('/neu/rechtliches/abo-bedingungen.html') && hrefs.includes(`/${x.html.slice('public/'.length)}`), x.doc)
assert.ok(hrefs.every((h) => !h.startsWith('/rechtliches/')), 'keine Verweise auf eingefrorene Seiten')
}
})
test('render mit stil neu: weiß und schwarz ohne Lila und Verlauf; ohne stil bleibt die alte Hülle', () => {
const meta = { fassung: F, titel: 'Prüfdokument', seitenkopf: 'Prüfdokument', nav: [{ href: '/dashboard.html', text: 'Portal' }] }
const md = `# Titel\n\n**Fassung ${F}** · Entwurf – Prüfung durch Kanzlei offen\n\n## 1. Geltung\n\n(1) Text.\n`
const neu = render(md, { ...meta, stil: 'neu' }), alt = render(md, meta)
for (const t of ['#1a1f36', '#ebeef1', '#f8f9fb', '#111', '-apple-system']) assert.ok(neu.includes(t), t)
assert.doesNotMatch(neu, /#6d1fd1|gradient|#f7f6f3/)
assert.ok(alt.includes('#6d1fd1') && alt.includes('#f7f6f3'))
assert.equal(neu.replace(/<style>[\s\S]*?<\/style>/, ''), alt.replace(/<style>[\s\S]*?<\/style>/, ''), 'nur die Hülle unterscheidet sich')
assert.throws(() => render(md, { ...meta, stil: 'dunkel' }), /stil/)
})
test('lauf funktionen6: schreibt in neuen Ordner, --check, --nur, falscher Aufruf', () => {
const dir = mkdtempSync(join(tmpdir(), 'legalv6-'))
try {
mkdirSync(join(dir, 'docs/rechtliches'), { recursive: true })
for (const s of SLUGS) writeFileSync(join(dir, `docs/rechtliches/${s}-v6.md`), `# T\n\n**Fassung ${F}** · Entwurf\n\nText.\n`)
const z = []; const ruf = (...a) => lauf(a, { root: dir, log: (x) => z.push(x), fehler: (x) => z.push(x) })
assert.equal(ruf('funktionen6', '--check'), 1)
assert.equal(ruf('funktionen6', '--nur=avv-webshop,news-lb'), 0)
assert.deepEqual(z.filter((x) => x.startsWith('->')), [`-> public/neu/rechtliches/leistungsbeschreibung-news-${F}.html`, `-> public/neu/rechtliches/avv-webshop-${F}.html`])
assert.equal(ruf('funktionen6'), 0); assert.equal(ruf('funktionen6', '--check'), 0)
writeFileSync(join(dir, 'docs/rechtliches/avv-webshop-v6.md'), `# T\n\n**Fassung ${F}** · Entwurf\n\nGeändert.\n`)
z.length = 0; assert.equal(ruf('funktionen6', '--check'), 1)
assert.deepEqual(z.filter((x) => x.startsWith('ABWEICHT')), [`ABWEICHT public/neu/rechtliches/avv-webshop-${F}.html`])
assert.equal(ruf('funktionen6', '--schreiben'), 2); assert.equal(ruf('shop', '--nur=x'), 2); assert.equal(ruf('funktionen6', '--nur=gibt-es-nicht'), 2)
} finally { rmSync(dir, { recursive: true, force: true }) }
})
test('build-legal-pdfs.sh: Modi abo6 und funktionen6, all und erste Usage-Zeile unverändert, nie parallel', () => {
const sh = readFileSync(join(ROOT, 'scripts/build-legal-pdfs.sh'), 'utf8')
assert.match(sh, /^abo6\)$/m); assert.match(sh, /^funktionen6\)$/m)
const abo6 = sh.slice(sh.indexOf('\nabo6)'), sh.indexOf('\nfunktionen6)'))
assert.ok(abo6.includes('public/neu/rechtliches/abo-bedingungen.html') && abo6.includes('public/neu/rechtliches/WEBklar-Abo-Vertragsunterlagen.pdf') && abo6.includes('.sha256'))
for (const s of SLUGS) assert.ok(sh.includes(s), s)
assert.ok(sh.includes('FUNKTIONEN_FASSUNG_V6') && sh.includes('zuerst node scripts/build-legal-html.mjs funktionen6'))
const all = sh.slice(sh.indexOf('\nall)'), sh.indexOf('\n*)'))
assert.ok(!all.includes('neu/rechtliches') && all.includes(' pdf public/abo-bedingungen.html public/WEBklar-Abo-Vertragsunterlagen.pdf\n'), 'all druckt die Entwürfe nicht')
assert.ok(sh.includes('Usage: $0 [all|analyse|termine|shop|unterkunft|datenschutz]') && sh.includes('abo6|funktionen6'))
assert.match(sh, /^ {2}timeout 120 "\$CHROME" /m)
assert.ok(!/ &$|^\s*wait\b|xargs -P/m.test(sh))
assert.equal(spawnSync('bash', ['-n', join(ROOT, 'scripts/build-legal-pdfs.sh')], { encoding: 'utf8' }).status, 0)
})
test('PDFs der Fassung 6: erst nach der Freigabe vorhanden; wenn vorhanden, PDF, Prüfsumme der Seite daneben', { skip: !existsSync(join(ROOT, 'public/neu/rechtliches/WEBklar-Abo-Vertragsunterlagen.pdf')) && 'PDFs entstehen erst nach der Freigabe (Task 11, Ablauf)' }, () => {
for (const p of ['abo-bedingungen.html', ...SLUGS.map((s) => `${s}-2026-10-28.html`)]) {
const pdf = join(ROOT, 'public/neu/rechtliches', p === 'abo-bedingungen.html' ? 'WEBklar-Abo-Vertragsunterlagen.pdf' : p.replace('.html', '.pdf'))
assert.equal(readFileSync(pdf).subarray(0, 5).toString(), '%PDF-', pdf)
assert.equal(readFileSync(`${pdf}.sha256`, 'utf8').trim(), createHash('sha256').update(readFileSync(join(ROOT, 'public/neu/rechtliches', p))).digest('hex'), p)
}
})