import { test } from 'node:test' import assert from 'node:assert/strict' import { h } from '../public/termine-dom.js' import { initTermine } from '../public/termine.js' import { bestellbarerStand } from './support/fragebogenStand.js' import { einschaltenAttrappe, hoereAufKn, mitDom, routen } from './support/modulAbo.js' // Terminplaner im Abo-Modell (P4 Task 3): der letzte Schritt des Fragebogens heißt „Prüfen und einschalten“ und mountet den Ablauf von P3 // statt der Bestellseite; keine Haken, kein Preisblock, kein Checkout. Ohne modell === 'abo' bleibt die Bestellseite wie heute. const STAND = bestellbarerStand({ now: new Date('2026-10-16T08:00:00.000Z') }) const daten = (extra) => ({ state: 'off', einstellung: STAND.einstellung, fragebogen: STAND.fragebogen, version: 1, branchen: [], kundenansicht: false, abo: { ok: true }, offen: [], bestellung: null, ...extra }) const liste = (modell, state = 'off', feature = {}) => ({ body: { ...(modell === undefined ? {} : { modell }), projects: [{ projectId: 'p1', projectName: 'Salon', features: { termine: { state, siteKey: 'abcdefghijklmnop', ...feature } } }], termine: { verfuegbar: true, bestellbar: true, grund: null, preis: '149 €', einrichtung: 'meist in 1–2 Wochen', hinweis: null }, }, }) const BESTELLUNG = { preis: '149 €', preisHinweis: 'einmalig, ohne Umsatzsteuer gemäß § 19 UStG', einrichtung: '', belehrung: null, dokumente: [], consent: { version: 'v-test', knopf: 'Zahlungspflichtig bestellen · 149 €', items: [{ id: 'beginn', text: 'Ich verlange den sofortigen Beginn.', haken: true }] }, } const EINSTELLUNGEN = 'GET /api/termine/einstellungen?projectId=p1' async function oeffne(fx, tabelle, optionen) { routen(fx, tabelle) const wurzel = h('div') document.body.append(wurzel) await initTermine(wurzel, { projectId: 'p1', ...optionen }) await fx.ruhe() return wurzel } test('Abo, alles bestätigt: letzter Schritt E1 mountet den Ablauf, ohne Haken, Preisblock und Checkout; keine Reiter', mitDom(async (fx) => { const a = einschaltenAttrappe() const k = hoereAufKn(fx) const wurzel = await oeffne(fx, { 'GET /api/funktionen': liste('abo'), [EINSTELLUNGEN]: { body: daten() } }, { einschalten: a.fn }) assert.equal(fx.text(fx.finde(wurzel, '.tm-fb-bestellen h3')), 'Prüfen und einschalten') assert.match(fx.text(fx.finde(wurzel, '.tm-schritt[aria-current="step"]')), /Prüfen und einschalten/) assert.match(fx.text(fx.finde(wurzel, '.tm-schritt-stand')), /Prüfen und einschalten/) assert.deepEqual(a.aufrufe.map((x) => [x.key, x.projectId, x.viewAs]), [['termine', 'p1', false]]) assert.equal(fx.alle(wurzel, 'input[type="checkbox"]').length, 0, 'auch kein Haken „gesundheit“') for (const sel of ['[data-aktion="bestellen"]', '.tm-bestellen-preis', '.tm-rechtstext', '.tm-bestellen-zustimmung']) assert.ok(fx.finde(wurzel, sel) === null, sel) assert.equal(fx.fetchLog.filter((x) => x.method === 'POST').length, 0) assert.deepEqual(k.reiter.at(-1).eintraege, []) })) test('Abo, offene Punkte: kein Ablauf, Hinweis E2, „Zum Schritt“ führt zurück', mitDom(async (fx) => { const a = einschaltenAttrappe() const offen = [{ schritt: 'zeiten', text: 'Bitte wähle das Bundesland deines Betriebs.' }] const wurzel = await oeffne(fx, { 'GET /api/funktionen': liste('abo'), [EINSTELLUNGEN]: { body: daten({ offen }) } }, { einschalten: a.fn }) assert.equal(a.aufrufe.length, 0) assert.match(fx.text(wurzel), /Du kannst einschalten, sobald die offenen Punkte erledigt sind\./) await fx.klick(fx.finde(wurzel, '[data-aktion="zum-schritt"][data-schritt="zeiten"]')) assert.equal(fx.finde(wurzel, '.tm-schritt[aria-current="step"]').getAttribute('data-schritt'), 'zeiten') })) test('Abo: nach dem Einschalten lädt die Ansicht neu und zeigt die Statuskarte „In Einrichtung“ mit „Eingeschaltet am“', mitDom(async (fx) => { const a = einschaltenAttrappe() let n = 0 const wurzel = await oeffne(fx, { 'GET /api/funktionen': () => (n === 0 ? liste('abo') : liste('abo', 'setup', { orderedAt: '2026-10-16T09:00:00.000Z', ticketWoid: '100150' })), [EINSTELLUNGEN]: () => ({ body: n === 0 ? daten() : daten({ state: 'setup' }) }), }, { einschalten: a.fn }) n = 1 a.fertig(0) await fx.ruhe() const karte = fx.finde(wurzel, '.tm-statuskarte[data-state="setup"]') assert.match(fx.text(karte), /Eingeschaltet am\s*16\.10\.2026/) assert.doesNotMatch(fx.text(karte), /Bestellt am|Zahlung/) })) test('Abo: ohne Funktion einschalten steht E4; in der Kundenansicht steht F1 und der Ablauf bekommt viewAs true', mitDom(async (fx) => { const ohne = await oeffne(fx, { 'GET /api/funktionen': liste('abo'), [EINSTELLUNGEN]: { body: daten() } }) assert.match(fx.text(ohne), /Das Einschalten ist gerade nicht verfügbar\. Bitte lade die Seite neu oder schreib uns im Chat\./) const a = einschaltenAttrappe() const ansicht = await oeffne(fx, { 'GET /api/funktionen': liste('abo'), [EINSTELLUNGEN]: { body: daten({ kundenansicht: true }) } }, { einschalten: a.fn }) assert.match(fx.text(ansicht), /Du siehst den Fragebogen in der Kundenansicht\. Angaben kannst du ändern, Schritte bestätigen und einschalten kann nur der Kunde selbst\./) assert.equal(a.aufrufe[0].viewAs, true) })) test('Abo: die Zusammenfassung der Angaben bleibt auf der Einschalt-Seite (Gruppen mit „Ändern“)', mitDom(async (fx) => { const a = einschaltenAttrappe() const wurzel = await oeffne(fx, { 'GET /api/funktionen': liste('abo'), [EINSTELLUNGEN]: { body: daten() } }, { einschalten: a.fn }) assert.ok(fx.alle(wurzel, '.tm-bestellen-gruppe').length > 0) assert.ok(fx.finde(wurzel, '[data-aktion="aendern"]')) })) test('Ohne modell (fehlt, leer, „Abo“, „abo “): unverändert Bestellseite mit Knopf des Registers, Ablauf wird nie gerufen', mitDom(async (fx) => { for (const modell of [undefined, '', 'Abo', 'abo ']) { const a = einschaltenAttrappe() const wurzel = await oeffne(fx, { 'GET /api/funktionen': liste(modell), [EINSTELLUNGEN]: { body: daten({ bestellung: BESTELLUNG }) } }, { einschalten: a.fn }) assert.equal(a.aufrufe.length, 0, JSON.stringify(modell)) assert.equal(fx.text(fx.finde(wurzel, '[data-aktion="bestellen"]')), 'Zahlungspflichtig bestellen · 149 €') assert.equal(fx.text(fx.finde(wurzel, '.tm-fb-bestellen h3')), 'Prüfen und bestellen') assert.match(fx.text(fx.finde(wurzel, '.tm-schritt[aria-current="step"]')), /Prüfen und bestellen/) } }))