Öffnungszeiten: Behalten nur ohne Abo anbieten (kein_abo)
Behalten (30 €) gab es bei jedem inklusive false. nicht_aktiv und gekuendigt heißen aber, dass das Wartungs-Abo läuft (Server: 409 Abo läuft wieder). Jetzt gilt an allen fünf Stellen (Kachel, Karte, Behalten-Seite, Weg aus der Kachel, Abgeschaltet-Karte) dasselbe Prädikat: inklusive false UND grund kein_abo; sonst „Abo läuft wieder“ ohne Bezahlknopf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7bb918390a
commit
cee44d6852
5 files changed
+166
-20
No files matched your search
+21
-5
@@ -267,6 +267,17 @@ function zeitenFrist(aboEndeAt) {
|
||||
return new Date(Date.UTC(y, m - 1, d + 30)).toLocaleDateString('de-DE', { timeZone: 'UTC', day: '2-digit', month: '2-digit', year: 'numeric' })
|
||||
}
|
||||
|
||||
// Behalten (30 €) ist nur bezahlbar, wenn das Wartungs-Abo wirklich beendet ist: inklusive false UND grund kein_abo (Z12, R20, R25). nicht_aktiv und
|
||||
// gekuendigt heissen, dass das Abo laeuft (Probezeit/Zahlung offen bzw. Kuendigung vorgemerkt): 'abo', nie ein Bezahlknopf. Doppelt zu behaltenLage in
|
||||
// zeiten-bestellen.js (funktionen.js importiert dort nichts); beide pinnen die Tests. 'bezahlen' | 'abo' | 'inklusive' | 'unklar' (Abo nicht lesbar)
|
||||
function zeitenBehaltenLage(angebot) {
|
||||
if (angebot.inklusive === true) return 'inklusive'
|
||||
if (angebot.inklusive !== false) return 'unklar'
|
||||
if (angebot.grund === 'kein_abo') return 'bezahlen'
|
||||
return angebot.grund === 'nicht_aktiv' || angebot.grund === 'gekuendigt' ? 'abo' : 'unklar'
|
||||
}
|
||||
const ZEITEN_ABO_WIEDER = 'Dein Wartungs-Abo läuft wieder – Öffnungszeiten live ist darin enthalten.'
|
||||
|
||||
// 'JJJJ-MM-TT' als TT.MM.JJJJ, sonst ''
|
||||
const zeitenTag = (iso) => {
|
||||
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(typeof iso === 'string' ? iso : '')
|
||||
@@ -296,11 +307,14 @@ function zeitenTile(projects, angebot = {}) {
|
||||
}
|
||||
if (target.zeitenLeer === true) extra += `<p>${aktiv ? 'Trag jetzt deine Zeiten ein.' : 'Trag jetzt deine Zeiten ein – erst dann kann das Team die Tabelle einbauen.'}</p>`
|
||||
action = knopf('Zeiten bearbeiten')
|
||||
// Wartungs-Abo beendet (aboEndeAt): fuer einmalig 30 € behalten; nur wenn sicher ist, dass das Abo nicht wieder laeuft (inklusive false)
|
||||
const frist = f.aboEndeAt && angebot.inklusive === false ? zeitenFrist(f.aboEndeAt) : ''
|
||||
// Wartungs-Abo beendet (aboEndeAt): fuer einmalig 30 € behalten, aber nur ohne Abo (kein_abo); laeuft es (nicht_aktiv, gekuendigt), steht der Hinweis ohne Knopf da
|
||||
const lage = f.aboEndeAt ? zeitenBehaltenLage(angebot) : ''
|
||||
const frist = lage === 'bezahlen' ? zeitenFrist(f.aboEndeAt) : ''
|
||||
if (frist) {
|
||||
extra += `<p class="fn-count">Dein Wartungs-Abo ist beendet – behalte die Funktion bis ${frist} für einmalig ${esc(preis)}.</p>`
|
||||
action += knopf(`Für ${esc(preis)} behalten`, { weg: 'behalten', klasse: 'kb-btn-outline' })
|
||||
} else if (lage === 'abo') {
|
||||
extra += `<p class="fn-count">${ZEITEN_ABO_WIEDER}</p>`
|
||||
}
|
||||
} else if (f.state === 'ordered') {
|
||||
pill = ['Zahlung offen', 'setup']
|
||||
@@ -313,9 +327,11 @@ function zeitenTile(projects, angebot = {}) {
|
||||
const daten = aboEnde && target.zeitenLeer === false && !(typeof f.loeschungAm === 'string' && f.loeschungAm < berlinIsoDay())
|
||||
extra = `<p>${aboEnde ? 'Öffnungszeiten live ist abgeschaltet, weil dein Wartungs-Abo beendet ist.' : ZEITEN_ABGESCHALTET}</p>`
|
||||
if (daten && zeitenTag(f.loeschungAm)) extra += `<p class="fn-count">Deine Eingaben bleiben bis ${zeitenTag(f.loeschungAm)} gespeichert.</p>`
|
||||
if (daten && angebot.inklusive === true) extra += '<p class="fn-count">Dein Wartungs-Abo läuft wieder – wir schalten Öffnungszeiten live automatisch wieder ein (meist innerhalb eines Tages).</p>'
|
||||
else if (daten && angebot.inklusive !== false) extra += `<p class="fn-count">${ZEITEN_NICHT_PRUEFBAR}</p>`
|
||||
else action = daten ? knopf(`Für ${esc(preis)} wieder einschalten`, { weg: 'behalten' }) : chat
|
||||
const lage = daten ? zeitenBehaltenLage(angebot) : ''
|
||||
if (!daten) action = chat
|
||||
else if (lage === 'bezahlen') action = knopf(`Für ${esc(preis)} wieder einschalten`, { weg: 'behalten' })
|
||||
else if (lage === 'unklar') extra += `<p class="fn-count">${ZEITEN_NICHT_PRUEFBAR}</p>`
|
||||
else extra += '<p class="fn-count">Dein Wartungs-Abo läuft wieder – wir schalten Öffnungszeiten live automatisch wieder ein (meist innerhalb eines Tages).</p>'
|
||||
} else if (angebot.inklusive === true) {
|
||||
extra = '<p>Im Wartungs-Abo enthalten.</p>'
|
||||
action = knopf('Einschalten')
|
||||
|
||||
@@ -47,6 +47,19 @@ const text = (wert) => (typeof wert === 'string' ? wert : '')
|
||||
const istConsent = (wert) => wert !== null && typeof wert === 'object' && typeof wert.version === 'string' && typeof wert.knopf === 'string' && Array.isArray(wert.items)
|
||||
const chatKnopf = () => h('button', { type: 'button', class: 'kb-btn-outline', 'data-view': 'chat' }, 'Frage dazu? Chat öffnen')
|
||||
|
||||
/**
|
||||
* Wo steht das Wartungs-Abo, wenn das Abo-Ende erkannt ist (Behalten)? 'bezahlen': kein Abo (inklusive false UND grund kein_abo), nur dann gibt es
|
||||
* Behalten gegen 30 €. 'abo': das Abo laeuft (grund nicht_aktiv = Probezeit/Zahlung offen, gekuendigt = Kuendigung vorgemerkt; der Server antwortet
|
||||
* beim Behalten dort mit 409 „Abo läuft wieder“): nie ein Bezahlknopf (Z12, R20, R25). 'inklusive': enthalten (inklusive true). 'unklar': Abo nicht
|
||||
* lesbar oder unbekannter Grund (fail-closed, kein Bezahlknopf auf Verdacht). Doppelt zu zeitenBehaltenLage in funktionen.js (die Kachel importiert nichts).
|
||||
*/
|
||||
export function behaltenLage(angebot) {
|
||||
if (angebot?.inklusive === true) return 'inklusive'
|
||||
if (angebot?.inklusive !== false) return 'unklar'
|
||||
if (angebot.grund === 'kein_abo') return 'bezahlen'
|
||||
return angebot.grund === 'nicht_aktiv' || angebot.grund === 'gekuendigt' ? 'abo' : 'unklar'
|
||||
}
|
||||
|
||||
/** Adresse einer Unterlage: https oder /rechtliches/ (sicherLink) oder eine Datei unter /vertragsunterlagen/ dieses Portals, sonst ''. */
|
||||
function unterlagenLink(url) {
|
||||
const sicher = sicherLink(url)
|
||||
@@ -105,7 +118,11 @@ export function renderZeitenBestellen(root, { ctx = {}, angebot = {}, projekt =
|
||||
if (angebot.inklusive === null || angebot.inklusive === undefined) {
|
||||
return zeige(h('div', { class: 'kb-panel tm-info zt-hinweis', 'data-hinweis': 'nicht-pruefbar', role: 'alert' }, h('p', null, NICHT_PRUEFBAR), nochmal ? h('button', { type: 'button', class: 'kb-btn-outline', onclick: nochmal }, 'Noch einmal versuchen') : null, zurueck))
|
||||
}
|
||||
if (modus === 'behalten' && angebot.inklusive === true) {
|
||||
if (modus === 'behalten' && behaltenLage(angebot) !== 'bezahlen') {
|
||||
// das Abo laeuft (wieder) oder ist nicht sicher lesbar: nichts zu bezahlen, nie ein Bezahlknopf auf Verdacht
|
||||
if (behaltenLage(angebot) === 'unklar') {
|
||||
return zeige(h('div', { class: 'kb-panel tm-info zt-hinweis', 'data-hinweis': 'nicht-pruefbar', role: 'alert' }, h('p', null, NICHT_PRUEFBAR), nochmal ? h('button', { type: 'button', class: 'kb-btn-outline', onclick: nochmal }, 'Noch einmal versuchen') : null, zurueck))
|
||||
}
|
||||
return zeige(h('div', { class: 'kb-panel tm-info zt-hinweis', 'data-hinweis': 'abo-laeuft' }, h('h3', null, 'Dein Wartungs-Abo läuft wieder'), h('p', null, 'Öffnungszeiten live ist darin enthalten – du musst nichts bezahlen.'), zurueck))
|
||||
}
|
||||
if (modus === 'behalten') variante = 'behalten'
|
||||
@@ -265,7 +282,7 @@ function dankeText(danke, feature) {
|
||||
* projekt = Eintrag aus GET /api/funktionen (projectId, zeitenLeer), feature = projekt.features.zeiten (state, einbau, ticketWoid, orderedAt,
|
||||
* invoiceUrl, aboEndeAt, disabledReason, loeschungAm), angebot = zeiten (inklusive, preis), kundenansicht = Sitzung in der Kundenansicht,
|
||||
* danke = 'bestellt' | 'behalten' | 'eingeschaltet' (Rueckkehr von Stripe oder Einschalten), meldung = Text am Knopf.
|
||||
* behalten() oeffnet die Behalten-Seite (Knopf „Für 30 € behalten“ nur, wenn sicher ist, dass das Abo nicht wieder laeuft: inklusive false);
|
||||
* behalten() oeffnet die Behalten-Seite (Knopf „Für 30 € behalten“ nur ohne Abo: behaltenLage 'bezahlen', also inklusive false UND grund kein_abo; laeuft das Abo, steht nur der Hinweis);
|
||||
* weiter({ bestellen, meldung }) wird gerufen, wenn „Zahlung fortsetzen“ scheitert (bestellen: es gibt keine offene Zahlungsseite mehr, die Ansicht
|
||||
* zeigt wieder die Bestellseite).
|
||||
*/
|
||||
@@ -273,7 +290,6 @@ export function zeitenStatusKarte({ projekt, feature, angebot, kundenansicht, da
|
||||
const state = text(feature?.state) || 'off'
|
||||
const [pilleText, pilleArt] = PILLE[state] || PILLE.setup
|
||||
const preis = text(angebot?.preis) || '30 €'
|
||||
const inklusive = angebot?.inklusive
|
||||
const leer = projekt?.zeitenLeer === true
|
||||
const ticket = feature?.ticketWoid
|
||||
const hatTicket = ticket !== null && ticket !== undefined && String(ticket) !== ''
|
||||
@@ -309,17 +325,21 @@ export function zeitenStatusKarte({ projekt, feature, angebot, kundenansicht, da
|
||||
|
||||
// Behalten (30 €): in Einrichtung/aktiv nach dem erkannten Abo-Ende, abgeschaltet solange die Eingaben noch da sind
|
||||
const behaltenKnopf = (beschriftung) => h('button', { type: 'button', class: 'kb-btn-outline', 'data-aktion': 'behalten', disabled: sperre, onclick: () => { if (typeof behalten === 'function') behalten() } }, beschriftung)
|
||||
const behaltenBox = frist && inklusive === false
|
||||
? h('div', { class: 'tm-box tm-box-warn', role: 'note', 'data-hinweis': 'behalten' },
|
||||
const lage = behaltenLage(angebot)
|
||||
let behaltenBox = null
|
||||
if (frist && lage === 'bezahlen') {
|
||||
behaltenBox = h('div', { class: 'tm-box tm-box-warn', role: 'note', 'data-hinweis': 'behalten' },
|
||||
h('span', null, `Dein Wartungs-Abo ist beendet – behalte die Funktion bis ${frist} für einmalig ${preis}.`),
|
||||
behaltenKnopf(`Für ${preis} behalten`))
|
||||
: null
|
||||
} else if (frist && lage === 'abo') {
|
||||
behaltenBox = h('div', { class: 'tm-box tm-box-info', role: 'note', 'data-hinweis': 'abo-laeuft' }, h('span', null, 'Dein Wartungs-Abo läuft wieder – Öffnungszeiten live ist darin enthalten.'))
|
||||
}
|
||||
let abgeschaltetBox = null
|
||||
if (daten && inklusive === false) {
|
||||
if (daten && lage === 'bezahlen') {
|
||||
abgeschaltetBox = h('div', { class: 'tm-box tm-box-warn', role: 'note', 'data-hinweis': 'wieder-einschalten' },
|
||||
h('span', null, `Deine Eingaben bleiben${tagText(feature?.loeschungAm) ? ` bis ${tagText(feature.loeschungAm)}` : ''} gespeichert. Für einmalig ${preis} schaltest du die Funktion wieder ein.`),
|
||||
behaltenKnopf(`Für ${preis} wieder einschalten`))
|
||||
} else if (daten && inklusive === true) {
|
||||
} else if (daten && (lage === 'inklusive' || lage === 'abo')) {
|
||||
abgeschaltetBox = h('div', { class: 'tm-box tm-box-info', role: 'note', 'data-hinweis': 'abo-laeuft' }, h('span', null, 'Dein Wartungs-Abo läuft wieder – wir schalten Öffnungszeiten live automatisch wieder ein (meist innerhalb eines Tages).'))
|
||||
} else if (daten) {
|
||||
abgeschaltetBox = h('div', { class: 'tm-box tm-box-info', role: 'note', 'data-hinweis': 'nicht-pruefbar' }, h('span', null, NICHT_PRUEFBAR))
|
||||
|
||||
+9
-6
@@ -17,7 +17,7 @@
|
||||
Kundenansicht (window.KbView.id() gesetzt): Einschalten, Bestellen und Behalten sind gesperrt, der Server antwortet dort ohnehin mit 403. */
|
||||
|
||||
import { h, infoKarte, request } from './termine-dom.js'
|
||||
import { renderZeitenBestellen, zeitenStatusKarte } from './zeiten-bestellen.js'
|
||||
import { behaltenLage, renderZeitenBestellen, zeitenStatusKarte } from './zeiten-bestellen.js'
|
||||
|
||||
// Auswahl bleibt beim Wechsel zwischen Funktionen und Zeiten erhalten
|
||||
const auswahl = { projectId: '' }
|
||||
@@ -44,10 +44,13 @@ function pickProjekt(projects) {
|
||||
|| projects[0]
|
||||
}
|
||||
|
||||
/** Darf der Kunde die Funktion jetzt behalten (30 €)? Nach dem erkannten Abo-Ende in Einrichtung/aktiv, abgeschaltet nur mit gespeicherten Eingaben. */
|
||||
function behaltenMoeglich(projekt) {
|
||||
/**
|
||||
* Darf der Kunde die Funktion jetzt behalten (30 €)? Nach dem erkannten Abo-Ende in Einrichtung/aktiv, abgeschaltet nur mit gespeicherten Eingaben,
|
||||
* und nur ohne Abo (behaltenLage 'bezahlen': inklusive false UND grund kein_abo); laeuft das Abo (nicht_aktiv, gekuendigt, inklusive), gibt es nichts zu bezahlen.
|
||||
*/
|
||||
function behaltenMoeglich(projekt, angebot) {
|
||||
const feature = projekt?.features?.zeiten
|
||||
if (!feature) return false
|
||||
if (!feature || behaltenLage(angebot) !== 'bezahlen') return false
|
||||
if (feature.state === 'setup' || feature.state === 'active') return Boolean(feature.aboEndeAt)
|
||||
return feature.state === 'disabled' && feature.disabledReason === 'abo_ende' && projekt.zeitenLeer === false
|
||||
}
|
||||
@@ -86,7 +89,7 @@ export async function initZeiten(root, { projectId } = {}) {
|
||||
const key = stand(s.projekt, s.angebot)
|
||||
const editorDa = root.dataset.zeitenKey === key && root.querySelector('.zt-editor') && S?.root === root
|
||||
if (editorDa && !rueckkehr.bestellt && !rueckkehr.behalten) {
|
||||
if (wunschWeg === 'behalten' && behaltenMoeglich(S.projekt)) zeigeBehalten(S)
|
||||
if (wunschWeg === 'behalten' && behaltenMoeglich(S.projekt, S.angebot)) zeigeBehalten(S)
|
||||
return
|
||||
}
|
||||
S = s
|
||||
@@ -199,7 +202,7 @@ function zeichneProjekt(s, { weg = '', meldung = '', bestellformular = false } =
|
||||
return
|
||||
}
|
||||
kopf.replaceChildren(statusKarte())
|
||||
if (weg === 'behalten' && behaltenMoeglich(projekt)) zeigeBehalten(s)
|
||||
if (weg === 'behalten' && behaltenMoeglich(projekt, s.angebot)) zeigeBehalten(s)
|
||||
}
|
||||
|
||||
/** Behalten-Seite (30 €) statt der Statuskarte; der Editor darunter bleibt, nur versteckt, damit nichts verloren geht. „Zurück“ zeigt wieder die Karte. */
|
||||
|
||||
@@ -211,6 +211,47 @@ test('aboEndeAt: Behalten-Hinweis mit Datum', async () => {
|
||||
assert.doesNotMatch(nurText(kaputt), /bis NaN|Invalid|undefined/)
|
||||
})
|
||||
|
||||
// Fix Runde 1: Behalten (30 €) gibt es nur ohne Abo (inklusive false UND grund kein_abo). nicht_aktiv und gekuendigt heissen, dass das Abo läuft (R20/R25).
|
||||
test('aboEndeAt bei laufendem Abo (nicht_aktiv, gekuendigt): „Abo läuft wieder“, nie ein Bezahlknopf', async () => {
|
||||
for (const grund of ['nicht_aktiv', 'gekuendigt']) {
|
||||
for (const state of ['active', 'setup']) {
|
||||
const k = kachel((await render([projekt('p1', state, { aboEndeAt: '2026-10-01T10:00:00.000Z', einbau: 'angestossen' }, { zeitenLeer: false })], { zeiten: ANGEBOT(false, grund) })).html)
|
||||
const was = `${grund}/${state}`
|
||||
assert.match(nurText(k), /Dein Wartungs-Abo läuft wieder – Öffnungszeiten live ist darin enthalten\./, was)
|
||||
assert.deepEqual(knoepfe(k), ['Zeiten bearbeiten'], was)
|
||||
assert.doesNotMatch(nurText(k), /ist beendet|behalte die Funktion|für einmalig|Für 30 €|30 €/, was)
|
||||
assert.doesNotMatch(k, /data-zeiten-weg/, was)
|
||||
}
|
||||
}
|
||||
// unbekannter Grund oder kein Grund (false, null): nicht auf Verdacht bezahlen lassen
|
||||
for (const grund of [null, 'unbekannt']) {
|
||||
const k = kachel((await render([projekt('p1', 'active', { aboEndeAt: '2026-10-01T10:00:00.000Z' }, { zeitenLeer: false })], { zeiten: ANGEBOT(false, grund) })).html)
|
||||
assert.deepEqual(knoepfe(k), ['Zeiten bearbeiten'], String(grund))
|
||||
assert.doesNotMatch(nurText(k), /Für 30 €|behalte die Funktion/, String(grund))
|
||||
}
|
||||
// Gegenprobe: ohne Abo (kein_abo) bleibt es beim Angebot
|
||||
const ohne = kachel((await render([projekt('p1', 'active', { aboEndeAt: '2026-10-01T10:00:00.000Z' }, { zeitenLeer: false })], { zeiten: ANGEBOT(false, 'kein_abo') })).html)
|
||||
assert.deepEqual(knoepfe(ohne), ['Zeiten bearbeiten', 'Für 30 € behalten'])
|
||||
})
|
||||
|
||||
test('disabled abo_ende mit Daten bei laufendem Abo (nicht_aktiv, gekuendigt): kein „Für 30 € wieder einschalten“, stattdessen „Abo läuft wieder“', async () => {
|
||||
const feature = { disabledReason: 'abo_ende', disabledAt: '2026-10-04T10:00:00.000Z', loeschungAm: '2999-01-01' }
|
||||
for (const grund of ['nicht_aktiv', 'gekuendigt']) {
|
||||
const k = kachel((await render([projekt('p1', 'disabled', feature, { zeitenLeer: false })], { zeiten: ANGEBOT(false, grund) })).html)
|
||||
assert.doesNotMatch(k, /Für 30 €|data-zeiten-weg|<button/, grund)
|
||||
assert.match(nurText(k), /Dein Wartungs-Abo läuft wieder – wir schalten Öffnungszeiten live automatisch wieder ein/, grund)
|
||||
assert.match(nurText(k), /Deine Eingaben bleiben bis 01\.01\.2999 gespeichert/, grund)
|
||||
}
|
||||
for (const grund of [null, 'unbekannt']) {
|
||||
const k = kachel((await render([projekt('p1', 'disabled', feature, { zeitenLeer: false })], { zeiten: ANGEBOT(false, grund) })).html)
|
||||
assert.doesNotMatch(k, /Für 30 €|<button/, String(grund))
|
||||
assert.match(nurText(k), /Gerade nicht prüfbar/, String(grund))
|
||||
}
|
||||
// Gegenprobe: kein_abo bietet es an
|
||||
const ohne = kachel((await render([projekt('p1', 'disabled', feature, { zeitenLeer: false })], { zeiten: ANGEBOT(false, 'kein_abo') })).html)
|
||||
assert.deepEqual(knoepfe(ohne), ['Für 30 € wieder einschalten'])
|
||||
})
|
||||
|
||||
test('ordered: „Zahlung wird bestätigt …“', async () => {
|
||||
const k = kachel((await render([projekt('p1', 'ordered', { unlockedBy: null })], { zeiten: ANGEBOT(false, 'kein_abo') })).html)
|
||||
assert.match(k, /fn-pill fn-pill-setup">Zahlung offen</)
|
||||
|
||||
@@ -7,7 +7,7 @@ import { featureConfig, featureHaken, freiweg, oeffentlicheAnsicht } from '../se
|
||||
import { oeffentlicheConsent } from '../server/services/featureOrder.js'
|
||||
import { h } from '../public/termine-dom.js'
|
||||
import { initZeiten } from '../public/zeiten.js'
|
||||
import { behaltenFrist, renderZeitenBestellen, tagText, zeitenStatusKarte } from '../public/zeiten-bestellen.js'
|
||||
import { behaltenFrist, behaltenLage, renderZeitenBestellen, tagText, zeitenStatusKarte } from '../public/zeiten-bestellen.js'
|
||||
|
||||
// Einschalt-, Bestell- und Behalten-Weg der Öffnungszeiten live im Portal (public/zeiten.js, public/zeiten-bestellen.js; Spec 3.1 bis 3.3,
|
||||
// Rulings R20 und R32). Fake-DOM und Fake-fetch aus test/support/fakeDom.js: jeder Aufruf steht in fx.fetchLog, die Antworten kommen der Reihe nach
|
||||
@@ -500,6 +500,72 @@ test('Behalten: 409 „Abo läuft wieder“ zeichnet die Ansicht nach der neuen
|
||||
assert.ok(fx.finde(root, '.zt-status'))
|
||||
}))
|
||||
|
||||
// Fix Runde 1: Behalten (30 €) gibt es nur ohne Abo (inklusive false UND grund kein_abo). nicht_aktiv und gekuendigt heissen, dass das Abo läuft (R20/R25, Z12).
|
||||
test('behaltenLage: bezahlen nur bei inklusive false und kein_abo', () => {
|
||||
assert.equal(behaltenLage(angebot(false, 'kein_abo')), 'bezahlen')
|
||||
assert.equal(behaltenLage(angebot(false, 'nicht_aktiv')), 'abo')
|
||||
assert.equal(behaltenLage(angebot(false, 'gekuendigt')), 'abo')
|
||||
assert.equal(behaltenLage(angebot(true)), 'inklusive')
|
||||
for (const unklar of [angebot(null, null), angebot(false, null), angebot(false, 'unbekannt'), { verfuegbar: true }, undefined, null]) assert.equal(behaltenLage(unklar), 'unklar', JSON.stringify(unklar))
|
||||
})
|
||||
|
||||
test('Behalten bei laufendem Abo (nicht_aktiv, gekuendigt): die Karte sagt „Abo läuft wieder“, es gibt keinen Behalten-Knopf und keine Behalten-Seite', mitDom({}, async (fx) => {
|
||||
for (const grund of ['nicht_aktiv', 'gekuendigt']) {
|
||||
// aktiv mit erkanntem Abo-Ende, auch wenn die Kachel „Für 30 € behalten“ (wunschWeg) schicken wollte
|
||||
fx.antworten.push(liste([projekt('p1', 'active', ABO_ENDE, { zeitenLeer: false })], angebot(false, grund)))
|
||||
let root = await oeffne(fx, { dataset: { wunschWeg: 'behalten' } })
|
||||
assert.equal(knopf(fx, root, 'behalten'), null, grund)
|
||||
assert.equal(fx.finde(root, '[data-variante]'), null, `${grund}: keine Behalten-Seite`)
|
||||
assert.match(fx.text(fx.finde(root, '[data-hinweis="abo-laeuft"]')), /Dein Wartungs-Abo läuft wieder – Öffnungszeiten live ist darin enthalten\./, grund)
|
||||
assert.doesNotMatch(fx.text(root), /ist beendet|behalte die Funktion|Für 30 €/, grund)
|
||||
assert.equal(fx.alle(root, 'input[type="checkbox"]').length, 0)
|
||||
|
||||
// abgeschaltet (abo_ende) mit Daten
|
||||
fx.antworten.push(liste([projekt('p1', 'disabled', { disabledReason: 'abo_ende', disabledAt: '2026-10-04T10:00:00.000Z', loeschungAm: '2999-01-01' }, { zeitenLeer: false })], angebot(false, grund)))
|
||||
root = await oeffne(fx, { dataset: { wunschWeg: 'behalten' } })
|
||||
assert.equal(knopf(fx, root, 'behalten'), null, `${grund}: abgeschaltet`)
|
||||
assert.equal(fx.finde(root, '[data-variante]'), null)
|
||||
assert.match(fx.text(fx.finde(root, '[data-hinweis="abo-laeuft"]')), /Dein Wartungs-Abo läuft wieder/, grund)
|
||||
assert.doesNotMatch(fx.text(root), /Für 30 €|schaltest du die Funktion wieder ein/, grund)
|
||||
}
|
||||
// unbekannter Grund (false, null): kein Bezahlknopf auf Verdacht
|
||||
for (const an of [angebot(false, null), angebot(false, 'unbekannt')]) {
|
||||
fx.antworten.push(liste([projekt('p1', 'active', ABO_ENDE, { zeitenLeer: false })], an))
|
||||
const root = await oeffne(fx, { dataset: { wunschWeg: 'behalten' } })
|
||||
assert.equal(knopf(fx, root, 'behalten'), null)
|
||||
assert.equal(fx.finde(root, '[data-variante]'), null)
|
||||
fx.antworten.push(liste([projekt('p1', 'disabled', { disabledReason: 'abo_ende', loeschungAm: '2999-01-01' }, { zeitenLeer: false })], an))
|
||||
const aus = await oeffne(fx)
|
||||
assert.equal(knopf(fx, aus, 'behalten'), null)
|
||||
assert.ok(fx.finde(aus, '[data-hinweis="nicht-pruefbar"]'))
|
||||
}
|
||||
// Gegenprobe: ohne Abo (kein_abo) bleibt Behalten
|
||||
fx.antworten.push(liste([projekt('p1', 'active', ABO_ENDE, { zeitenLeer: false })], angebot(false, 'kein_abo')))
|
||||
const ohne = await oeffne(fx, { dataset: { wunschWeg: 'behalten' } })
|
||||
assert.ok(fx.finde(ohne, '[data-variante="behalten"]'))
|
||||
}))
|
||||
|
||||
test('Behalten-Seite direkt gerufen (renderZeitenBestellen, modus behalten): nur bei kein_abo die bezahlte Seite, sonst Hinweis ohne Haken und Knopf', mitDom({}, (fx) => {
|
||||
const ctx = { projectId: 'p1', features: { zeiten: oeffentlicheAnsicht('zeiten') }, ustHinweis: config.ustHinweis, kundenansicht: false }
|
||||
const zeige = (an) => {
|
||||
const wurzel = h('div')
|
||||
renderZeitenBestellen(wurzel, { ctx, angebot: an, projekt: projekt('p1', 'active', ABO_ENDE), modus: 'behalten' })
|
||||
return wurzel
|
||||
}
|
||||
assert.ok(fx.finde(zeige(angebot(false, 'kein_abo')), '[data-variante="behalten"]'))
|
||||
for (const grund of ['nicht_aktiv', 'gekuendigt']) {
|
||||
const wurzel = zeige(angebot(false, grund))
|
||||
assert.match(fx.text(wurzel), /Dein Wartungs-Abo läuft wieder/, grund)
|
||||
assert.equal(fx.alle(wurzel, 'input,button').length, 0, grund)
|
||||
}
|
||||
for (const grund of [null, 'unbekannt']) {
|
||||
const wurzel = zeige(angebot(false, grund))
|
||||
assert.match(fx.text(wurzel), /Gerade nicht prüfbar – bitte später erneut/, String(grund))
|
||||
assert.equal(fx.alle(wurzel, 'input').length, 0)
|
||||
assert.equal(fx.finde(wurzel, '[data-variante]'), null)
|
||||
}
|
||||
}))
|
||||
|
||||
test('renderZeitenBestellen im Modus behalten: Abo läuft wieder ⇒ Hinweis statt Seite; Abo nicht lesbar ⇒ „Gerade nicht prüfbar“', mitDom({}, (fx) => {
|
||||
const ctx = { projectId: 'p1', features: { zeiten: oeffentlicheAnsicht('zeiten') }, ustHinweis: config.ustHinweis, kundenansicht: false }
|
||||
const wurzel = h('div')
|
||||
|
||||
Reference in new issue
Block a user