import { test } from 'node:test' import assert from 'node:assert/strict' import { zeigeBestellen, produktAusRoute } from '../public/neu/ansicht-bestellen.js' import { sichtbarerText, ohneKopf, ladeAlteApp } from './support/knWortlautOrakel.js' import { mit, me, aboAntwort, angebot, neueWurzel } from './support/knDaten.js' const alt = ladeAlteApp() const route = (produkt) => ({ bereich: 'vertraege', etage: 'bestellen', produkt, query: '' }) for (const key of ['hosting', 'wartung']) for (const [name, daten] of [['ohne Angebot', aboAntwort()], ['mit Angebot', aboAntwort({ offers: { hosting: angebot(), wartung: angebot() } })], ['Testkonto', aboAntwort({ testAccount: true })], ['nicht verfügbar', aboAntwort({ available: false })]]) { test(`Bestellen ${key} (${name}): sichtbarer Text wie in app.js`, mit({}, async (fx) => { const w = neueWurzel(fx); await zeigeBestellen(w, { route: route(key), me: me(), nav: 'push', daten }); await fx.ruhe() assert.equal(sichtbarerText(w), sichtbarerText(ohneKopf(alt.bestellen(daten, key)))) })) } test('produktAusRoute', () => { assert.equal(produktAusRoute({ produkt: 'wartung' }, ''), 'wartung'); assert.equal(produktAusRoute({}, '#bestellen-hosting'), 'hosting'); assert.equal(produktAusRoute({}, '#abo'), '') }) test('Bestellen meldet den Produktnamen als Titel', mit({}, async (fx) => { const titel = []; const w = neueWurzel(fx); w.addEventListener('kn:titel', (e) => titel.push(e.detail.text)) await zeigeBestellen(w, { route: route('wartung'), me: me(), nav: 'push', daten: aboAntwort() }); assert.deepEqual(titel, ['Wartungs-Abo']) })) test('Beginn-Auswahl ändert die Zeilen, Prüfungen wörtlich, Body wörtlich', mit({ 'POST /api/abo/checkout': { url: 'https://checkout.stripe.com/x' } }, async (fx) => { const w = neueWurzel(fx); await zeigeBestellen(w, { route: route('wartung'), me: me(), nav: 'push', daten: aboAntwort() }) await fx.klick(fx.finde(w, '#abo-buy')); assert.match(fx.text(w), /Bitte stimmen Sie den Vertragsbedingungen zu\./) await fx.klick(fx.finde(w, '#abo-terms')); await fx.klick(fx.finde(w, '#abo-buy')); assert.match(fx.text(w), /Bitte wählen Sie, wann wir mit der Leistung beginnen sollen\./) await fx.klick(fx.finde(w, 'input[value="sofort"]')) assert.ok(fx.text(w).includes('Heute fällig: 50,00 € (gemäß § 19 UStG ohne Umsatzsteuer)')); assert.ok(fx.text(w).includes('sofort, auf Ihren ausdrücklichen Wunsch vor Ablauf der Widerrufsfrist')) await fx.klick(fx.finde(w, '#abo-buy')); await fx.ruhe() assert.deepEqual(fx.fetchLog.at(-1), { method: 'POST', url: '/api/abo/checkout', body: { productKey: 'wartung', acceptTerms: true, startChoice: 'sofort', termsVersion: '2026-09-30-v5', offerId: '', lateStartAt: '2026-10-30T00:00:00.000Z' } }) assert.equal(fx.window.location.href, 'https://checkout.stripe.com/x') })) test('Bestellen: unsichere Adresse der Zahlungsseite wird nicht geöffnet, Ansichtsmodus sperrt', mit({ 'POST /api/abo/checkout': () => ({ body: { url: 'javascript:alert(1)' } }) }, async (fx) => { const w = neueWurzel(fx); await zeigeBestellen(w, { route: route('wartung'), me: me(), nav: 'push', daten: aboAntwort() }) await fx.klick(fx.finde(w, '#abo-terms')); await fx.klick(fx.finde(w, 'input[value="nach_frist"]')); await fx.klick(fx.finde(w, '#abo-buy')); await fx.ruhe() assert.notEqual(fx.window.location.href, 'javascript:alert(1)') assert.equal(fx.finde(w, '#abo-buy').hasAttribute('disabled'), false); assert.equal(fx.finde(w, '#abo-buy').textContent, 'Zahlungspflichtig bestellen') const v = neueWurzel(fx); await zeigeBestellen(v, { route: route('wartung'), me: me({ viewAs: true }), nav: 'push', daten: aboAntwort() }) assert.equal(fx.finde(v, '#abo-buy').hasAttribute('disabled'), true); assert.match(fx.text(v), /Im Ansichtsmodus nicht möglich\./) })) test('Kerninfo und Knopf stehen ohne Klappe nebeneinander', mit({}, async (fx) => { const w = neueWurzel(fx); await zeigeBestellen(w, { route: route('wartung'), me: me(), nav: 'push', daten: aboAntwort() }) assert.equal(fx.alle(w, 'details, dialog').length, 0); assert.ok(fx.finde(w, '#abo-order').textContent.includes('Zahlungspflichtig bestellen')) })) test('Bestellen: Angebot geht als offerId mit, Fehler der Bestellung machen den Knopf wieder frei', mit({ 'POST /api/abo/checkout': () => ({ status: 400, body: { error: 'Kaputt.' } }) }, async (fx) => { const daten = aboAntwort({ offers: { hosting: null, wartung: angebot({ id: 'o7' }) } }) const w = neueWurzel(fx); await zeigeBestellen(w, { route: route('wartung'), me: me(), nav: 'push', daten }) await fx.klick(fx.finde(w, '#abo-terms')); await fx.klick(fx.finde(w, 'input[value="nach_frist"]')); await fx.klick(fx.finde(w, '#abo-buy')); await fx.ruhe() assert.equal(fx.fetchLog.at(-1).body.offerId, 'o7'); assert.match(fx.text(w), /Kaputt\./); assert.equal(fx.finde(w, '#abo-buy').hasAttribute('disabled'), false) assert.match(fx.text(w), /Erste Abbuchung am 30\.10\.2026: 25,00 €/) })) test('Bestellen: ohne Produkt oder bei Ausfall des Abos steht nur ein Satz', mit({ 'GET /api/abo': () => ({ status: 500, body: { error: 'x' } }) }, async (fx) => { const w = neueWurzel(fx); await zeigeBestellen(w, { route: { bereich: 'vertraege', etage: 'bestellen', query: '' }, me: me(), nav: 'push', daten: aboAntwort() }); await fx.ruhe() assert.equal(sichtbarerText(w), sichtbarerText('Die Online-Bestellung wird gerade eingerichtet – schreiben Sie uns gern im Chat.')) const v = neueWurzel(fx); await zeigeBestellen(v, { route: route('hosting'), me: me(), nav: 'push' }); await fx.ruhe(); assert.match(fx.text(v), /Deine Verträge konnten gerade nicht geladen werden\./) }))