Gästetexte des Terminplaners: Entwurf-Kennzeichnung entfernt
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c1266fcce7
commit
495d5ad9ca
11 files changed
+79
-56
No files matched your search
@@ -1217,12 +1217,15 @@ test('Anleitung ohne gespeicherte Einstellung: nur der Einbau-Code und die Warnu
|
||||
})
|
||||
|
||||
test('Anleitung, Drift: Ticket auf Version 1, Einstellung jetzt Version 3 => Warnung am Ende von warnungen, ticketVersion und ticketWoid', async () => {
|
||||
const k = legeAnleitung({ version: 3, ticketDetails: TICKET_MIT_STAND(1) })
|
||||
// eine andere Warnung muss davor stehen (ohne Entwurfs-Banner sonst keine): fehlende Impressum-URL
|
||||
const einstellung = d8Einstellung()
|
||||
einstellung.betrieb.impressumUrl = ''
|
||||
const k = legeAnleitung({ einstellung, version: 3, ticketDetails: TICKET_MIT_STAND(1) })
|
||||
const res = await anleitung(k)
|
||||
assert.equal(res.status, 200, JSON.stringify(res.body))
|
||||
assert.equal(warnungDrift(1, 3), 'Einstellungen seit der Bestellung geändert (Ticket Version 1, jetzt 3).')
|
||||
assert.equal(res.body.warnungen.at(-1), warnungDrift(1, 3))
|
||||
assert.ok(res.body.warnungen.length > 1, 'sie steht hinter den uebrigen Warnungen')
|
||||
assert.ok(res.body.warnungen.length > 1 && /Impressum/.test(res.body.warnungen[0]), `sie steht hinter den uebrigen Warnungen: ${res.body.warnungen.join(' | ')}`)
|
||||
assert.equal(res.body.warnungen.filter((w) => w.startsWith('Einstellungen seit der Bestellung')).length, 1)
|
||||
assert.equal(res.body.ticketVersion, 1)
|
||||
assert.equal(res.body.ticketWoid, ticketDoc(k.ticketId).woid)
|
||||
|
||||
@@ -219,6 +219,11 @@ test('cssVorschlag: der Hinweis nennt den Kontrast gegen Weiss, wenn der Akzent
|
||||
const NOW = new Date('2026-10-05T10:00:00Z') // Montag, 12:00 Berliner Zeit
|
||||
const SPAETER = new Date('2026-10-06T10:00:00Z') // einen Tag spaeter: nur die "Erzeugt am"-Zeile aendert sich
|
||||
const GEHEIM = 'XYZ-GEHEIM'
|
||||
// Endregel (Leistungsbeschreibung Nr. 4 Abs. 9): wer den Datenschutz-Absatz einfügt; Checkliste und Schritt 4 der Kurzfassung nennen sie
|
||||
const DATENSCHUTZ_ENDREGEL =
|
||||
'liegt die Datenschutzerklärung in der von WEBklar betreuten Website, fügen wir den Absatz beim Einbau ein, das gehört zur Einrichtung und wird nicht auf das Stundenkontingent angerechnet; '
|
||||
+ 'sonst den Absatz als Textbaustein per Chat an den Kunden senden, der ihn selbst einfügt'
|
||||
|
||||
const BETRIEB = {
|
||||
name: 'Salon Muster',
|
||||
email: 'inhaber-intern@salon-muster.example',
|
||||
@@ -880,6 +885,36 @@ test('D3b Banner: ENTWURF_BANNER genau dann, wenn entwurf, und zwar als erste Wa
|
||||
assert.equal(erzeuge({ entwurf: true }).warnungen.filter((w) => w === ENTWURF_BANNER).length, 1)
|
||||
})
|
||||
|
||||
test('einbau_ohne_banner: im Standardaufruf steht kein ENTWURF-Banner in Warnungen, Volltext und Kurzfassung; mit entwurf: true bleibt er', () => {
|
||||
assert.equal(RECHTSTEXTE_ENTWURF, false)
|
||||
const r = erzeuge()
|
||||
assert.deepEqual(r.warnungen, [])
|
||||
assert.ok(!r.warnungen.includes(ENTWURF_BANNER))
|
||||
assert.ok(!r.text.includes('ENTWURF'), 'Volltext')
|
||||
assert.ok(!r.kurz.includes('ENTWURF'), 'Kurzfassung (Ticket)')
|
||||
for (const a of r.abschnitte) assert.ok(!a.text.includes('ENTWURF'), `Abschnitt ${a.id}`)
|
||||
// die D3b-Tests mit ausdrücklichem entwurf: true bleiben gültig: der Banner ist weiter eine eigene Warnung
|
||||
const mit = erzeuge({ entwurf: true })
|
||||
assert.deepEqual(mit.warnungen, [ENTWURF_BANNER])
|
||||
assert.ok(mit.kurz.includes(ENTWURF_BANNER))
|
||||
})
|
||||
|
||||
test('checkliste_datenschutz_endregel: die Checkliste nennt die Endregel der Leistungsbeschreibung, die Zwischenregel ist weg', () => {
|
||||
const text = abschnitt(erzeuge(), 'checkliste').text
|
||||
const punkt = zeilen(text).find((z) => z.includes('Datenschutz-Absatz „Online-Terminbuchung“ steht in der Datenschutzerklärung'))
|
||||
assert.ok(punkt, text)
|
||||
assert.ok(punkt.includes(`(Fassung Sie; ${DATENSCHUTZ_ENDREGEL}).`), punkt)
|
||||
for (const teil of ['von WEBklar betreuten Website', 'fügen wir den Absatz beim Einbau ein', 'nicht auf das Stundenkontingent angerechnet', 'Textbaustein per Chat an den Kunden']) {
|
||||
assert.ok(punkt.includes(teil), `Endregel: ${teil}`)
|
||||
}
|
||||
assert.ok(!text.includes('liegt die Datenschutzerklärung nicht in unserem Repo'), 'Zwischenregel aus D3b ist weg')
|
||||
assert.ok(!erzeuge().kurz.includes('nicht in unserem Repo'), 'auch nicht in der Kurzfassung')
|
||||
assert.ok(!erzeuge().text.includes('nicht in unserem Repo'), 'und nicht im Volltext')
|
||||
const du = fixtureEinstellung()
|
||||
du.anrede = 'du'
|
||||
assert.ok(abschnitt(erzeuge({ einstellung: du }), 'checkliste').text.includes(`(Fassung Du; ${DATENSCHUTZ_ENDREGEL}).`))
|
||||
})
|
||||
|
||||
test('D3b Warnungen: fehlende Telefonnummer, und Anschrift nur, wenn Gaeste ein Widerrufsrecht haben', () => {
|
||||
const ohneTelefon = fixtureEinstellung()
|
||||
ohneTelefon.betrieb.telefon = ''
|
||||
@@ -996,7 +1031,7 @@ test('D3b Checkliste: immer-Punkte (Freischalten, Probebuchung, stornieren, Kont
|
||||
'Datenschutz-Absatz', 'Impressum', 'Kontrast', '390 px', 'Tastatur',
|
||||
]) assert.ok(text.includes(erwartet), `${erwartet}\n---\n${text}`)
|
||||
assert.ok(text.includes(BETRIEB.impressumUrl) && text.includes(BETRIEB.datenschutzUrl), 'die URLs stehen zum Pruefen im Punkt')
|
||||
assert.ok(text.includes('liegt die Datenschutzerklärung nicht in unserem Repo: Absatz per Chat an den Kunden senden'), 'Zwischenregel bis Block F')
|
||||
assert.ok(text.includes(DATENSCHUTZ_ENDREGEL), 'Endregel der Leistungsbeschreibung (Nr. 4 Abs. 9)')
|
||||
assert.ok(text.includes(`${config.womsBaseUrl}/customers/c1?tab=funktionen`), 'Link ins WOMS-Kundenpanel')
|
||||
// die Reihenfolge: erst Einbau, dann Freischalten, dann Probebuchung, dann die Pruefungen am sichtbaren Widget
|
||||
const stelle = (teil) => z.findIndex((x) => x.includes(teil))
|
||||
@@ -1246,7 +1281,7 @@ test('D4 kurz Schritt 4 und 6: Datenschutz-Fassung nach Anrede, Preis nur bei be
|
||||
du.anrede = 'du'
|
||||
assert.match(schritt({}, 4), /Sie-Form/)
|
||||
assert.match(schritt({ einstellung: du }, 4), /Du-Form/)
|
||||
assert.ok(schritt({}, 4).includes('Absatz per Chat an den Kunden senden'), 'Zwischenregel aus der Checkliste (F10b2 ersetzt sie an einer Stelle)')
|
||||
assert.ok(schritt({}, 4).includes(DATENSCHUTZ_ENDREGEL), 'dieselbe Endregel wie in der Checkliste')
|
||||
assert.match(schritt({ feature: PAKET_FEATURE }, 6), /Nicht auf das Stundenkontingent anrechnen \(Einrichtung bezahlt, 149 €\)/)
|
||||
assert.ok(schritt({}, 6).includes('Nicht auf das Stundenkontingent anrechnen'))
|
||||
assert.ok(!schritt({}, 6).includes('bezahlt'), 'ohne Kauf keine Behauptung, die Einrichtung sei bezahlt')
|
||||
|
||||
@@ -235,7 +235,7 @@ test('Einbauanleitung: Snippet, Variablen, drei Stile, Datenschutz-Absatz, Abnah
|
||||
'data-modus', 'data-api', 'data-knopf', 'data-leistung',
|
||||
'--wkt-akzent', '--wkt-akzent-text', '--wkt-hintergrund', '--wkt-text', '--wkt-rand', '--wkt-radius', '--wkt-schrift', '--wkt-schrift-titel',
|
||||
'@layer wkt', 'wkt-style',
|
||||
'docs/rechtliches/termine-datenschutz-absatz.md', 'ENTWURF',
|
||||
'docs/rechtliches/termine-datenschutz-absatz.md', 'Fassung',
|
||||
'Abnahme', '/api/public/termine/v1', '/config', '/tage', '/zeiten', '/buchungen', '/widerruf', '/kalender/',
|
||||
'termine-demo.html', 'termine-widget.ui.mjs',
|
||||
]) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { config } from '../server/config.js'
|
||||
import {
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
GESUNDHEIT_HINWEIS,
|
||||
} from '../server/services/termine/rechtstexte.js'
|
||||
|
||||
// ENTWURF – nur Pilot, vor Einsatz bei fremden Betrieben prüfen.
|
||||
// Die Belehrung wird gegen den Wortlaut der Anlagen 1 und 2 zu Art. 246a EGBGB geprueft (docs/rechtliches/quellen/,
|
||||
// abgerufen 2026-10-02): jeder Absatz des Musters muss unveraendert im Ergebnis stehen, nur die Luecken sind gefuellt.
|
||||
|
||||
@@ -246,8 +245,8 @@ test('Formular-Hinweis: Sie ist die Vorgabe, Aufbewahrung 90 Tage ohne gültigen
|
||||
|
||||
// ---------------------------------------------------------------- Datenschutz-Absatz „Online-Terminbuchung“ (Quelle der Einbauanleitung und der Doku)
|
||||
|
||||
test('Rechtstexte sind Entwurf, bis Block F die ENTWURF-Köpfe und diese Konstante zusammen entfernt', () => {
|
||||
assert.equal(RECHTSTEXTE_ENTWURF, true)
|
||||
test('D1 (f): die Rechtstexte gelten nicht mehr als Entwurf (RECHTSTEXTE_ENTWURF === false)', () => {
|
||||
assert.equal(RECHTSTEXTE_ENTWURF, false)
|
||||
})
|
||||
|
||||
const DS_SCHALTER = ['telefonFeld', 'freitext', 'erinnerung', 'widerruf', 'telefon']
|
||||
@@ -508,13 +507,29 @@ const DOKUMENTE = [
|
||||
'docs/rechtliches/termine-widerrufsbelehrung-muster.md',
|
||||
'docs/rechtliches/termine-datenschutz-absatz.md',
|
||||
'docs/rechtliches/termine-hinweise.md',
|
||||
'docs/rechtliches/termine-datenschutz-6c-entwurf.md',
|
||||
'docs/rechtliches/termine-datenschutz-6c.md',
|
||||
]
|
||||
|
||||
test('Jedes Rechtstext-Dokument beginnt mit dem Entwurfs-Kopf', () => {
|
||||
// Das Wort ENTWURF als Kennzeichnung; ENTWURF_BANNER und RECHTSTEXTE_ENTWURF (Bezeichner im Code) zählen nicht
|
||||
const ENTWURF_WORT = /ENTWURF(?![_A-Z])/
|
||||
|
||||
test('kein_entwurf_mehr: kein ENTWURF-Kopf in den vier Dokumenten, keiner in den Texten von rechtstexte.js und in der Einbau-Doku; die Konstante steht auf false', () => {
|
||||
for (const rel of DOKUMENTE) {
|
||||
assert.equal(lies(rel).split('\n')[0].trim(), KOPF, rel)
|
||||
const md = lies(rel)
|
||||
assert.ok(!ENTWURF_WORT.test(md), `${rel}: ENTWURF steht noch im Dokument`)
|
||||
assert.notEqual(md.split('\n')[0].trim(), KOPF, `${rel}: der Entwurfs-Kopf steht noch in der ersten Zeile`)
|
||||
assert.ok(md.startsWith('# '), `${rel}: das Dokument beginnt mit der Überschrift`)
|
||||
}
|
||||
assert.ok(!existsSync(new URL('../docs/rechtliches/termine-datenschutz-6c-entwurf.md', import.meta.url)), 'die 6c-Datei heißt nicht mehr -entwurf')
|
||||
const quelle = lies('server/services/termine/rechtstexte.js')
|
||||
assert.ok(!ENTWURF_WORT.test(quelle.replace(/RECHTSTEXTE_ENTWURF/g, '')), 'rechtstexte.js: ENTWURF in Kopf oder Kommentar (nur der Bezeichner RECHTSTEXTE_ENTWURF ist erlaubt)')
|
||||
assert.ok(/export const RECHTSTEXTE_ENTWURF = false\b/.test(quelle))
|
||||
assert.equal(RECHTSTEXTE_ENTWURF, false)
|
||||
const einbau = lies('docs/funktionen/termine-einbau.md')
|
||||
assert.ok(!ENTWURF_WORT.test(einbau), 'termine-einbau.md: ENTWURF')
|
||||
assert.ok(!/nur beim Pilotkunden einsetzen|Solange der Kopf dort steht/.test(einbau), 'termine-einbau.md Abschnitt 6 ohne die Pilot-Einschränkung')
|
||||
// die Gäste-Dokumente haben keine offene Rechtsprüfungs-Liste mehr (die Fragen stehen im Kanzlei-Paket)
|
||||
for (const rel of DOKUMENTE.slice(0, 3)) assert.ok(!/^## Für die Rechtsprüfung/m.test(lies(rel)), `${rel}: Liste „Für die Rechtsprüfung offen“ steht noch`)
|
||||
})
|
||||
|
||||
test('Hinweise-Dokument: Formular-Hinweis (Sie/Du) und alle Zustimmungstexte stehen wörtlich darin', () => {
|
||||
@@ -553,7 +568,7 @@ test('Datenschutz-Absatz: Löschfristen der Vorgaben, Sie- und Du-Fassung, Auftr
|
||||
})
|
||||
|
||||
test('6c-Entwurf: nur als Entwurfsdatei, public/datenschutz.html bleibt unverändert (ohne Abschnitt 6c)', () => {
|
||||
const md = lies('docs/rechtliches/termine-datenschutz-6c-entwurf.md')
|
||||
const md = lies('docs/rechtliches/termine-datenschutz-6c.md')
|
||||
assert.ok(md.includes('6c. Online-Terminbuchung'))
|
||||
assert.ok(md.includes('Auftragsverarbeiter'))
|
||||
assert.ok(/nicht (in|auf) .*(einbauen|veröffentlich)|nicht eingebaut|nicht veröffentlicht/i.test(md))
|
||||
|
||||
Reference in new issue
Block a user