\./)
assert.match(mail.html, /Wir verkaufen keine <Waffen>\.
Bitte melde Dich\./)
assert.doesNotMatch(mail.text, /299 €|UStG|angenommen/)
})
// ---------------------------------------------------------------------------
// Webshop E4: Käufermails G09–G14 (Sie-Form, Betreff in der Vorlage, Fuß mit Anbieter und Widerrufsfunktion)
// und K25, K26 an den Händler (Du-Form)
// ---------------------------------------------------------------------------
const SHOP_GEMEIN = ['farbe', 'betrieb_name', 'anbieter_zeile', 'impressum_url', 'datenschutz_url', 'widerruf_url']
test('G09–G14: Platzhalter, Blöcke und Betreff wie im Plan (G-Vorlagen des Webshops haben keine Anrede-Blöcke)', () => {
const erwartet = {
G09: {
s: 'Ihre Bestellung {{bestellnummer}} bei {{betrieb_name}}',
p: ['bestellnummer', 'datum', 'positionen', 'zwischensumme', 'versand', 'gesamt', 'steuer_hinweis', 'lieferart_text', 'lieferadresse', 'abholort', 'lieferzeit', 'zahlungsart', 'ausschluss_text', 'agb', 'widerrufsbelehrung', 'widerrufsformular', 'gewaehrleistung'],
b: ['versand', 'abholung', 'ausschluss'],
},
G10: { s: 'Ihre Bestellung {{bestellnummer}} wurde versendet', p: ['bestellnummer', 'sendungsnummer', 'versanddienst'], b: ['sendung'] },
G11: { s: 'Ihre Bestellung {{bestellnummer}} ist abholbereit', p: ['bestellnummer', 'abholort', 'hinweis'], b: [] },
G12: { s: 'Eingangsbestätigung Ihres Widerrufs bei {{betrieb_name}}', p: ['name', 'bestellnummer', 'eingang', 'nachricht'], b: ['nachricht'] },
G13: { s: 'Erstattung zu Ihrer Bestellung {{bestellnummer}}', p: ['bestellnummer', 'betrag', 'grund'], b: ['grund'] },
G14: { s: 'Ihre Bestellung {{bestellnummer}} wurde nicht abgeschlossen', p: ['bestellnummer'], b: [] },
}
assert.deepEqual(Object.keys(erwartet), SHOP_GUEST)
for (const [id, { s, p, b }] of Object.entries(erwartet)) {
const tpl = loadTemplate(id)
assert.equal(tpl.subject, s, id)
assert.deepEqual([...tpl.placeholders].sort(), [...p, ...SHOP_GEMEIN].sort(), `${id}: Platzhalter`)
assert.deepEqual([...tpl.blocks].sort(), [...b].sort(), `${id}: Blöcke`)
}
})
test('G09–G14: neutrales Layout in der Shop-Farbe, kein Logo, Fuß mit Anbieter, Impressum, Datenschutz und Widerrufsfunktion', () => {
for (const id of SHOP_GUEST) {
const tpl = loadTemplate(id)
const source = sourceOf(id)
assert.match(source, /]*style="[^"]*background:\{\{farbe\}\}/, `${id}: Kopfbalken in {{farbe}}`)
assert.doesNotMatch(source, / ]*>Impressum, id)
assert.match(mail.html, /href="Wert-datenschutz_url"[^>]*>Datenschutz, id)
assert.match(mail.html, /href="Wert-widerruf_url"[^>]*>Vertrag widerrufen, id)
assert.match(mail.html, /background:Wert-farbe/, id)
assert.doesNotMatch(mail.text, DU_FORM, `${id}: Sie-Form, nicht Du\n${mail.text}`)
}
}
})
const G_WERTE = { bestellnummer: '26-K7QM2X', betrieb_name: 'Töpferei ', farbe: '#112233', anbieter_zeile: 'Töpferei Muster, Hauptstr. 1, 12345 Musterstadt, info@example.com', impressum_url: 'https://shop.example/impressum', datenschutz_url: 'https://shop.example/datenschutz', widerruf_url: 'https://project.webklar.com/shop/sk/widerruf' }
const g = (id, werte, blocks = {}) => renderMail(id, { ...G_WERTE, ...werte }, blocks)
const G09_WERTE = {
datum: '05.10.2026',
positionen: '2 × Tasse (blau) – 24,00 €\n1 × Teller – 12,50 €',
zwischensumme: '36,50 €',
versand: '4,90 €',
gesamt: '41,40 €',
steuer_hinweis: 'Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.',
lieferart_text: 'Versand nach Deutschland',
lieferadresse: 'Erika Beispiel\nMusterweg 3\n12345 Musterstadt',
abholort: 'Werkstatt, Hauptstr. 1',
lieferzeit: '2–4 Werktage',
zahlungsart: 'Kreditkarte',
ausschluss_text: 'Teller: Vom Widerruf ausgeschlossen: nach Kundenwunsch angefertigt',
agb: 'AGB-Text Zeile 1\nAGB-Text Zeile 2',
widerrufsbelehrung: 'Widerrufsbelehrung Zeile 1\nZeile 2',
widerrufsformular: 'An Töpferei Muster\nHiermit widerrufe ich',
gewaehrleistung: 'GESETZLICHE GEWÄHRLEISTUNG\n\nMindestens zwei Jahre',
}
test('G09 Versand: Betreff mit Bestellnummer und Shop, Positionen, Summen, Lieferadresse, alle Rechtstexte, Links', () => {
const mail = g('G09', G09_WERTE, { versand: true, abholung: false, ausschluss: true })
assert.deepEqual(mail.problems, [])
assert.equal(mail.subject, 'Ihre Bestellung 26-K7QM2X bei Töpferei ')
assert.match(mail.html, /Töpferei <Muster>/, 'Shopname maskiert')
assert.match(mail.html, /2 × Tasse \(blau\) – 24,00 € 1 × Teller – 12,50 €/)
assert.match(mail.html, /Erika Beispiel Musterweg 3 12345 Musterstadt/)
for (const teil of ['26-K7QM2X', '05.10.2026', '36,50 €', '4,90 €', '41,40 €', '§ 19 UStG', 'Versand nach Deutschland', '2–4 Werktage', 'Kreditkarte', 'Vom Widerruf ausgeschlossen: nach Kundenwunsch angefertigt', 'AGB-Text Zeile 2', 'Widerrufsbelehrung Zeile 1', 'Hiermit widerrufe ich', 'GESETZLICHE GEWÄHRLEISTUNG', 'Hinweis zur gesetzlichen Gewährleistung', 'Muster-Widerrufsformular', 'Allgemeine Geschäftsbedingungen']) {
assert.ok(mail.text.includes(teil), teil)
}
assert.match(mail.text, /Lieferadresse: Erika Beispiel/)
assert.doesNotMatch(mail.text, /Abholort|Werkstatt, Hauptstr/)
assert.match(mail.html, /href="https:\/\/project\.webklar\.com\/shop\/sk\/widerruf"/)
})
test('G09 Abholung ohne Ausschluss: Abholort statt Lieferadresse, kein Widerrufs-Ausschluss', () => {
const mail = g('G09', { ...G09_WERTE, lieferart_text: 'Abholung', versand: 'Abholung – kostenlos' }, { versand: false, abholung: true, ausschluss: false })
assert.deepEqual(mail.problems, [])
assert.match(mail.text, /Abholort: Werkstatt, Hauptstr\. 1/)
assert.doesNotMatch(mail.text, /Lieferadresse|Musterweg|Hinweis zum Widerrufsrecht|ausgeschlossen/)
assert.match(mail.text, /Abholung – kostenlos/)
})
test('G10: Sendungsnummer und Versanddienst nur im Block sendung', () => {
const mit = g('G10', { sendungsnummer: '0034<1>', versanddienst: 'DHL' }, { sendung: true })
assert.deepEqual(mit.problems, [])
assert.equal(mit.subject, 'Ihre Bestellung 26-K7QM2X wurde versendet')
assert.match(mit.text, /Versanddienst: DHL/)
assert.match(mit.text, /Sendungsnummer: 0034<1>/)
assert.match(mit.html, /0034<1>/)
const ohne = g('G10', { sendungsnummer: '', versanddienst: '' }, {})
assert.deepEqual(ohne.problems, [])
assert.doesNotMatch(ohne.text, /Sendungsnummer|Versanddienst/)
assert.match(ohne.text, /versendet/)
})
test('G11: Abholort und Hinweis', () => {
const mail = g('G11', { abholort: 'Werkstatt, Hauptstr. 1', hinweis: 'Di–Fr 10–16 Uhr' })
assert.deepEqual(mail.problems, [])
assert.equal(mail.subject, 'Ihre Bestellung 26-K7QM2X ist abholbereit')
assert.match(mail.text, /Abholort: Werkstatt, Hauptstr\. 1/)
assert.match(mail.text, /Hinweis: Di–Fr 10–16 Uhr/)
})
test('G12: Eingang mit Datum und Uhrzeit, Erklärung nur im Block nachricht, Händler meldet sich', () => {
const werte = { name: 'Erika ', bestellnummer: '26-ZZZZZZ', eingang: '05.10.2026, 14:03 Uhr', nachricht: 'Bitte stornieren.\nDanke' }
const mit = g('G12', werte, { nachricht: true })
assert.deepEqual(mit.problems, [])
assert.equal(mit.subject, 'Eingangsbestätigung Ihres Widerrufs bei Töpferei ')
assert.match(mit.text, /Guten Tag Erika ,/)
assert.match(mit.text, /Eingegangen am: 05\.10\.2026, 14:03 Uhr/)
assert.match(mit.text, /Bestellnummer: 26-ZZZZZZ/)
assert.match(mit.html, /Bitte stornieren\. Danke/)
assert.match(mit.text, /meldet sich bei Ihnen/)
const ohne = g('G12', werte, {})
assert.doesNotMatch(ohne.text, /Bitte stornieren|Ihre Erklärung:/)
})
test('G13: Betrag, Grund nur im Block, Gutschrift über die ursprüngliche Zahlungsart', () => {
const mit = g('G13', { betrag: '12,50 €', grund: 'Artikel beschädigt' }, { grund: true })
assert.deepEqual(mit.problems, [])
assert.equal(mit.subject, 'Erstattung zu Ihrer Bestellung 26-K7QM2X')
assert.match(mit.text, /Erstatteter Betrag: 12,50 €/)
assert.match(mit.text, /Grund: Artikel beschädigt/)
assert.match(mit.text, /Die Gutschrift erfolgt über die ursprüngliche Zahlungsart\./)
assert.doesNotMatch(g('G13', { betrag: '12,50 €', grund: 'x' }, {}).text, /Grund:/)
})
test('G14: nichts berechnet, kein Betrag', () => {
const mail = g('G14', {})
assert.deepEqual(mail.problems, [])
assert.equal(mail.subject, 'Ihre Bestellung 26-K7QM2X wurde nicht abgeschlossen')
assert.match(mail.text, /wurde nicht abgeschlossen; es wurde nichts berechnet\./)
})
const K25_WERTE = { kunde_name: 'Erika', shopname: 'Töpferei', bestellnummer: '26-K7QM2X', gesamt: '41,40 €', positionen: '2 × Tasse – 24,00 €\n1 × Teller – 12,50 €', lieferart_text: 'Versand nach Deutschland', kaeufer_name: 'Max ', portal_url: 'https://portal.example/dashboard.html#webshop?projekt=p1' }
test('K25: Betreff, Platzhalter, Blöcke; Bestand-Warnung und Lastschrift-Hinweis nur im Block', () => {
const tpl = loadTemplate('K25')
assert.equal(tpl.file, 'K25 Neue Bestellung.html')
assert.equal(tpl.subject, 'Neue Bestellung {{bestellnummer}} in Deinem Webshop')
assert.deepEqual([...tpl.placeholders].sort(), Object.keys(K25_WERTE).sort())
assert.deepEqual(tpl.blocks, ['ueberverkauft', 'sepa'])
const normal = renderMail('K25', K25_WERTE, {})
assert.deepEqual(normal.problems, [])
assert.equal(normal.subject, 'Neue Bestellung 26-K7QM2X in Deinem Webshop')
assert.match(normal.text, /Hallo Erika/)
assert.match(normal.text, /Käufer: Max /)
assert.match(normal.html, /2 × Tasse – 24,00 € 1 × Teller – 12,50 €/)
assert.match(normal.html, /href="https:\/\/portal\.example\/dashboard\.html#webshop\?projekt=p1"/)
assert.doesNotMatch(normal.text, /Achtung|Lastschrift/)
const voll = renderMail('K25', K25_WERTE, { ueberverkauft: true, sepa: true })
assert.match(voll.text, /Achtung: Bestand reichte nicht/)
assert.match(voll.text, /Zahlung per Lastschrift/)
assert.doesNotMatch(renderMail('K25', K25_WERTE, { sepa: true }).text, /Achtung/)
assert.doesNotMatch(renderMail('K25', K25_WERTE, { ueberverkauft: true }).text, /Lastschrift/)
})
const K26_WERTE = { kunde_name: 'Erika', shopname: 'Töpferei', bestellnummer: '26-K7QM2X', kaeufer_name: 'Max ', kaeufer_email: 'max@example.com', eingang: '05.10.2026, 14:03 Uhr', nachricht: 'Ich widerrufe.\nBitte melden', portal_url: 'https://portal.example/dashboard.html#webshop?projekt=p1' }
test('K26: Betreff, Platzhalter, Erklärung nur im Block, Hinweis auf die 14 Tage', () => {
const tpl = loadTemplate('K26')
assert.equal(tpl.file, 'K26 Widerruf im Webshop.html')
assert.equal(tpl.subject, 'Widerruf in Deinem Webshop eingegangen')
assert.deepEqual([...tpl.placeholders].sort(), Object.keys(K26_WERTE).sort())
assert.deepEqual(tpl.blocks, ['nachricht'])
const mit = renderMail('K26', K26_WERTE, { nachricht: true })
assert.deepEqual(mit.problems, [])
assert.match(mit.text, /Hallo Erika/)
assert.match(mit.text, /Eingegangen am: 05\.10\.2026, 14:03 Uhr/)
assert.match(mit.text, /E-Mail: max@example\.com/)
assert.match(mit.html, /Ich widerrufe\. Bitte melden/)
assert.match(mit.text, /innerhalb von 14 Tagen/)
assert.doesNotMatch(renderMail('K26', K26_WERTE, {}).text, /Ich widerrufe|Erklärung:/)
})
|