104 lines
6.3 KiB
JavaScript
104 lines
6.3 KiB
JavaScript
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.equal(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/)
|
||
}
|
||
}))
|