152 lines
10 KiB
JavaScript
152 lines
10 KiB
JavaScript
import { test } from 'node:test'
|
||
import assert from 'node:assert/strict'
|
||
import { readFileSync } from 'node:fs'
|
||
import { zeigeSpeicher } from '../public/neu/ansicht-speicher.js'
|
||
import { nimmChatVorgabe } from '../public/neu/ansicht-hilfen.js'
|
||
import { klartext } from '../server/services/bestelltexte.js'
|
||
import { mit, me, abo, aboAntwort, neueWurzel, VORSCHAU_SPEICHER } from './support/knDaten.js'
|
||
import { NEU, TERMS_URL, meMit, aboNeu } from './support/knBestellDaten.js'
|
||
|
||
// E-Mail-Speicher im schlanken Muster (Spec 5.6, Fassung 6): Plus mit Block, Linkzeile, Knopf und Kundenart-Zeile ohne Häkchen,
|
||
// Minus mit genau einer Warnzeile und dem Knopf „Speichern“
|
||
const speicher = (o = {}, sub = {}) => aboNeu({ subscription: abo({ storage: { units: 2, pendingSetup: false, pendingReduction: null }, canChangeStorage: true, ...sub }), ...o })
|
||
const PLUS = { direction: 'up', units: 3, totalGb: 20, monthlyCents: 300, deltaMonthlyCents: 100, prorationCents: 40, nextChargeAt: '2026-11-16T00:00:00.000Z', effectiveAt: null }
|
||
const IDS = ['storage-block', 'storage-linkzeile', 'storage-warnung', 'storage-error', 'storage-submit', 'storage-kundenart']
|
||
const reihe = (fx, w) => fx.alle(fx.finde(w, '[data-panel="speicher"]'), (el) => IDS.includes(el.id)).map((el) => el.id)
|
||
const zeigeNeu = async (fx, daten, m = meMit('unternehmen')) => {
|
||
const w = neueWurzel(fx)
|
||
await zeigeSpeicher(w, { route: { bereich: 'vertraege', reiter: 'email', query: '' }, me: m, nav: 'tab', daten })
|
||
await fx.ruhe()
|
||
return w
|
||
}
|
||
|
||
test('Plus: Block, Linkzeile, Knopf, Kundenart-Zeile, kein Häkchen, Body ohne Kundenart', mit({ 'POST /api/abo/storage/preview': PLUS, 'POST /api/abo/storage': { change: { task: true }, subscription: abo() }, 'GET /api/abo': speicher() }, async (fx) => {
|
||
const w = await zeigeNeu(fx, speicher(), meMit('unternehmen'))
|
||
await fx.klick(fx.finde(w, '#storage-plus'))
|
||
await fx.ruhe()
|
||
assert.deepEqual(reihe(fx, w), ['storage-block', 'storage-linkzeile', 'storage-error', 'storage-submit', 'storage-kundenart'])
|
||
assert.deepEqual([fx.finde(w, '#storage-terms'), fx.finde(w, '#storage-submit').textContent, fx.finde(w, '#storage-warnung')], [null, 'Zahlungspflichtig bestellen', null])
|
||
assert.equal(fx.alle(w, 'input[type="checkbox"], details, dialog').length, 0)
|
||
assert.equal(fx.finde(w, '#storage-linkzeile a').getAttribute('href'), `${TERMS_URL}#zusatzspeicher`)
|
||
const zeilen = fx.alle(fx.finde(w, '#storage-block'), 'dt').map((e) => fx.text(e))
|
||
assert.deepEqual(zeilen, ['Neue Gesamtgröße', 'Preis', 'Zusätzlich jetzt', 'Nächste Abbuchung', 'Einrichtung', 'Laufzeit'])
|
||
assert.equal(fx.text(fx.finde(w, '#storage-kundenart')), 'Du bestellst als Betrieb · Stimmt nicht? Schreib uns im Chat')
|
||
await fx.klick(fx.finde(w, '#storage-submit'))
|
||
await fx.ruhe()
|
||
assert.deepEqual(fx.fetchLog.find((a) => a.url === '/api/abo/storage').body, { units: 3, acceptTerms: true, termsVersion: NEU })
|
||
assert.match(fx.text(w), /Danke, deine Bestellung ist eingegangen\. Der Speicher wird in der Regel innerhalb eines Werktags eingerichtet\./)
|
||
}))
|
||
|
||
test('Minus: genau eine Warnzeile aus dem Wortlaut, Knopf „Speichern“, Body nur mit units', mit({ 'POST /api/abo/storage/preview': VORSCHAU_SPEICHER, 'POST /api/abo/storage': { change: { effectiveAt: '2026-11-01T00:00:00.000Z' }, subscription: abo() } }, async (fx) => {
|
||
const w = await zeigeNeu(fx, speicher(), meMit('privat'))
|
||
await fx.klick(fx.finde(w, '#storage-minus'))
|
||
await fx.ruhe()
|
||
assert.equal(fx.alle(w, '#storage-warnung').length, 1)
|
||
assert.equal(fx.text(fx.finde(w, '#storage-warnung')), klartext(speicher().bestelltexte.speicher.warnung.text))
|
||
assert.equal(fx.finde(w, '#storage-warnung a').getAttribute('href'), `${TERMS_URL}#zusatzspeicher`)
|
||
assert.deepEqual([fx.finde(w, '#storage-linkzeile'), fx.finde(w, '#storage-kundenart'), fx.finde(w, '#storage-submit').textContent], [null, null, 'Speichern'])
|
||
assert.deepEqual(reihe(fx, w), ['storage-block', 'storage-warnung', 'storage-error', 'storage-submit'])
|
||
await fx.klick(fx.finde(w, '#storage-submit'))
|
||
await fx.ruhe()
|
||
assert.deepEqual(fx.fetchLog.find((a) => a.url === '/api/abo/storage').body, { units: 1 })
|
||
assert.match(fx.text(w), /Gespeichert\. Die Verkleinerung wirkt zum 01\.11\.2026\./)
|
||
}))
|
||
|
||
test('Privat und leer: Kundenart-Zeile nennt privat; Ansichtsmodus sperrt Stepper und Knöpfe', mit({ 'POST /api/abo/storage/preview': PLUS }, async (fx) => {
|
||
for (const m of [meMit('privat'), meMit('')]) {
|
||
const w = await zeigeNeu(fx, speicher(), m)
|
||
await fx.klick(fx.finde(w, '#storage-plus'))
|
||
await fx.ruhe()
|
||
assert.ok(fx.text(fx.finde(w, '#storage-kundenart')).startsWith('Du bestellst privat · '))
|
||
}
|
||
const v = await zeigeNeu(fx, speicher(), meMit('unternehmen', { viewAs: true }))
|
||
assert.ok(['#storage-minus', '#storage-plus'].every((s) => fx.finde(v, s).hasAttribute('disabled')))
|
||
assert.match(fx.text(v), /Im Ansichtsmodus nicht möglich\./)
|
||
}))
|
||
|
||
test('Ohne Wortlaut: Plus und Minus zeigen nur den Satz, keinen Knopf', mit({ 'POST /api/abo/storage/preview': PLUS }, async (fx) => {
|
||
const w = await zeigeNeu(fx, speicher({ bestelltexte: undefined }), meMit('unternehmen'))
|
||
for (const knopf of ['#storage-plus', '#storage-minus', '#storage-minus']) {
|
||
await fx.klick(fx.finde(w, knopf))
|
||
await fx.ruhe()
|
||
if (fx.finde(w, '#storage-block')) assert.fail('kein Block ohne Wortlaut')
|
||
if (knopf === '#storage-plus') {
|
||
assert.match(fx.text(w), /Die Online-Bestellung wird gerade eingerichtet – schreib uns gern im Chat\./)
|
||
assert.equal(fx.finde(w, '#storage-submit'), null)
|
||
}
|
||
}
|
||
}))
|
||
|
||
test('Unsichere Adressen in Linkzeile und Warnung werden Text', mit({ 'POST /api/abo/storage/preview': PLUS }, async (fx) => {
|
||
const boese = speicher()
|
||
boese.bestelltexte.speicher.linkzeile.urls = ['javascript:alert(1)', '//evil.example']
|
||
const w = await zeigeNeu(fx, boese)
|
||
await fx.klick(fx.finde(w, '#storage-plus'))
|
||
await fx.ruhe()
|
||
assert.equal(fx.alle(w, '#storage-linkzeile a[href]').length, 0)
|
||
}))
|
||
|
||
for (const [name, daten, erwartet] of [
|
||
['gesperrt', speicher({}, { canChangeStorage: false }), /Änderungen sind derzeit nicht möglich – bei Fragen schreib uns gern im Chat\./],
|
||
['gesperrt (gekündigt)', speicher({}, { canChangeStorage: false, cancelAtPeriodEnd: true }), /Änderungen sind derzeit nicht möglich – Abo gekündigt\./],
|
||
['gesperrt (Wechsel)', speicher({}, { canChangeStorage: false, switchPending: { type: 'wechsel' } }), /Änderungen sind derzeit nicht möglich – Wechsel wartet auf die Zahlung\./],
|
||
['gesperrt (Zahlung)', speicher({}, { canChangeStorage: false, paymentProblem: true }), /Änderungen sind derzeit nicht möglich – Zahlung offen\./],
|
||
['wird eingerichtet', speicher({}, { storage: { units: 1, pendingSetup: true, pendingReduction: null } }), /Dein zusätzlicher Speicher wird eingerichtet\. Das dauert in der Regel bis zum nächsten Werktag\./],
|
||
['Reduzierung geplant', speicher({}, { storage: { units: 3, pendingSetup: false, pendingReduction: { units: 1, effectiveAt: '2026-11-01T00:00:00.000Z' } } }), /Geplant: ab 01\.11\. 10 GB\./],
|
||
]) {
|
||
test(`E-Mail-Speicher (${name}): Texte in du`, mit({ 'POST /api/abo/storage/preview': VORSCHAU_SPEICHER }, async (fx) => {
|
||
const w = await zeigeNeu(fx, daten)
|
||
assert.match(fx.text(fx.finde(w, '[data-panel="speicher"]')), erwartet)
|
||
assert.doesNotMatch(fx.text(w), /\b(Sie|Ihr(e[nmrs]?)?|Ihnen)\b/)
|
||
}))
|
||
}
|
||
|
||
test('409 hängt den Hinweis zum Neuladen an', mit({ 'POST /api/abo/storage/preview': VORSCHAU_SPEICHER, 'POST /api/abo/storage': () => ({ status: 409, body: { error: 'Stand geändert.' } }) }, async (fx) => {
|
||
const w = await zeigeNeu(fx, speicher())
|
||
await fx.klick(fx.finde(w, '#storage-minus'))
|
||
await fx.ruhe()
|
||
await fx.klick(fx.finde(w, '#storage-submit'))
|
||
await fx.ruhe()
|
||
assert.match(fx.text(w), /Stand geändert\. Bitte lade die Seite neu, falls sich der Stand geändert hat\./)
|
||
}))
|
||
|
||
test('Postfach anfragen setzt die Chat-Vorgabe und geht zum Chat', mit({}, async (fx) => {
|
||
fx.window.history.pushState = (_s, _t, url) => { fx.window.location.hash = url }
|
||
const w = await zeigeNeu(fx, speicher())
|
||
await fx.klick(fx.finde(w, (e) => e.textContent === 'Postfach anfragen'))
|
||
assert.equal(nimmChatVorgabe(), 'Bitte lege ein neues Postfach an: ')
|
||
assert.equal(fx.window.location.hash, '#chat')
|
||
}))
|
||
|
||
test('Speicher: ohne Daten wird das Abo geladen, Ausfall und fehlendes Abo sagen es', mit({ 'GET /api/abo': () => ({ status: 500, body: { error: 'x' } }) }, async (fx) => {
|
||
const w = neueWurzel(fx)
|
||
await zeigeSpeicher(w, { route: { bereich: 'vertraege', query: '' }, me: me(), nav: 'tab' })
|
||
await fx.ruhe()
|
||
assert.match(fx.text(w), /Deine Verträge konnten gerade nicht geladen werden\./)
|
||
const ohne = await zeigeNeu(fx, aboAntwort())
|
||
assert.equal(fx.finde(ohne, '[data-panel="speicher"]'), null)
|
||
assert.match(fx.text(ohne), /Den E-Mail-Speicher gibt es mit einem laufenden Abo\./)
|
||
}))
|
||
|
||
test('Ansichtsmodus schickt nichts, ein Vorschau-Fehler sperrt den Knopf', mit({ 'POST /api/abo/storage/preview': () => ({ status: 500, body: { error: 'Vorschau kaputt' } }), 'POST /api/abo/storage': {} }, async (fx) => {
|
||
const w = await zeigeNeu(fx, speicher())
|
||
await fx.klick(fx.finde(w, '#storage-plus'))
|
||
await fx.ruhe()
|
||
assert.match(fx.text(w), /Vorschau kaputt/)
|
||
assert.equal(fx.finde(w, '#storage-submit').hasAttribute('disabled'), true)
|
||
const vorher = fx.fetchLog.length
|
||
const v = await zeigeNeu(fx, speicher(), meMit('unternehmen', { viewAs: true }))
|
||
await fx.klick(fx.finde(v, '#storage-plus'))
|
||
await fx.ruhe()
|
||
assert.deepEqual(fx.fetchLog.slice(vorher), []) // im Ansichtsmodus geht nichts raus, auch keine Vorschau
|
||
}))
|
||
|
||
test('Quelltext: kein Sie, keine Klappe, kein Häkchen, Body ohne kundenart', () => {
|
||
const q = readFileSync(new URL('../public/neu/ansicht-speicher.js', import.meta.url), 'utf8').replace(/\/\*[\s\S]*?\*\//g, '')
|
||
assert.doesNotMatch(q, /\b(Sie|Ihr(e[nmrs]?)?|Ihnen)\b/)
|
||
assert.doesNotMatch(q, /details|dialog|innerHTML|storage-terms|checkbox/)
|
||
const aufruf = q.slice(q.indexOf('const body = '), q.indexOf("api('/api/abo/storage'"))
|
||
assert.ok(aufruf.includes('acceptTerms: true'))
|
||
assert.doesNotMatch(aufruf, /kundenart|unternehmer|startChoice/)
|
||
})
|