Abo-Bedingungen Fassung 6: Seite im neuen Stil (Entwurf)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7f4e84e0b1
commit
90b7f10f1a
2 files changed
+552
No files matched your search
@@ -39,3 +39,90 @@ test('Entwurf Fassung 6: Stellen in Reihenfolge, jeder Pflichtsatz im Wortlaut u
|
||||
}
|
||||
assert.doesNotMatch(md.slice(0, md.indexOf('## Was gleich bleibt')), /€/, 'die Pflichtsätze nennen keinen Preis')
|
||||
})
|
||||
|
||||
// Hilfen aus test/legalTexts.test.js (kein Import möglich)
|
||||
const strip = (html) => html.replace(/<[^>]+>/g, ' ').replace(/ /g, ' ').replace(/&/g, '&').replace(/\s+/g, ' ').trim()
|
||||
const between = (s, a, b) => {
|
||||
const i = s.indexOf(a)
|
||||
assert.ok(i >= 0, `Text fehlt: ${a}`)
|
||||
const j = s.indexOf(b, i)
|
||||
assert.ok(j > i, `Text fehlt: ${b}`)
|
||||
return s.slice(i, j)
|
||||
}
|
||||
|
||||
const V6 = 'public/neu/rechtliches/abo-bedingungen.html', V5A = 'public/vertragsunterlagen/v5/abo-bedingungen.html'
|
||||
const abschnitt = (html, von, bis) => {
|
||||
const a = von ? html.indexOf(`id="${von}"`) : 0
|
||||
assert.ok(a >= 0, `Anker #${von}`)
|
||||
const b = bis ? html.indexOf(`id="${bis}"`, a + 1) : html.length
|
||||
assert.ok(b > a, `Anker #${bis}`)
|
||||
return strip(html.slice(a, b))
|
||||
}
|
||||
test('v6-Seite: gleiche Anker wie Fassung 5, Absatzzahlen der geänderten Paragrafen', () => {
|
||||
const h = lies(V6), ids = (x) => [...x.matchAll(/ id="([^"]+)"/g)].map((m) => m[1])
|
||||
assert.deepEqual(ids(h), ids(lies(V5A)))
|
||||
const n = (von, bis) => [...h.slice(h.indexOf(`id="${von}"`), h.indexOf(`id="${bis}"`)).matchAll(/^ <li>([\s\S]*?)<\/li>$/gm)].length
|
||||
assert.deepEqual([['p1', 'p2'], ['p2', 'p3'], ['p4', 'p5'], ['p6', 'p7'], ['wechsel', 'zusatzspeicher'], ['p11', 'p12'], ['p14', 'p15']].map(([a, b]) => n(a, b)), [4, 6, 9, 6, 6, 4, 2])
|
||||
assert.equal((h.slice(h.indexOf('id="p3"'), h.indexOf('id="p4"')).match(/<h3>\(\d\)/g) ?? []).length, 8)
|
||||
})
|
||||
test('v6-Seite: Version, Stand, Entwurfsmarke und PDF-Link passen zu termsVersionNeu', () => {
|
||||
const m = /^(\d{4})-(\d{2})-(\d{2})-v6$/.exec(config.abo.termsVersionNeu); assert.ok(m)
|
||||
const h = lies(V6), [, y, mo, d] = m
|
||||
assert.ok(strip(h).includes(`Version 6 · Stand ${d}.${mo}.${y}`))
|
||||
assert.ok(h.includes(`content: "Version 6 · Stand ${d}.${mo}.${y}"`), 'Kopfzeile im Druck')
|
||||
assert.ok(!strip(h).includes('Version 5'))
|
||||
assert.equal(strip(h).includes('Entwurf – Prüfung durch Kanzlei offen'), ENTWURF)
|
||||
assert.ok(h.includes('href="/neu/rechtliches/WEBklar-Abo-Vertragsunterlagen.pdf"') && !h.includes('href="/WEBklar-Abo-Vertragsunterlagen.pdf"'))
|
||||
})
|
||||
test('v6-Seite: jede Pflichtstelle steht im Wortlaut an ihrem Platz', () => {
|
||||
const h = lies(V6)
|
||||
for (const p of PFLICHT) for (const t of p.texte) assert.ok(abschnitt(h, p.von, p.bis).includes(t), `${p.stelle}: ${t.slice(0, 50)}`)
|
||||
assert.ok(strip(h).includes(`Funktionen ${PFLICHT[0].texte[0]}`), 'Zeile „Funktionen“ in der Übersichtstabelle')
|
||||
})
|
||||
test('v6-Seite: Widerrufsbelehrung und Muster-Formular wortgleich mit Fassung 5, Abo-Preise unverändert, Funktionen ohne Preis', () => {
|
||||
const h = lies(V6), v5 = lies(V5A)
|
||||
const muster = (x) => between(abschnitt(x, 'widerruf', 'formular'), 'Widerrufsrecht Sie haben das Recht', 'Ende der Widerrufsbelehrung')
|
||||
assert.equal(muster(h), muster(v5))
|
||||
const formular = (x) => between(abschnitt(x, 'formular', 'widerruf-hinweise'), 'Muster-Widerrufsformular', '(*) Unzutreffendes streichen.')
|
||||
assert.equal(formular(h), formular(v5))
|
||||
for (const s of ['20,00 €', '50,00 €', '1,00 €', '100 € pro Stunde', 'zwölf Monaten']) assert.ok(strip(h).includes(s), s)
|
||||
assert.doesNotMatch(abschnitt(h, 'p3', 'p4').split('(8) Funktionen')[1], /€/)
|
||||
})
|
||||
test('v6-Seite: neues Aussehen, keine alten Farben, Druckkopf', () => {
|
||||
const style = lies(V6).match(/<style>([\s\S]*?)<\/style>/)[1]
|
||||
for (const t of ['#1a1f36', '#5e6678', '#ebeef1', '#d8dde3', '#f8f9fb', '#111', '-apple-system', 'color-scheme: light']) assert.ok(style.includes(t), t)
|
||||
assert.doesNotMatch(style, /#6d1fd1|#9d3dfe|gradient|prefers-color-scheme|backdrop-filter|#f7f6f3/)
|
||||
})
|
||||
|
||||
test('v6-Seite: Querverweise treffen (16 Paragrafen, mindestens 15 Verweise, Leistungsbeschreibung A/B Nr.)', () => {
|
||||
const html = lies(V6)
|
||||
const ids = [...html.matchAll(/ id="([^"]+)"/g)].map((m) => m[1])
|
||||
for (const [, ref] of html.matchAll(/href="#([^"]+)"/g)) assert.equal(ids.filter((i) => i === ref).length, 1, `#${ref}`)
|
||||
// Absätze je Paragraf: <li> der ol.abs auf oberster Ebene; bei § 3 die Überschriften "(M)"
|
||||
const absaetze = {}
|
||||
for (const [, n, body] of html.matchAll(/<h2 id="[^"]+">§ (\d+) [^<]*<\/h2>([\s\S]*?)(?=<h2 |<p class="anlage-kopf)/g)) {
|
||||
const h3 = [...body.matchAll(/<h3>\((\d+)\)/g)].map((x) => Number(x[1]))
|
||||
if (h3.length) { absaetze[n] = Math.max(...h3); continue }
|
||||
const start = body.indexOf('<ol class="abs">')
|
||||
let depth = 0, count = 0
|
||||
if (start >= 0) for (const [, close, tag] of body.slice(start).matchAll(/<(\/?)(ol|ul|li)\b[^>]*>/g)) {
|
||||
if (tag === 'li') { if (!close && depth === 1) count++; continue }
|
||||
depth += close ? -1 : 1
|
||||
if (depth === 0) break
|
||||
}
|
||||
absaetze[n] = count
|
||||
}
|
||||
assert.equal(Object.keys(absaetze).length, 16)
|
||||
assert.deepEqual([absaetze[3], absaetze[4], absaetze[6], absaetze[9], absaetze[11], absaetze[14], absaetze[15]], [8, 9, 6, 6, 4, 2, 2])
|
||||
const text = strip(html)
|
||||
const refs = [...text.matchAll(/§ (\d+) Abs\. (\d+)/g)]
|
||||
assert.ok(refs.length >= 15, `Querverweise: ${refs.length}`)
|
||||
for (const [ref, n, a] of refs) assert.ok(Number(a) <= (absaetze[n] ?? 0), `${ref}: § ${n} hat ${absaetze[n] ?? 0} Absätze`)
|
||||
// Leistungsbeschreibung A/B Nr. N: nummerierte <h3> der jeweiligen Beschreibung
|
||||
const nummern = (fromId, toId) => [...html.slice(html.indexOf(`id="${fromId}"`), html.indexOf(`id="${toId}"`)).matchAll(/<h3>(\d+)\. /g)].map((x) => Number(x[1]))
|
||||
const maxA = Math.max(...nummern('hosting', 'wartung')), maxB = Math.max(...nummern('wartung', 'widerruf'))
|
||||
for (const [ref, ab, nr] of text.matchAll(/Leistungsbeschreibung ([AB]) Nr\. (\d+)/g)) assert.ok(Number(nr) <= (ab === 'A' ? maxA : maxB), ref)
|
||||
})
|
||||
test('v6-Seite: Entwurf ist nicht indexierbar (N45)', () => {
|
||||
assert.equal(lies(V6).includes('<meta name="robots" content="noindex">'), ENTWURF)
|
||||
})
|
||||
Reference in new issue
Block a user