Terminplaner TP2: Kachel mit Bestellweg, Abo-Felder in der Funktionen-Liste
GET /api/funktionen liefert bei termine.verfuegbar: true zusaetzlich bestellbar
(true | false | null bei Lesefehler im Abo), grund (kein_abo | gekuendigt |
nicht_aktiv | null), preis und einrichtung aus dem Register; nicht verfuegbar
bleibt exakt { verfuegbar: false }. Die Kachel zeigt je Zustand Pille und Knopf:
Aus mit Abo "Terminplaner einrichten · 149 €" bzw. "Fragebogen fortsetzen",
Aus ohne Abo keinen Knopf, sondern den Hinweis zum Abo, Zahlung offen und In
Einrichtung "Status ansehen", Aktiv "Termine oeffnen", Abgeschaltet "Termine
ansehen". Zielprojekt: aktiv, ordered/setup, abgeschaltet, Entwurf, erstes.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8d628ef906
commit
6ae0f67457
5 files changed
+364
-43
No files matched your search
@@ -148,20 +148,93 @@ const termineMit = (mode, pilotEmails = []) => {
|
||||
config.termine.pilotEmails = pilotEmails
|
||||
}
|
||||
after(() => termineMit(MODUS.mode, MODUS.pilotEmails))
|
||||
const verfuegbar = async (c = cookie) => (await get('/api/funktionen', { cookie: c })).termine
|
||||
const termineAntwort = async (c = cookie) => (await get('/api/funktionen', { cookie: c })).termine
|
||||
const verfuegbar = async (c = cookie) => (await termineAntwort(c)).verfuegbar
|
||||
|
||||
test('GET /api/funktionen: termine.verfuegbar je Modus und Pilot-E-Mail', async () => {
|
||||
test('GET /api/funktionen: termine.verfuegbar je Modus und Pilot-E-Mail; false bleibt exakt { verfuegbar: false }', async () => {
|
||||
termineMit('off')
|
||||
assert.deepEqual(await verfuegbar(), { verfuegbar: false })
|
||||
assert.deepEqual(await termineAntwort(), { verfuegbar: false })
|
||||
termineMit('pilot', ['k@example.invalid'])
|
||||
assert.deepEqual(await verfuegbar(), { verfuegbar: true })
|
||||
assert.deepEqual(await verfuegbar(customerCookie('c2')), { verfuegbar: false }, 'Kunde nicht in der Pilotliste')
|
||||
assert.equal(await verfuegbar(), true)
|
||||
assert.deepEqual(await termineAntwort(customerCookie('c2')), { verfuegbar: false }, 'Kunde nicht in der Pilotliste')
|
||||
termineMit('pilot', [])
|
||||
assert.deepEqual(await verfuegbar(), { verfuegbar: false })
|
||||
assert.deepEqual(await termineAntwort(), { verfuegbar: false })
|
||||
termineMit('pilot', ['*'])
|
||||
assert.deepEqual(await verfuegbar(customerCookie('c2')), { verfuegbar: true })
|
||||
assert.equal(await verfuegbar(customerCookie('c2')), true)
|
||||
termineMit('on')
|
||||
assert.deepEqual(await verfuegbar(customerCookie('c2')), { verfuegbar: true })
|
||||
assert.equal(await verfuegbar(customerCookie('c2')), true)
|
||||
})
|
||||
|
||||
// ---- Abo-Felder der Kachel (C10a): bestellbar, grund, preis, einrichtung nur bei verfuegbar: true
|
||||
const wartung = (patch = {}) => aw.seed('subscriptions', subscriptionDoc('sub_h', { ...wartungDoc, ...patch }))
|
||||
const angebot = (bestellbar, grund) => ({
|
||||
verfuegbar: true,
|
||||
bestellbar,
|
||||
grund,
|
||||
preis: config.features.termine.priceLabel,
|
||||
einrichtung: config.features.termine.setupEstimate,
|
||||
})
|
||||
|
||||
test('GET /api/funktionen: Kunde mit Wartungs-Abo => bestellbar true, grund null, Preis und Einrichtung aus dem Register', async (t) => {
|
||||
t.after(() => aw.seed('subscriptions', hostingDoc))
|
||||
termineMit('pilot', ['*'])
|
||||
wartung()
|
||||
const termine = await termineAntwort()
|
||||
assert.deepEqual(termine, angebot(true, null))
|
||||
assert.ok(termine.preis && termine.einrichtung, 'das Register liefert beide Werte')
|
||||
assert.deepEqual(Object.keys(termine), ['verfuegbar', 'bestellbar', 'grund', 'preis', 'einrichtung'])
|
||||
})
|
||||
|
||||
test('GET /api/funktionen: ohne Wartungs-Abo (nur Hosting, gar kein Abo, beendet) => bestellbar false, grund kein_abo', async (t) => {
|
||||
t.after(() => aw.seed('subscriptions', hostingDoc))
|
||||
termineMit('pilot', ['*'])
|
||||
assert.deepEqual(await termineAntwort(), angebot(false, 'kein_abo'), 'c1 hat seit seedFixtures nur Hosting')
|
||||
assert.deepEqual(await termineAntwort(customerCookie('c3')), angebot(false, 'kein_abo'), 'c3 hat gar kein Abo')
|
||||
wartung({ status: 'canceled', endedAt: '2026-09-01T00:00:00.000Z' })
|
||||
assert.deepEqual(await termineAntwort(), angebot(false, 'kein_abo'), 'beendetes Wartungs-Abo')
|
||||
})
|
||||
|
||||
test('GET /api/funktionen: Kuendigung vorgemerkt => grund gekuendigt; past_due und trialing => nicht_aktiv', async (t) => {
|
||||
t.after(() => aw.seed('subscriptions', hostingDoc))
|
||||
termineMit('pilot', ['*'])
|
||||
wartung({ cancelAtPeriodEnd: true })
|
||||
assert.deepEqual(await termineAntwort(), angebot(false, 'gekuendigt'))
|
||||
wartung({ cancelAt: '2026-11-30T00:00:00.000Z' })
|
||||
assert.deepEqual(await termineAntwort(), angebot(false, 'gekuendigt'))
|
||||
wartung({ status: 'past_due' })
|
||||
assert.deepEqual(await termineAntwort(), angebot(false, 'nicht_aktiv'))
|
||||
wartung({ status: 'trialing' })
|
||||
assert.deepEqual(await termineAntwort(), angebot(false, 'nicht_aktiv'))
|
||||
})
|
||||
|
||||
test('GET /api/funktionen: Lesefehler im Abo => bestellbar null, grund null, die Projekte kommen trotzdem; nichts Technisches nach aussen', async (t) => {
|
||||
t.after(() => aw.seed('subscriptions', hostingDoc))
|
||||
const fehler = t.mock.method(console, 'error', () => {})
|
||||
termineMit('pilot', ['*'])
|
||||
wartung()
|
||||
aw.failNext({ collection: 'subscriptions', status: 500, message: 'geheimer Appwrite-Text' })
|
||||
const res = await get('/api/funktionen', { cookie })
|
||||
assert.deepEqual(res.termine, angebot(null, null))
|
||||
for (const id of ['pt1', 'pt2']) assert.ok(res.projects.some((p) => p.projectId === id), `Projekt ${id} ist in der Liste`)
|
||||
assert.equal(featuresOf({ body: res }, 'pt1').termine.state, 'active', 'die Zustaende der Projekte bleiben')
|
||||
assert.ok(!JSON.stringify(res).includes('geheimer'))
|
||||
assert.ok(fehler.mock.calls.every((aufruf) => !String(aufruf.arguments[0]).includes('geheimer')), 'auch nicht im Log')
|
||||
assert.ok(fehler.mock.calls.length >= 1, 'der Fehler steht im Log')
|
||||
// der naechste Aufruf liest wieder, nichts wird gemerkt
|
||||
assert.deepEqual(await termineAntwort(), angebot(true, null))
|
||||
})
|
||||
|
||||
test('GET /api/funktionen: ist der Terminplaner nicht verfuegbar, wird das Abo nicht gelesen', async (t) => {
|
||||
const echt = globalThis.fetch
|
||||
const abfragen = []
|
||||
globalThis.fetch = async (url, optionen) => {
|
||||
if (/\/collections\/subscriptions\/documents/.test(String(url))) abfragen.push(String(url))
|
||||
return echt(url, optionen)
|
||||
}
|
||||
t.after(() => (globalThis.fetch = echt))
|
||||
termineMit('off')
|
||||
assert.deepEqual(await termineAntwort(), { verfuegbar: false })
|
||||
assert.equal(abfragen.length, 0, abfragen.join(' | '))
|
||||
})
|
||||
|
||||
test('GET /api/funktionen: Projekte tragen features.termine und News wie bisher', async () => {
|
||||
|
||||
Reference in new issue
Block a user