Nach "Zahlungspflichtig bestellen" setzt die Seite den Knopf auf "Weiter zur Zahlung…" (gesperrt) und leitet zu Stripe. Bricht der Kunde per Zurück-Wischgeste ab, stellt Safari die Seite aus dem Bfcache her: der Knopf blieb gesperrt, ein neuer Versuch ging nur nach Neuladen. Nur ansicht-wechsel.js hatte einen pageshow-Handler. Neuer Helfer beiRueckkehr(el, fn) in ansicht-hilfen.js (pageshow mit persisted, entfernt sich, sobald das Element nicht mehr im Dokument steht); genutzt von der Bestellseite und von "Zahlungsart aktualisieren" (Verträge). Speicher und Gutschrift leiten nicht zu Stripe, dort ist nichts zu tun. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
57 lines
6.9 KiB
JavaScript
57 lines
6.9 KiB
JavaScript
import { test } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { zeigeAbo, aboZustand, aboZeile, laufzeitText } from '../public/neu/vertraege-abo.js'
|
|
import { mit, me, abo, aboAntwort, angebot, neueWurzel } from './support/knDaten.js'
|
|
|
|
const WARTUNG = aboAntwort({ subscription: abo({ priceAmount: 5000, currentPeriodEnd: '2026-11-16T00:00:00.000Z', minTermEnd: '2099-06-01T00:00:00.000Z', termsUrl: '/abo-bedingungen.html', termsPdfUrl: '/WEBklar-Abo-Vertragsunterlagen.pdf' }) })
|
|
const zeige = async (fx, daten, m = me()) => { const b = neueWurzel(fx); await zeigeAbo(b, { daten, me: m }); await fx.ruhe(); return b }
|
|
const SRV = { 'GET /api/abo/takeover-credits': { requests: [{ status: 'rueckfrage' }, { status: 'offen' }], limits: {} } }
|
|
test('aboZustand', () => {
|
|
assert.deepEqual([aboAntwort(), aboAntwort({ subscription: abo({ active: false }) }), WARTUNG, aboAntwort({ subscription: abo({ cancelAtPeriodEnd: true }) })].map(aboZustand), ['ohne', 'beendet', 'laeuft', 'gekuendigt'])
|
|
})
|
|
test('Abo Wartung: Vertrag, Preis, Laufzeit, Bedingungen, Gutschrift-Einstieg mit Rückfragen', mit(SRV, async (fx) => {
|
|
const b = await zeige(fx, WARTUNG); const t = fx.text(b)
|
|
for (const s of ['Mein Vertrag', 'aktiv', 'Wartungs-Abo', 'Gemäß § 19 UStG ohne Umsatzsteuer.', 'nächste Abbuchung 16.11.2026', 'Mindestlaufzeit bis 01.06.2099. Kündigung frühestens zu diesem Termin, mit einem Monat Frist.']) assert.ok(t.includes(s), s)
|
|
assert.equal(fx.finde(b, 'a[href="/WEBklar-Abo-Vertragsunterlagen.pdf"]').textContent, 'als PDF')
|
|
assert.equal(fx.finde(b, 'a[href="#gutschrift"][data-view="takeover"] [data-badge]').textContent, '1')
|
|
}))
|
|
test('Abo Hosting: Wechsel-Einstieg; wartende Zahlung nur mit sicherer Adresse; gekündigt ohne Wechsel-Karte', mit(SRV, async (fx) => {
|
|
const hosting = (o) => aboAntwort({ subscription: abo({ produkt: 'hosting', canSwitch: true, ...o }) })
|
|
const w = fx.finde(await zeige(fx, hosting({})), 'a[data-view="wechsel"]'); assert.deepEqual([w.textContent, w.getAttribute('href')], ['Zum Wartungs-Abo wechseln', '#wechsel'])
|
|
const sicher = await zeige(fx, hosting({ switchPending: { since: 'x', paymentUrl: 'https://pay.stripe.com/x' } })); assert.equal(fx.finde(sicher, 'a[href="https://pay.stripe.com/x"]').textContent, 'Jetzt bezahlen')
|
|
const unsicher = await zeige(fx, hosting({ switchPending: { since: 'x', paymentUrl: 'javascript:alert(1)' } })); assert.match(fx.text(unsicher), /Schreib uns gern im Chat, wenn du Hilfe brauchst\./)
|
|
const gekuendigt = await zeige(fx, hosting({ cancelAtPeriodEnd: true })); assert.equal(fx.finde(gekuendigt, 'a[data-view="wechsel"]'), null); assert.match(fx.text(gekuendigt), /Dein Abo ist gekündigt\. Bitte schreib uns im Chat, wenn du zum Wartungs-Abo wechseln möchtest\./)
|
|
}))
|
|
test('Zahlungsproblem: Knopf öffnet das Stripe-Portal; Widerruf nur in der Frist; Ansichtsmodus sperrt', mit({ ...SRV, 'POST /api/abo/portal': { url: 'https://billing.stripe.com/x' } }, async (fx) => {
|
|
const daten = aboAntwort({ subscription: abo({ paymentProblem: true, withdrawalOpen: true, withdrawalUntil: '2026-10-17T21:59:00.000Z' }) })
|
|
const b = await zeige(fx, daten); assert.match(fx.text(b), /Bitte aktualisiere deine Zahlungsart, damit deine Website online bleibt\./); assert.match(fx.finde(b, 'a[href="/widerrufen.html"]').textContent, /^Vertrag widerrufen · bis 17\.10\./)
|
|
await fx.klick(fx.finde(b, (e) => e.textContent === 'Zahlungsart aktualisieren')); assert.equal(fx.window.location.href, 'https://billing.stripe.com/x')
|
|
const portal = fx.finde(b, (e) => e.textContent === 'Zahlungsart aktualisieren'); assert.equal(portal.hasAttribute('disabled'), true, 'während der Weiterleitung gesperrt')
|
|
fx.window.dispatchEvent({ type: 'pageshow', persisted: true }); assert.equal(portal.hasAttribute('disabled'), false, 'M6: zurück von Stripe wieder bedienbar')
|
|
const ansicht = await zeige(fx, daten, me({ viewAs: true })); assert.equal(fx.finde(ansicht, (e) => e.textContent === 'Zahlungsart aktualisieren').hasAttribute('disabled'), true)
|
|
assert.equal(fx.finde(await zeige(fx, WARTUNG), 'a[href="/widerrufen.html"]'), null)
|
|
}))
|
|
test('Ohne Abo: zwei Produkte mit Link zur Bestellung, nicht verfügbar sperrt, beendet sagt es', mit(SRV, async (fx) => {
|
|
const b = await zeige(fx, aboAntwort()); assert.deepEqual(['hosting', 'wartung'].map((k) => fx.finde(b, `[data-produkt="${k}"] a`).getAttribute('href')), ['#bestellen-hosting', '#bestellen-wartung'])
|
|
assert.match(fx.text(b), /20,00 €/)
|
|
const aus = await zeige(fx, aboAntwort({ available: false })); assert.equal(fx.finde(aus, '[data-produkt="hosting"] a').getAttribute('aria-disabled'), 'true'); assert.match(fx.text(aus), /schreib uns gern im Chat\./)
|
|
assert.match(fx.text(await zeige(fx, aboAntwort({ subscription: abo({ active: false }) }))), /Dein bisheriges Abo ist beendet\. Du kannst jederzeit ein neues abschließen\./)
|
|
}))
|
|
test('Abo: Ausfall der Rückfragen-Abfrage lässt den Einstieg stehen', mit({ 'GET /api/abo/takeover-credits': () => ({ status: 500, body: { error: 'x' } }) }, async (fx) => {
|
|
const b = await zeige(fx, WARTUNG); assert.ok(fx.finde(b, 'a[href="#gutschrift"]')); assert.equal(fx.finde(b, '[data-badge]'), null)
|
|
}))
|
|
test('Texte aus app.js: Zeile und Laufzeit', () => {
|
|
assert.equal(aboZeile(abo({ cancelAtPeriodEnd: true, cancelAt: '2026-12-01T00:00:00.000Z' })), 'Leistungen bis 01.12.2026')
|
|
assert.equal(aboZeile(abo({ serviceStartsAt: '2026-10-30T00:00:00.000Z' })), 'Beginn und erste Abbuchung am 30.10.2026')
|
|
assert.equal(laufzeitText(abo({ cancelAtPeriodEnd: true, currentPeriodEnd: '2026-12-01T00:00:00.000Z' })), 'Gekündigt zum 01.12.2026. Bis dahin bleiben alle Leistungen verfügbar.')
|
|
assert.equal(laufzeitText(abo({ minTermEnd: '2000-01-01T00:00:00.000Z' })), 'Monatlich kündbar zum Ende des Abrechnungsmonats.')
|
|
})
|
|
test('Ohne Abo: Angebot mit Etikett und Gültigkeit; Fußsatz mit Link; Zusatzspeicher und Beginn nach Frist beim laufenden Abo', mit(SRV, async (fx) => {
|
|
const b = await zeige(fx, aboAntwort({ offers: { hosting: null, wartung: angebot({ text: 'Erste 2 Monate je 25,00 €', validUntil: '2026-11-30T00:00:00.000Z' }) } }))
|
|
assert.match(fx.text(fx.finde(b, '[data-produkt="wartung"]')), /Dein persönliches AngebotErste 2 Monate je 25,00 € · gültig bis 30\.11\.2026/)
|
|
assert.doesNotMatch(fx.text(fx.finde(b, '[data-produkt="hosting"]')), /Dein persönliches Angebot/)
|
|
assert.equal(fx.finde(b, 'a[href="/abo-bedingungen.html"]').textContent, 'Vertragsbedingungen'); assert.match(fx.text(b), /Alle Preise gemäß § 19 UStG ohne Umsatzsteuer\. Es gelten die Vertragsbedingungen\./)
|
|
const l = await zeige(fx, aboAntwort({ subscription: abo({ storage: { units: 2, pendingSetup: false, pendingReduction: null }, serviceStartsAt: '2026-10-30T00:00:00.000Z' }) }))
|
|
assert.match(fx.text(l), /E-Mail-Speicher15 GB \(2 Zusatzpakete\)/); assert.match(fx.text(l), /Wie gewählt beginnen wir nach Ablauf der Widerrufsfrist am 30\.10\.2026\. Dann wird auch zum ersten Mal abgebucht\./)
|
|
}))
|