import { test } from 'node:test' import assert from 'node:assert/strict' import { existsSync, readFileSync } from 'node:fs' import { installFakeDom } from './support/fakeDom.js' import { h } from '../public/auftrag-dom.js' import { initProjektWiderruf } from '../public/auftrag-widerruf.js' // Kündigen und Widerrufen in du (E15, C14): die neuen Seiten sprechen du, die alten Seiten und Skripte bleiben wie sie sind const lese = (p) => readFileSync(new URL(`../public/${p}`, import.meta.url), 'utf8') const sichtbar = (html) => html.replace(/||/g, ' ').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ') const SIE = /\b(Sie|Ihr|Ihre|Ihren|Ihrem|Ihrer|Ihres|Ihnen)\b/ const mit = (fn) => async () => { const fx = installFakeDom() try { await fn(fx) } finally { fx.restore() } } test('Kündigen und Widerrufen (neu): kein Sie im sichtbaren Text, Attribut gesetzt; die alten Seiten bleiben', () => { for (const n of ['kuendigen', 'widerrufen']) { const neu = lese(`neu/oeffentlich/${n}.html`) assert.doesNotMatch(sichtbar(neu), SIE, n) assert.match(neu, /
]*data-anrede="du"/, n) if (existsSync(new URL(`../public/${n}.html`, import.meta.url))) { // P10 löscht die alten Seiten, dann entfällt dieser Teil assert.doesNotMatch(lese(`${n}.html`), /data-anrede/, `${n}: alte Seite unverändert`) assert.match(sichtbar(lese(`${n}.html`)), SIE, `${n}: alte Seite bleibt Sie`) } } }) test('Text-Pins der du-Fassung', () => { const k = sichtbar(lese('neu/oeffentlich/kuendigen.html')) const w = sichtbar(lese('neu/oeffentlich/widerrufen.html')) for (const s of ['Zahl der Pakete, die du kündigst (je 5 GB; leer = alle gebuchten Pakete)', 'Das geht auch, wenn dein Abo schon gekündigt ist.', 'Deine Kündigung ist eingegangen', 'Eine Eingangsbestätigung mit diesen Angaben haben wir an deine E-Mail-Adresse geschickt. Den Zeitpunkt, zu dem die Kündigung wirkt, bestätigen wir dir per E-Mail.', 'Jetzt kündigen']) assert.ok(k.includes(s), s) for (const s of ['Dein Widerruf ist bei uns eingegangen', 'Eine Eingangsbestätigung mit diesen Angaben haben wir an deine E-Mail-Adresse geschickt.', 'Widerruf bestätigen']) assert.ok(w.includes(s), s) }) test('Die neuen Seiten ändern nichts außer dem Text: Felder, Werte und Skripte gleich den alten', () => { for (const n of ['kuendigen', 'widerrufen']) { if (!existsSync(new URL(`../public/${n}.html`, import.meta.url))) continue const attribute = (html) => [...html.slice(html.search(/ `${m[1]}=${m[2] ?? ''}`).sort() assert.deepEqual(attribute(lese(`neu/oeffentlich/${n}.html`)), attribute(lese(`${n}.html`)), n) const skripte = (html) => [...html.matchAll(/]*>[\s\S]*?<\/script>/g)].map((m) => m[0].replace(/\s+/g, ' ')) assert.deepEqual(skripte(lese(`neu/oeffentlich/${n}.html`)), skripte(lese(`${n}.html`)), n) } }) test('legalFormPaketeFehler: leer und 1 bis max passen, alles andere bekommt den du-Satz', () => { const { legalFormPaketeFehler } = new Function(`${lese('legal-form.js')}\nreturn { legalFormPaketeFehler }`)() for (const ok of ['', ' ', '1', '10']) assert.equal(legalFormPaketeFehler(ok, 10), '', JSON.stringify(ok)) for (const schlecht of ['0', '11', '-1', '2,5', 'abc']) assert.equal(legalFormPaketeFehler(schlecht, 10), 'Bitte gib 1 bis 10 Pakete an oder lass das Feld leer (alle Pakete).', schlecht) assert.equal(legalFormPaketeFehler(undefined, 10), '') assert.equal(legalFormPaketeFehler('5', 3), 'Bitte gib 1 bis 3 Pakete an oder lass das Feld leer (alle Pakete).') assert.match(lese('legal-form.js'), /getAttribute\('data-anrede'\) === 'du'/) }) /** Kündigungsformular mit Zusatzspeicher und den Skripten legal-form.js (klassisch) im Fake-DOM; antwortet der Server, steht es in fx.fetchLog. */ async function legalForm(fx, attribute, packages) { const speicher = h('input', { type: 'radio', name: 'contract', value: 'zusatzspeicher', checked: true }) const abo = h('input', { type: 'radio', name: 'contract', value: 'abo' }) const pakete = h('input', { id: 'packages', name: 'packages', type: 'number', min: '1', max: '10' }) const name = h('input', { id: 'name', name: 'name' }) const fehler = h('p', { id: 'form-error', class: 'hidden' }) const knopf = h('button', { type: 'submit' }, 'Jetzt kündigen') const form = h('form', { id: 'decl-form', ...attribute }, speicher, abo, h('div', { id: 'packages-wrap' }, pakete), name, fehler, knopf) // fakeDom kennt form.elements nicht; das Skript liest darüber Vertrag, Paketzahl und Name Object.defineProperty(form, 'elements', { value: { contract: { get value() { return speicher.checked ? 'zusatzspeicher' : 'abo' } }, packages: pakete, name } }) pakete.max = '10' fx.document.body.append(form, h('div', { id: 'done', class: 'hidden' }), h('span', { id: 'done-at' }), h('dl', { id: 'done-list' })) new Function(`${lese('legal-form.js')}\ninitLegalForm({ endpoint: '/api/abo/public/cancel', fields: ['contract', 'packages', 'name'] })`)() await fx.setze(pakete, packages) await fx.setze(name, 'Anna') await fx.klick(knopf) await fx.ruhe() return { fehler, knopf } } test('legal-form.js: mit data-anrede="du" prüft die Seite die Paketzahl vor dem Absenden, ohne das Attribut unverändert', mit(async (fx) => { fx.antworten.push(() => ({ status: 200, body: {} })) // GET /api/auth/me const du = await legalForm(fx, { 'data-anrede': 'du' }, '11') assert.equal(du.fehler.textContent, 'Bitte gib 1 bis 10 Pakete an oder lass das Feld leer (alle Pakete).') assert.equal(du.fehler.classList.contains('hidden'), false) assert.equal(du.knopf.hasAttribute('disabled'), false) assert.ok(!fx.fetchLog.some((a) => a.url === '/api/abo/public/cancel'), 'es wurde nichts gesendet') })) test('legal-form.js: ohne das Attribut geht die Eingabe wie heute an den Server', mit(async (fx) => { fx.antworten.push(() => ({ status: 200, body: {} }), () => ({ status: 400, body: { error: 'Bitte geben Sie 1 bis 10 Pakete an oder lassen Sie das Feld leer (alle Pakete).' } })) const alt = await legalForm(fx, {}, '11') assert.ok(fx.fetchLog.some((a) => a.url === '/api/abo/public/cancel'), 'alter Weg: der Server prüft') assert.equal(alt.fehler.textContent, 'Bitte geben Sie 1 bis 10 Pakete an oder lassen Sie das Feld leer (alle Pakete).') })) test('auftrag-widerruf.js: du-Meldung bei data-anrede, sonst der alte Satz', mit(async (fx) => { const DU = 'Die Nummer sieht anders aus als AB-2026-0001. Bitte prüfe die Nummer oder lass das Feld leer.' const ALT = 'Die Nummer sieht anders aus als AB-2026-0001. Bitte prüfen Sie die Nummer oder lassen Sie das Feld leer.' for (const [attribute, erwartet] of [[{ 'data-anrede': 'du' }, DU], [{}, ALT]]) { const nummer = h('input', { id: 'abNummer', name: 'abNummer' }) const fehler = h('p', { id: 'form-error', class: 'error hidden' }) const knopf = h('button', { type: 'submit' }, 'Widerruf bestätigen') const form = h('form', { id: 'decl-form', ...attribute }, h('label', {}, h('input', { type: 'radio', name: 'order', value: 'projekt', checked: true })), h('div', { id: 'ab-nummer-wrap' }, nummer), fehler, knopf) fx.document.body.append(form) initProjektWiderruf(form) await fx.setze(nummer, 'irgendwas') await fx.klick(knopf) assert.equal(fehler.textContent, erwartet) assert.equal(fehler.classList.contains('hidden'), false) } }))