Webshop: im Abo-Modell Anfrage ohne Unternehmer-Haken, Privat-Hinweis, Einschalten

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-16 18:45:17 +00:00
1 parent 1cabab2826
commit 28da4180bd
7 files changed
+147 -22

No files matched your search

+12 -9
View File
@@ -31,10 +31,13 @@ export const anfrageListe = (anfrage) => h('dl', { class: 'sh-fakten' }, anfrage
/** /**
* Formular „Webshop anfragen“ in root. optionen: projectId, vorgabe (frueherer Stand der Anfrage zum Vorausfuellen), preis ('299 €'), * Formular „Webshop anfragen“ in root. optionen: projectId, vorgabe (frueherer Stand der Anfrage zum Vorausfuellen), preis ('299 €'),
* ustHinweis (Satz zur Umsatzsteuer), nachSenden(anfrage) (die Anfrage ist gespeichert: die Ansicht zeichnet sich neu). * ustHinweis (Satz zur Umsatzsteuer), nachSenden(anfrage) (die Anfrage ist gespeichert: die Ansicht zeichnet sich neu), modell ('abo': kein
* Preis und keine Umsatzsteuer, kein Feld „unternehmer“, statt des Hakens „keine ausgeschlossenen Waren“ ein Satz über dem Knopf; gesendet wird
* keineAusgeschlossenen: true, unternehmer entfällt – die Kundenart kennt der Server).
* Der Server prueft alles; ein 409 (z. B. laeuft schon eine Anfrage) zeigt seinen Text und laesst die Ansicht neu laden. * Der Server prueft alles; ein 409 (z. B. laeuft schon eine Anfrage) zeigt seinen Text und laesst die Ansicht neu laden.
*/ */
export function renderAnfrageFormular(root, { projectId, vorgabe = null, preis = '299 €', ustHinweis = '', nachSenden } = {}) { export function renderAnfrageFormular(root, { projectId, vorgabe = null, preis = '299 €', ustHinweis = '', nachSenden, modell } = {}) {
const abo = modell === 'abo' // Abo-Modell (strikt): ohne Preis, ohne Unternehmer-Haken, der Satz S2 statt des Hakens
const v = vorgabe && typeof vorgabe === 'object' ? vorgabe : {} const v = vorgabe && typeof vorgabe === 'object' ? vorgabe : {}
const auswahl = (liste, aktuell) => { const auswahl = (liste, aktuell) => {
const optionen = [h('option', { value: '' }, 'Bitte wählen …'), ...liste.map(([wert, name]) => h('option', { value: wert }, name))] const optionen = [h('option', { value: '' }, 'Bitte wählen …'), ...liste.map(([wert, name]) => h('option', { value: wert }, name))]
@@ -64,10 +67,11 @@ export function renderAnfrageFormular(root, { projectId, vorgabe = null, preis =
h('legend', null, 'Wie bekommen deine Kunden die Ware?'), h('legend', null, 'Wie bekommen deine Kunden die Ware?'),
hakenFeld('versand', versand, 'Versand'), hakenFeld('versand', versand, 'Versand'),
hakenFeld('abholung', abholung, 'Abholung')), hakenFeld('abholung', abholung, 'Abholung')),
hakenFeld('unternehmer', unternehmer, 'Ich verkaufe als Unternehmer (nicht privat).'), abo ? null : hakenFeld('unternehmer', unternehmer, 'Ich verkaufe als Unternehmer (nicht privat).'),
hakenFeld('keineAusgeschlossenen', ausgeschlossen, `Ich verkaufe keine ausgeschlossenen Waren (${AUSGESCHLOSSEN}).`), abo ? null : hakenFeld('keineAusgeschlossenen', ausgeschlossen, `Ich verkaufe keine ausgeschlossenen Waren (${AUSGESCHLOSSEN}).`),
feld('nachricht', 'Noch etwas, das wir wissen sollten? (freiwillig)', nachricht), feld('nachricht', 'Noch etwas, das wir wissen sollten? (freiwillig)', nachricht),
allgemeinerFehler(), allgemeinerFehler(),
abo ? h('p', { class: 'sh-hinweis', 'data-satz': 'ausgeschlossen' }, `Mit „Anfrage senden“ bestätigst du, dass du keine ausgeschlossenen Waren verkaufst (${AUSGESCHLOSSEN}).`) : null,
h('div', { class: 'sh-aktionen' }, absenden)) h('div', { class: 'sh-aktionen' }, absenden))
let laeuft = false let laeuft = false
@@ -88,8 +92,7 @@ export function renderAnfrageFormular(root, { projectId, vorgabe = null, preis =
artikelzahl: artikelzahl.value, artikelzahl: artikelzahl.value,
versand: versand.checked, versand: versand.checked,
abholung: abholung.checked, abholung: abholung.checked,
unternehmer: unternehmer.checked, ...(abo ? { keineAusgeschlossenen: true } : { unternehmer: unternehmer.checked, keineAusgeschlossenen: ausgeschlossen.checked }),
keineAusgeschlossenen: ausgeschlossen.checked,
nachricht: nachricht.value.trim(), nachricht: nachricht.value.trim(),
}, },
}, },
@@ -112,12 +115,12 @@ export function renderAnfrageFormular(root, { projectId, vorgabe = null, preis =
h('h3', null, 'Webshop anfragen'), h('h3', null, 'Webshop anfragen'),
h('p', null, 'Mit dem Webshop verkaufst du direkt auf deiner Website: deine Kunden legen Artikel in den Warenkorb und bezahlen online.'), h('p', null, 'Mit dem Webshop verkaufst du direkt auf deiner Website: deine Kunden legen Artikel in den Warenkorb und bezahlen online.'),
h('ul', null, h('ul', null,
h('li', null, `${preis} einmalig für den Einbau in deine Website, keine laufende Gebühr von WEBklar.`), h('li', null, abo ? 'Im Wartungs-Abo enthalten.' : `${preis} einmalig für den Einbau in deine Website, keine laufende Gebühr von WEBklar.`),
h('li', null, 'Die Zahlungen gehen direkt auf dein eigenes Stripe-Konto. Die Gebühren von Stripe zahlst du direkt an Stripe.'), h('li', null, 'Die Zahlungen gehen direkt auf dein eigenes Stripe-Konto. Die Gebühren von Stripe zahlst du direkt an Stripe.'),
h('li', null, 'Deine Kunden zahlen mit Karte, Apple Pay, Google Pay, Klarna oder SEPA-Lastschrift.'), h('li', null, 'Deine Kunden zahlen mit Karte, Apple Pay, Google Pay, Klarna oder SEPA-Lastschrift.'),
h('li', null, 'PayPal ist über Stripe leider nicht möglich.'), h('li', null, 'PayPal ist über Stripe leider nicht möglich.'),
h('li', null, 'Wir sehen uns deine Anfrage kurz an. Erst nach unserer Zusage kannst du bestellen.')), h('li', null, abo ? 'Wir sehen uns deine Anfrage kurz an. Erst nach unserer Zusage kannst du einschalten.' : 'Wir sehen uns deine Anfrage kurz an. Erst nach unserer Zusage kannst du bestellen.')),
ustHinweis ? h('p', { class: 'sh-hinweis' }, ustHinweis) : null) ustHinweis && !abo ? h('p', { class: 'sh-hinweis' }, ustHinweis) : null)
const karte = h('section', { class: 'kb-panel sh-anfrage' }, erklaerung, form) const karte = h('section', { class: 'kb-panel sh-anfrage' }, erklaerung, form)
root.replaceChildren(karte) root.replaceChildren(karte)
+19 -6
View File
@@ -15,7 +15,7 @@
import { fehlerKarte, h, infoKarte, request } from './shop-dom.js' import { fehlerKarte, h, infoKarte, request } from './shop-dom.js'
import { anfrageKarte, anfrageZeilen, renderAnfrageFormular } from './shop-anfrage.js' import { anfrageKarte, anfrageZeilen, renderAnfrageFormular } from './shop-anfrage.js'
import { renderBestellen, statusKarte } from './funktion-bestellen.js' import { renderBestellen, renderEinschaltSeite, statusKarte } from './funktion-bestellen.js'
import { meldeReiter, reiterEintraege } from './fn-reiter.js' import { meldeReiter, reiterEintraege } from './fn-reiter.js'
/** Die Reiter: jeder ein Modul mit render(panel, ctx), erst beim ersten Oeffnen geladen. */ /** Die Reiter: jeder ein Modul mit render(panel, ctx), erst beim ersten Oeffnen geladen. */
@@ -31,6 +31,8 @@ const HINWEIS_SETUP = 'Wir bauen deinen Shop gerade ein. Du kannst schon Artikel
const HINWEIS_ABGESCHALTET = 'Dein Webshop ist abgeschaltet. Du kannst Bestellungen noch ansehen, bearbeiten, erstatten und exportieren.' const HINWEIS_ABGESCHALTET = 'Dein Webshop ist abgeschaltet. Du kannst Bestellungen noch ansehen, bearbeiten, erstatten und exportieren.'
const DANKE_SETUP = 'Danke! Die Zahlung ist bei uns angekommen.' const DANKE_SETUP = 'Danke! Die Zahlung ist bei uns angekommen.'
const NICHT_VERFUEGBAR = 'Der Webshop ist noch nicht verfügbar.' const NICHT_VERFUEGBAR = 'Der Webshop ist noch nicht verfügbar.'
const PRIVAT_TITEL = 'Der Webshop ist für Betriebe' // Abo-Modell, Text S3
const PRIVAT_TEXT = 'Für Privatpersonen gibt es den Webshop leider nicht. Stimmt das nicht? Schreib uns im Chat.'
const SHOP_PREIS = '299 €' const SHOP_PREIS = '299 €'
const DOKUMENT_TITEL = [ const DOKUMENT_TITEL = [
['leistungsbeschreibung', 'Leistungsbeschreibung'], ['leistungsbeschreibung', 'Leistungsbeschreibung'],
@@ -66,22 +68,22 @@ function leseRueckkehr() {
return { bestellt: query.get('bestellt') === '1', stripe: stripe === 'zurueck' || stripe === 'neu' ? stripe : '', projekt: query.get('projekt') || '' } return { bestellt: query.get('bestellt') === '1', stripe: stripe === 'zurueck' || stripe === 'neu' ? stripe : '', projekt: query.get('projekt') || '' }
} }
export function initShop(root, { projectId } = {}) { export function initShop(root, { projectId, einschalten } = {}) {
if (!root) return Promise.resolve() if (!root) return Promise.resolve()
return starte(root, projectId) return starte(root, projectId, einschalten)
} }
async function starte(root, projectId) { async function starte(root, projectId, einschalten) {
const rueckkehr = leseRueckkehr() const rueckkehr = leseRueckkehr()
const wunsch = rueckkehr.projekt || projectId || root.dataset?.wunschProjekt || '' // die Kachel in Funktionen legt ihr Projekt hier ab const wunsch = rueckkehr.projekt || projectId || root.dataset?.wunschProjekt || '' // die Kachel in Funktionen legt ihr Projekt hier ab
if (root.dataset) delete root.dataset.wunschProjekt if (root.dataset) delete root.dataset.wunschProjekt
if (wunsch) auswahl.projectId = wunsch if (wunsch) auswahl.projectId = wunsch
const s = { root, projects: [], funktionen: null, projekt: null, status: null, lauf: 0, buehne: null, meldungEl: null, danke: false, stripe: '', neueAnfrage: false } const s = { root, projects: [], funktionen: null, projekt: null, status: null, lauf: 0, buehne: null, meldungEl: null, danke: false, stripe: '', neueAnfrage: false, einschalten }
S = s S = s
try { try {
await ladeFunktionen(s) await ladeFunktionen(s)
} catch (err) { } catch (err) {
if (S === s) root.replaceChildren(fehlerKarte(err.message, () => starte(root, auswahl.projectId))) if (S === s) root.replaceChildren(fehlerKarte(err.message, () => starte(root, auswahl.projectId, einschalten)))
return return
} }
if (S !== s) return if (S !== s) return
@@ -217,6 +219,11 @@ function zeigeAnfrage(s, { status, aktuell }) {
s.buehne.replaceChildren(anfrageKarte(ansicht, { projectId, neu: () => { s.neueAnfrage = true; zeigeAnfrage(s, { status, aktuell }) } })) s.buehne.replaceChildren(anfrageKarte(ansicht, { projectId, neu: () => { s.neueAnfrage = true; zeigeAnfrage(s, { status, aktuell }) } }))
return return
} }
// Abo-Modell: den Webshop gibt es nur fuer Betriebe; Privatkunden und eine leere oder fehlende Kundenart (E4) bekommen den Hinweis statt des Formulars
if (istAbo(s) && s.funktionen.kundenart !== 'unternehmen') {
s.buehne.replaceChildren(infoKarte(PRIVAT_TITEL, PRIVAT_TEXT, { chat: true }))
return
}
// keine Anfrage, zurueckgezogen oder „Neue Anfrage stellen“: das Formular, mit den frueheren Angaben vorausgefuellt // keine Anfrage, zurueckgezogen oder „Neue Anfrage stellen“: das Formular, mit den frueheren Angaben vorausgefuellt
const wurzel = h('div', { class: 'sh-wurzel' }) const wurzel = h('div', { class: 'sh-wurzel' })
s.buehne.replaceChildren(wurzel) s.buehne.replaceChildren(wurzel)
@@ -225,6 +232,7 @@ function zeigeAnfrage(s, { status, aktuell }) {
vorgabe: ansicht?.anfrage || null, vorgabe: ansicht?.anfrage || null,
preis: text(f?.priceLabel) || SHOP_PREIS, preis: text(f?.priceLabel) || SHOP_PREIS,
ustHinweis: text(s.funktionen?.ustHinweis), ustHinweis: text(s.funktionen?.ustHinweis),
modell: s.funktionen?.modell,
nachSenden: async () => { s.neueAnfrage = false; await ladeNeu(s) }, nachSenden: async () => { s.neueAnfrage = false; await ladeNeu(s) },
}) })
} }
@@ -274,6 +282,11 @@ function zeigeBestellseite(s, { status, aktuell }) {
}, },
} }
const daten = bestelldaten(s, status) const daten = bestelldaten(s, status)
if (istAbo(s)) {
// Abo-Modell: die Zusage der Anfrage fuehrt zum Einschalten (kein Preis, keine Haken, kein Checkout)
renderEinschaltSeite(wurzel, { ctx, daten, key: 'shop', zusammenfassung: zusammenfassungVon(daten.anfrage), einschalten: s.einschalten, nachEinschalten: () => ladeNeu(s) })
return
}
renderBestellen(wurzel, { ctx, daten, key: 'shop', zusammenfassung: zusammenfassungVon(daten.anfrage) }) renderBestellen(wurzel, { ctx, daten, key: 'shop', zusammenfassung: zusammenfassungVon(daten.anfrage) })
} }
@@ -23,7 +23,7 @@ test('mountet den Ablauf genau einmal in den Platz, mit key, projectId und viewA
assert.deepEqual([a.aufrufe[0].key, a.aufrufe[0].projectId, a.aufrufe[0].viewAs], ['shop', 'p1', false]) 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(a.aufrufe[0].wurzel === fx.finde(wurzel, '[data-einschalten="shop"]'))
assert.ok(fx.finde(wurzel, '.tm-einschalten-platz')) assert.ok(fx.finde(wurzel, '.tm-einschalten-platz'))
assert.equal(fx.finde(wurzel, 'h3'), null) assert.ok(fx.finde(wurzel, 'h3') === null)
assert.equal(fx.alle(wurzel, 'input').length, 0) assert.equal(fx.alle(wurzel, 'input').length, 0)
assert.doesNotMatch(fx.text(wurzel), KEIN_PREIS) assert.doesNotMatch(fx.text(wurzel), KEIN_PREIS)
const hulle = fx.finde(wurzel, 'section.kb-panel.tm-bestellen.tm-einschalten') const hulle = fx.finde(wurzel, 'section.kb-panel.tm-bestellen.tm-einschalten')
@@ -36,7 +36,7 @@ test('Zusammenfassung mit E1; offene Punkte sperren den Ablauf (E2); „Ändern
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 } }) 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(fx.text(fx.finde(wurzel, 'h3')), 'Prüfen und einschalten')
assert.equal(a.aufrufe.length, 0) assert.equal(a.aufrufe.length, 0)
assert.equal(fx.finde(wurzel, '.tm-einschalten-platz'), null) assert.ok(fx.finde(wurzel, '.tm-einschalten-platz') === null)
assert.match(fx.text(wurzel), /Du kannst einschalten, sobald die offenen Punkte erledigt sind\./) assert.match(fx.text(wurzel), /Du kannst einschalten, sobald die offenen Punkte erledigt sind\./)
assert.doesNotMatch(fx.text(wurzel), /bestellen/i) 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="zum-schritt"][data-schritt="zeiten"]'))
@@ -51,7 +51,7 @@ test('Zusammenfassung ohne offene Punkte: Gruppe, dann der Platz mit dem Ablauf'
assert.equal(fx.text(fx.finde(wurzel, '.tm-bestellen-gruppe h4')), 'Deine Anfrage') assert.equal(fx.text(fx.finde(wurzel, '.tm-bestellen-gruppe h4')), 'Deine Anfrage')
assert.equal(a.aufrufe.length, 1) assert.equal(a.aufrufe.length, 1)
assert.doesNotMatch(fx.text(wurzel), /Du kannst einschalten, sobald/) assert.doesNotMatch(fx.text(wurzel), /Du kannst einschalten, sobald/)
assert.equal(fx.finde(wurzel, '[data-aktion="aendern"]'), null, 'ohne zumSchritt kein „Ändern“') 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 () => { test('Kundenansicht: der Ablauf bekommt viewAs true (daten und ctx)', mitDom(async () => {
+2 -2
View File
@@ -87,8 +87,8 @@ for (const f of FAELLE) {
test(`${f.name}, Abo: keine eigene Leiste, kn:reiter mit Kurzlabels, bei() wechselt`, mitDom(async (fx) => { test(`${f.name}, Abo: keine eigene Leiste, kn:reiter mit Kurzlabels, bei() wechselt`, mitDom(async (fx) => {
const k = hoereAufKn(fx) const k = hoereAufKn(fx)
const root = await oeffne(fx, f, 'abo') const root = await oeffne(fx, f, 'abo')
assert.equal(fx.finde(root, '[role="tablist"]'), null) assert.ok(fx.finde(root, '[role="tablist"]') === null)
assert.equal(fx.finde(root, '[role="tab"]'), null) assert.ok(fx.finde(root, '[role="tab"]') === null)
const panel = fx.finde(root, '[role="tabpanel"]') const panel = fx.finde(root, '[role="tabpanel"]')
assert.ok(panel, 'das Panel bleibt') assert.ok(panel, 'das Panel bleibt')
assert.equal(panel.hasAttribute('aria-labelledby'), false) assert.equal(panel.hasAttribute('aria-labelledby'), false)
+109
View File
@@ -0,0 +1,109 @@
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { h } from '../public/shop-dom.js'
import { initShop, vergissAuswahl } from '../public/shop.js'
import { AUSGESCHLOSSEN } from '../public/shop-anfrage.js'
import { KEIN_PREIS, einschaltenAttrappe, hoereAufKn, mitDom, routen } from './support/modulAbo.js'
// Webshop im Abo-Modell (P4 Task 5): Anfrage ohne Unternehmer-Haken und ohne Preis, Satz über dem Knopf, Hinweis für Privatkunden,
// nach der Zusage der Ablauf des Einschaltens statt der Bestellseite. Ohne modell === 'abo' bleibt alles wie heute.
const ANFRAGE = { warenart: 'kleidung', beschreibung: 'Handgemachte Mützen und Schals.', artikelzahl: 'bis20', versand: true, abholung: false, unternehmer: true, keineAusgeschlossenen: true, nachricht: '' }
const funk = (state, kopf = {}) => ({
projects: [{ projectId: 'p1', projectName: 'Mein Laden', features: { shop: { state } } }],
features: { shop: { priceLabel: '299 €', consent: { version: 'v', knopf: 'Zahlungspflichtig bestellen · 299 €', items: [] } } },
ustHinweis: 'einmalig, ohne Umsatzsteuer gemäß § 19 UStG',
legal: { shop: {} },
shop: { verfuegbar: true },
...kopf,
})
async function oeffne(fx, { kopf = {}, state = 'off', anfrage = null, einschalten } = {}) {
vergissAuswahl()
routen(fx, {
'GET /api/funktionen': { body: funk(state, kopf) },
'GET /api/shop/status?projectId=p1': { body: { verfuegbar: true, state, ticketWoid: null, anfrage } },
'POST /api/shop/anfrage': { body: { anfrage: {} } },
})
const root = h('div')
document.body.append(root)
await initShop(root, { projectId: 'p1', einschalten })
await fx.ruhe()
return root
}
const ZUGESAGT = { status: 'angenommen', anfrage: ANFRAGE, at: '2026-10-05T10:30:00.000Z' }
test('Abo, Betrieb: Anfrage ohne Unternehmer-Haken, Satz S2 über dem Knopf, kein Preis; Körper ohne unternehmer', mitDom(async (fx) => {
const root = await oeffne(fx, { kopf: { modell: 'abo', kundenart: 'unternehmen' } })
assert.ok(fx.finde(root, '[data-feld="unternehmer"]') === null)
assert.ok(fx.finde(root, '[data-feld="keineAusgeschlossenen"]') === null)
const satz = fx.finde(root, '[data-satz="ausgeschlossen"]')
assert.equal(fx.text(satz), `Mit „Anfrage senden“ bestätigst du, dass du keine ausgeschlossenen Waren verkaufst (${AUSGESCHLOSSEN}).`)
const knoten = fx.alle(root, '*')
assert.ok(knoten.indexOf(satz) < knoten.indexOf(fx.finde(root, '[data-aktion="anfrage-senden"]')), 'Satz steht über dem Knopf')
const erklaerung = fx.text(fx.finde(root, '.sh-erklaerung'))
assert.match(erklaerung, /Im Wartungs-Abo enthalten\./)
assert.match(erklaerung, /Wir sehen uns deine Anfrage kurz an\. Erst nach unserer Zusage kannst du einschalten\./)
assert.doesNotMatch(erklaerung, KEIN_PREIS)
assert.doesNotMatch(erklaerung, /bestellen/)
assert.doesNotMatch(fx.text(root), KEIN_PREIS)
await fx.setze(fx.finde(root, '[data-feld="warenart"]'), 'kleidung')
await fx.setze(fx.finde(root, '[data-feld="beschreibung"]'), 'Handgemachte Mützen und Schals.')
await fx.setze(fx.finde(root, '[data-feld="artikelzahl"]'), 'bis20')
await fx.klick(fx.finde(root, '[data-feld="versand"]'))
await fx.klick(fx.finde(root, '[data-aktion="anfrage-senden"]'))
assert.deepEqual(fx.fetchLog.find((a) => a.method === 'POST').body, {
projectId: 'p1',
anfrage: { warenart: 'kleidung', beschreibung: 'Handgemachte Mützen und Schals.', artikelzahl: 'bis20', versand: true, abholung: false, keineAusgeschlossenen: true, nachricht: '' },
})
}))
test('Abo, privat oder Kundenart leer/fehlend: Hinweis S3 mit Chat, nie das Formular, kein POST, leere Reiterliste', mitDom(async (fx) => {
const k = hoereAufKn(fx)
for (const kundenart of ['privat', '', undefined]) {
const root = await oeffne(fx, { kopf: { modell: 'abo', kundenart } })
assert.ok(fx.finde(root, 'form') === null, String(kundenart))
assert.match(fx.text(root), /Der Webshop ist für Betriebe/)
assert.match(fx.text(root), /Für Privatpersonen gibt es den Webshop leider nicht\. Stimmt das nicht\? Schreib uns im Chat\./)
assert.ok(fx.finde(root, 'button[data-view="chat"]'))
}
assert.equal(fx.fetchLog.filter((a) => a.method === 'POST').length, 0)
assert.deepEqual(k.reiter.at(-1).eintraege, [])
}))
test('Abo, privat mit bestehendem Shop (active): die fünf Reiter bleiben', mitDom(async (fx) => {
const k = hoereAufKn(fx)
await oeffne(fx, { kopf: { modell: 'abo', kundenart: 'privat' }, state: 'active' })
assert.equal(k.reiter.at(-1).eintraege.length, 5)
}))
test('Abo, Anfrage angenommen: Gruppe B1 und Ablauf key shop statt Bestellseite; onFertig lädt neu', mitDom(async (fx) => {
const a = einschaltenAttrappe()
const root = await oeffne(fx, { kopf: { modell: 'abo', kundenart: 'unternehmen' }, anfrage: ZUGESAGT, einschalten: a.fn })
assert.equal(a.aufrufe[0].key, 'shop')
assert.equal(a.aufrufe[0].projectId, 'p1')
assert.equal(fx.text(fx.finde(root, '.tm-bestellen-gruppe h4')), 'Deine Anfrage')
assert.ok(fx.finde(root, '[data-aktion="bestellen"]') === null)
assert.doesNotMatch(fx.text(root), KEIN_PREIS)
a.fertig(0)
await fx.ruhe()
assert.equal(fx.fetchLog.filter((x) => x.url === '/api/shop/status?projectId=p1').length, 2)
}))
test('Abo, Anfrage angenommen, ohne Funktion einschalten: Fehlerkarte E4', mitDom(async (fx) => {
const root = await oeffne(fx, { kopf: { modell: 'abo', kundenart: 'unternehmen' }, anfrage: ZUGESAGT })
assert.match(fx.text(root), /Das Einschalten ist gerade nicht verfügbar\./)
}))
test('Ohne modell (fehlt, „Abo“): Formular mit Unternehmer-Haken und 299 €, nach der Zusage die Bestellseite, der Ablauf wird nie gerufen', mitDom(async (fx) => {
for (const modell of [undefined, 'Abo']) {
const a = einschaltenAttrappe()
const kopf = modell ? { modell } : {}
const formular = await oeffne(fx, { kopf, einschalten: a.fn })
assert.ok(fx.finde(formular, '[data-feld="unternehmer"]'))
assert.ok(fx.finde(formular, '[data-feld="keineAusgeschlossenen"]'))
assert.match(fx.text(formular), /299 € einmalig/)
const zugesagt = await oeffne(fx, { kopf, anfrage: ZUGESAGT, einschalten: a.fn })
assert.ok(fx.finde(zugesagt, '[data-aktion="bestellen"]'))
assert.equal(a.aufrufe.length, 0)
}
}))
+1 -1
View File
@@ -45,7 +45,7 @@ test('Abo, alles bestätigt: letzter Schritt E1 mountet den Ablauf, ohne Haken,
assert.match(fx.text(fx.finde(wurzel, '.tm-schritt-stand')), /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.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“') 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) 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.equal(fx.fetchLog.filter((x) => x.method === 'POST').length, 0)
assert.deepEqual(k.reiter.at(-1).eintraege, []) assert.deepEqual(k.reiter.at(-1).eintraege, [])
})) }))
+1 -1
View File
@@ -28,7 +28,7 @@ test('app.js: VIEW_HASHES, viewRefreshers, FEATURE_LABELS und APP_PARENT kennen
test('shop.js und shop.css: die Hülle exportiert initShop, die Styles hängen an #view-webshop und sh-', () => { test('shop.js und shop.css: die Hülle exportiert initShop, die Styles hängen an #view-webshop und sh-', () => {
const js = lies('public/shop.js') const js = lies('public/shop.js')
assert.match(js, /export function initShop\(root, \{ projectId \} = \{\}\)/) assert.match(js, /export function initShop\(root, \{ projectId, einschalten \} = \{\}\)/)
assert.doesNotMatch(js.replace(/\/\*[\s\S]*?\*\//g, ''), /innerHTML|insertAdjacentHTML|outerHTML|document\.write/, 'Text nie als HTML') assert.doesNotMatch(js.replace(/\/\*[\s\S]*?\*\//g, ''), /innerHTML|insertAdjacentHTML|outerHTML|document\.write/, 'Text nie als HTML')
const css = lies('public/shop.css') const css = lies('public/shop.css')
assert.match(css, /#view-webshop/) assert.match(css, /#view-webshop/)