import { test } from 'node:test' import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { h } from '../public/termine-dom.js' import { renderEinschaltSeite, statusKarte } from '../public/funktion-bestellen.js' import { KEIN_PREIS, einschaltenAttrappe, mitDom } from './support/modulAbo.js' // Gemeinsame Einschalt-Seite der Funktionsmodule im Abo-Modell (P4 Task 2): Zusammenfassung und offene Punkte, danach der Ablauf von P3 // (zeigeEinschalten) im Platz div.tm-einschalten-platz; keine Haken, kein Preis, kein Checkout. function seite(optionen) { const wurzel = h('div') document.body.append(wurzel) renderEinschaltSeite(wurzel, { ctx: { projectId: 'p1' }, daten: { offen: [], kundenansicht: false }, key: 'shop', ...optionen }) return wurzel } const GRUPPEN = () => [{ schritt: 'anfrage', titel: 'Deine Anfrage', zeilen: ['Was du verkaufst: Kleidung'] }] test('mountet den Ablauf genau einmal in den Platz, mit key, projectId und viewAs; ohne Haken, Preis und Überschrift', mitDom(async (fx) => { const a = einschaltenAttrappe() const wurzel = seite({ einschalten: a.fn }) assert.equal(a.aufrufe.length, 1) assert.deepEqual([a.aufrufe[0].key, a.aufrufe[0].projectId, a.aufrufe[0].viewAs], ['shop', 'p1', false]) assert.ok(a.aufrufe[0].wurzel === fx.finde(wurzel, '[data-einschalten="shop"]')) assert.ok(fx.finde(wurzel, '.tm-einschalten-platz')) assert.ok(fx.finde(wurzel, 'h3') === null) assert.equal(fx.alle(wurzel, 'input').length, 0) assert.doesNotMatch(fx.text(wurzel), KEIN_PREIS) const hulle = fx.finde(wurzel, 'section.kb-panel.tm-bestellen.tm-einschalten') assert.equal(hulle.getAttribute('data-key'), 'shop') })) test('Zusammenfassung mit E1; offene Punkte sperren den Ablauf (E2); „Ändern“ und „Zum Schritt“ rufen zumSchritt', mitDom(async (fx) => { const a = einschaltenAttrappe() const schritte = [] const wurzel = seite({ einschalten: a.fn, zusammenfassung: GRUPPEN, zumSchritt: (k) => schritte.push(k), daten: { offen: [{ schritt: 'zeiten', text: 'Bitte wähle das Bundesland.' }], kundenansicht: false } }) assert.equal(fx.text(fx.finde(wurzel, 'h3')), 'Prüfen und einschalten') assert.equal(a.aufrufe.length, 0) assert.ok(fx.finde(wurzel, '.tm-einschalten-platz') === null) assert.match(fx.text(wurzel), /Du kannst einschalten, sobald die offenen Punkte erledigt sind\./) assert.doesNotMatch(fx.text(wurzel), /bestellen/i) await fx.klick(fx.finde(wurzel, '[data-aktion="zum-schritt"][data-schritt="zeiten"]')) await fx.klick(fx.finde(wurzel, '[data-aktion="aendern"][data-schritt="anfrage"]')) assert.deepEqual(schritte, ['zeiten', 'anfrage']) })) test('Zusammenfassung ohne offene Punkte: Gruppe, dann der Platz mit dem Ablauf', mitDom(async (fx) => { const a = einschaltenAttrappe() const wurzel = seite({ einschalten: a.fn, zusammenfassung: GRUPPEN }) assert.equal(fx.text(fx.finde(wurzel, 'h3')), 'Prüfen und einschalten') assert.equal(fx.text(fx.finde(wurzel, '.tm-bestellen-gruppe h4')), 'Deine Anfrage') assert.equal(a.aufrufe.length, 1) assert.doesNotMatch(fx.text(wurzel), /Du kannst einschalten, sobald/) assert.ok(fx.finde(wurzel, '[data-aktion="aendern"]') === null, 'ohne zumSchritt kein „Ändern“') })) test('Kundenansicht: der Ablauf bekommt viewAs true (daten und ctx)', mitDom(async () => { const a = einschaltenAttrappe() seite({ einschalten: a.fn, daten: { offen: [], kundenansicht: true } }) const b = einschaltenAttrappe() seite({ einschalten: b.fn, ctx: { projectId: 'p1', kundenansicht: true } }) assert.equal(a.aufrufe[0].viewAs, true) assert.equal(b.aufrufe[0].viewAs, true) })) test('onFertig: höchstens einmal, nicht nach ctx.aktuell() === false', mitDom(async (fx) => { let n = 0 const a = einschaltenAttrappe() seite({ einschalten: a.fn, nachEinschalten: () => { n += 1 } }) a.fertig(0) a.fertig(0) await fx.ruhe() assert.equal(n, 1) const b = einschaltenAttrappe() seite({ einschalten: b.fn, ctx: { projectId: 'p1', aktuell: () => false }, nachEinschalten: () => { n += 1 } }) b.fertig(0) await fx.ruhe() assert.equal(n, 1) })) test('nachEinschalten darf scheitern, ohne dass ein Promise unbehandelt bleibt', mitDom(async (fx) => { const a = einschaltenAttrappe() seite({ einschalten: a.fn, nachEinschalten: async () => { throw new Error('Netz weg') } }) a.fertig(0) await fx.ruhe() })) test('ohne Funktion einschalten: Fehlerkarte E4 statt Absturz', mitDom(async (fx) => { const wurzel = seite({}) assert.match(fx.text(wurzel), /Das Einschalten ist gerade nicht verfügbar\. Bitte lade die Seite neu oder schreib uns im Chat\./) assert.ok(fx.finde(wurzel, '[role="alert"]')) })) test('wirft oder scheitert einschalten, steht E4 im Platz', mitDom(async (fx) => { const wurzel = seite({ einschalten: () => { throw new Error('kaputt') } }) assert.match(fx.text(fx.finde(wurzel, '.tm-einschalten-platz')), /Das Einschalten ist gerade nicht verfügbar\./) const spaeter = seite({ einschalten: () => Promise.reject(new Error('kaputt')) }) await fx.ruhe() assert.match(fx.text(fx.finde(spaeter, '.tm-einschalten-platz')), /Das Einschalten ist gerade nicht verfügbar\./) })) test('statusKarte: Abo-Modell „Eingeschaltet am“, sonst „Bestellt am“ (auch bei „Abo“ und leer)', mitDom(async (fx) => { const args = { projekt: { projectId: 'p1' }, bestellung: { state: 'setup', orderedAt: '2026-10-16T09:00:00.000Z', ticketWoid: '100150' } } assert.match(fx.text(statusKarte({ ...args, modell: 'abo' })), /Eingeschaltet am\s*16\.10\.2026/) assert.match(fx.text(statusKarte(args)), /Bestellt am\s*16\.10\.2026/) for (const modell of ['Abo', '', 'abo ']) assert.match(fx.text(statusKarte({ ...args, modell })), /Bestellt am/, JSON.stringify(modell)) assert.doesNotMatch(fx.text(statusKarte({ ...args, modell: 'abo' })), /Bestellt am/) })) test('Quelltext: renderEinschaltSeite kommt ohne neue Importe, href, innerHTML und Pin-Wörter aus', () => { const roh = readFileSync(new URL('../public/funktion-bestellen.js', import.meta.url), 'utf8') const code = roh.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|\s)\/\/.*$/gm, '') assert.deepEqual(code.match(/from '[^']+'/g), ["from './termine-dom.js'", "from './termine-view.js'"]) assert.doesNotMatch(code, /import\s*\(/) })