Files
Webklar-Kundenbereich/test/funktionBestellenKey.test.js
ClaudeandClaude Opus 5.5 809da80c9b Shop G1: Verdrahtung #webshop, Webshop-Kachel, Bestellseite mit key und optionaler Zusammenfassung
Dashboard-Abschnitt view-webshop, shop.css und Platzhalter shop.js, app.js (Hash, Refresher, Label, Elternansicht), Kachel in funktionen.js nach shop.verfuegbar, GET /api/funktionen liefert shop.verfuegbar, funktion-bestellen.js nimmt key/name/zusammenfassung (Standard: Terminplaner unverändert).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:57:02 +00:00

83 lines
4.4 KiB
JavaScript

import { test } from 'node:test'
import assert from 'node:assert/strict'
import { installFakeDom } from './support/fakeDom.js'
import { h } from '../public/termine-dom.js'
import * as bestellen from '../public/funktion-bestellen.js'
// public/funktion-bestellen.js, Task G1: Checkout-Pfad und Zusammenfassung sind Parameter (key, zusammenfassung); ohne sie bleibt alles
// beim Terminplaner. Ohne Server und ohne den schweren Fragebogen-Test: die Bestellung ist von Hand gebaut.
const URL_ZAHLUNG = 'https://checkout.stripe.test/c/pay/cs_test_9'
const mitDom = (optionen, fn) => async () => {
const fx = installFakeDom(optionen)
try {
await fn(fx)
} finally {
fx.restore()
}
}
const bestellung = () => ({
preis: '299 €',
preisHinweis: 'einmalig, ohne Umsatzsteuer gemäß § 19 UStG',
consent: { version: 'v-shop', knopf: 'Zahlungspflichtig bestellen', items: [{ id: 'a', text: 'Ich stimme zu.', haken: true }, { id: 'b', text: 'Hinweis', haken: false }] },
})
const daten = () => ({ kundenansicht: false, offen: [], bestellung: bestellung(), einstellung: {}, fragebogen: {}, branchen: [] })
async function bestelleUndGibLog(fx, optionen) {
const wurzel = h('div')
document.body.append(wurzel)
bestellen.renderBestellen(wurzel, { ctx: { projectId: 'p1', aktuell: () => true }, daten: daten(), ...optionen })
await fx.klick(fx.finde(wurzel, 'input[type="checkbox"]'))
await fx.klick(fx.finde(wurzel, '[data-aktion="bestellen"]'))
await fx.ruhe()
return { wurzel, log: fx.fetchLog }
}
test('renderBestellen mit key "shop": Checkout geht an /api/funktionen/shop/checkout, Körper unverändert', mitDom({ antworten: [{ body: { url: URL_ZAHLUNG } }] }, async (fx) => {
const { log } = await bestelleUndGibLog(fx, { key: 'shop' })
assert.deepEqual(log, [{ method: 'POST', url: '/api/funktionen/shop/checkout', body: { projectId: 'p1', zustimmung: { version: 'v-shop', haken: ['a'] } } }])
assert.equal(fx.window.location.href, URL_ZAHLUNG)
}))
test('renderBestellen ohne key: weiter /api/funktionen/termine/checkout; ein unzulässiger key fällt darauf zurück', mitDom({ antworten: [{ body: { url: URL_ZAHLUNG } }] }, async (fx) => {
const { log } = await bestelleUndGibLog(fx, {})
assert.equal(log[0].url, '/api/funktionen/termine/checkout')
}))
test('renderBestellen: ein key mit Pfadzeichen wird nie in den Pfad übernommen', mitDom({ antworten: [{ body: { url: URL_ZAHLUNG } }] }, async (fx) => {
const { log } = await bestelleUndGibLog(fx, { key: '../abo/x?y' })
assert.equal(log[0].url, '/api/funktionen/termine/checkout')
}))
test('renderBestellen: ohne Zusammenfassungsfunktion (key shop) kein Zusammenfassungsblock; mit Funktion werden ihre Gruppen gezeichnet', mitDom({}, async (fx) => {
const ohne = h('div')
document.body.append(ohne)
bestellen.renderBestellen(ohne, { ctx: { projectId: 'p1' }, daten: daten(), key: 'shop' })
assert.equal(fx.finde(ohne, '.tm-bestellen-zusammenfassung'), null)
assert.ok(fx.finde(ohne, '[data-aktion="bestellen"]'), 'Preis, Zustimmungen und Knopf bleiben')
const mit = h('div')
document.body.append(mit)
const aufrufe = []
bestellen.renderBestellen(mit, {
ctx: { projectId: 'p1' }, daten: daten(), key: 'shop',
zusammenfassung: (...args) => { aufrufe.push(args.length); return [{ schritt: 'artikel', titel: 'Artikel', zeilen: ['3 Artikel'] }] },
})
assert.equal(aufrufe.length, 1)
assert.ok(fx.finde(mit, '.tm-bestellen-zusammenfassung'))
assert.match(fx.text(fx.finde(mit, '.tm-bestellen-gruppe')), /Artikel/)
}))
test('statusKarte: key und name steuern Fortsetzen-Pfad und Texte; ohne Angaben bleibt es der Terminplaner', mitDom({ antworten: [{ body: { url: URL_ZAHLUNG } }] }, async (fx) => {
const shop = bestellen.statusKarte({ projekt: { projectId: 'p1' }, bestellung: { state: 'setup' }, key: 'shop', name: 'Webshop' })
assert.match(fx.text(shop), /Wir richten deinen Webshop ein/)
assert.doesNotMatch(fx.text(shop), /Terminplaner/)
const termine = bestellen.statusKarte({ projekt: { projectId: 'p1' }, bestellung: { state: 'setup' } })
assert.match(fx.text(termine), /Wir richten deinen Terminplaner ein/)
const offen = bestellen.statusKarte({ projekt: { projectId: 'p1' }, bestellung: { state: 'ordered' }, key: 'shop', name: 'Webshop' })
document.body.append(offen)
await fx.klick(fx.finde(offen, '[data-aktion="fortsetzen"]'))
await fx.ruhe()
assert.deepEqual(fx.fetchLog, [{ method: 'POST', url: '/api/funktionen/shop/checkout', body: { projectId: 'p1', fortsetzen: true } }])
}))