Kundenansicht P5: Katalog, Abo-Stand und Anfragestatus in GET /api/funktionen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e30318a470
commit
b6f2df0d4a
3 files changed
+263
-2
No files matched your search
@@ -26,6 +26,7 @@ import { einschaltenDeps, einschaltenZeiten, zeitenProjektAnsicht } from '../ser
|
|||||||
import { starteBehalten } from '../services/zeiten/behalten.js'
|
import { starteBehalten } from '../services/zeiten/behalten.js'
|
||||||
import { aboMemo } from '../services/aboStatus.js'
|
import { aboMemo } from '../services/aboStatus.js'
|
||||||
import { aboProdukt } from '../services/aboFunktionen.js'
|
import { aboProdukt } from '../services/aboFunktionen.js'
|
||||||
|
import { anfrageStatusJeProjekt, katalogFuerKunde } from '../services/aboKatalog.js'
|
||||||
import { datumDe, einschalten, EINSCHALTEN_TEXTE, pruefeAboDeckung, pruefeEinschaltZustand } from '../services/funktionEinschalten.js'
|
import { datumDe, einschalten, EINSCHALTEN_TEXTE, pruefeAboDeckung, pruefeEinschaltZustand } from '../services/funktionEinschalten.js'
|
||||||
import { kundenartWirksam, NUR_BETRIEB_TEXT } from '../services/kundenart.js'
|
import { kundenartWirksam, NUR_BETRIEB_TEXT } from '../services/kundenart.js'
|
||||||
import { neueAnsicht } from '../services/kundenansicht/schalter.js'
|
import { neueAnsicht } from '../services/kundenansicht/schalter.js'
|
||||||
@@ -226,7 +227,7 @@ router.get('/', requireSession, async (req, res) => {
|
|||||||
)
|
)
|
||||||
// Der Abo-Stand wird fuer Terminplaner und Oeffnungszeiten nur einmal gelesen
|
// Der Abo-Stand wird fuer Terminplaner und Oeffnungszeiten nur einmal gelesen
|
||||||
const abos = aboMemo()
|
const abos = aboMemo()
|
||||||
res.json({
|
const antwort = {
|
||||||
projects,
|
projects,
|
||||||
// Preise und Bestelltexte je Funktion aus dem Register (nur die oeffentliche Ansicht, auch wenn der Terminplaner
|
// Preise und Bestelltexte je Funktion aus dem Register (nur die oeffentliche Ansicht, auch wenn der Terminplaner
|
||||||
// dem Kunden noch nicht freigegeben ist); setupEstimate und consentText bleiben als News-Alias fuer aeltere Clients
|
// dem Kunden noch nicht freigegeben ist); setupEstimate und consentText bleiben als News-Alias fuer aeltere Clients
|
||||||
@@ -248,7 +249,23 @@ router.get('/', requireSession, async (req, res) => {
|
|||||||
// Oeffnungszeiten live: Schalter ZEITEN_ENABLED (off | pilot | on); nur wenn verfuegbar zeigt das Portal die Kachel, dann mit
|
// Oeffnungszeiten live: Schalter ZEITEN_ENABLED (off | pilot | on); nur wenn verfuegbar zeigt das Portal die Kachel, dann mit
|
||||||
// dem Abo-Stand (inklusive, grund), dem Preis und den Texten des Gratisweges "Einschalten"
|
// dem Abo-Stand (inklusive, grund), dem Preis und den Texten des Gratisweges "Einschalten"
|
||||||
zeiten: await zeitenAngebot(req.session.email, customerId, abos),
|
zeiten: await zeitenAngebot(req.session.email, customerId, abos),
|
||||||
})
|
}
|
||||||
|
// Kundenansicht neu (V5): Abo-Modell mit Kundenart, Abo-Stand, Katalog und Anfragestatus von Webshop und Buchungskalender je Projekt.
|
||||||
|
// Ohne neue Ansicht bleibt die Antwort wie sie war. Ein Lesefehler beim Abo-Stand ist ein Fehler der Liste (nie ein erfundenes "kein Abo").
|
||||||
|
if (neueAnsicht(req)) {
|
||||||
|
const [kunde, abo, katalog, anfrageStatus] = await Promise.all([
|
||||||
|
kundenDokument(customerId),
|
||||||
|
aboProdukt(customerId, { now: jetzt }),
|
||||||
|
katalogFuerKunde({ email: req.session.email }),
|
||||||
|
anfrageStatusJeProjekt({ docs, projects }),
|
||||||
|
])
|
||||||
|
for (const p of projects) {
|
||||||
|
p.features.shop.anfrageStatus = anfrageStatus.get(p.projectId)?.shop ?? ''
|
||||||
|
p.features.unterkunft.anfrageStatus = anfrageStatus.get(p.projectId)?.unterkunft ?? ''
|
||||||
|
}
|
||||||
|
Object.assign(antwort, { modell: 'abo', kundenart: kundenartWirksam(kunde), abo, katalog })
|
||||||
|
}
|
||||||
|
res.json(antwort)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
fail(res, err, 'Die Funktionen konnten gerade nicht geladen werden. Bitte lade die Seite gleich noch einmal.')
|
fail(res, err, 'Die Funktionen konnten gerade nicht geladen werden. Bitte lade die Seite gleich noch einmal.')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { config } from '../config.js'
|
||||||
|
import { ENTHALTEN_IN } from './aboFunktionen.js'
|
||||||
|
import { consentAbo, featureConfig, featureSperre, katalogFelder, katalogReihenfolge } from './featureRegistry.js'
|
||||||
|
import { unterlagenFuer } from './aboUnterlagen.js'
|
||||||
|
import { leseShopConfig } from './shop/anfrage.js'
|
||||||
|
import { ladeEinstellungen } from './unterkunft/speicher.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Katalog der Funktionen im Abo-Modell (V5) fuer GET /api/funktionen mit neuer Ansicht. Reine Lesefunktionen; der Katalog enthaelt
|
||||||
|
* keinen Preis. Alle Texte kommen vom Server (Register, config.aboFunktionen), nie vom Client.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Oeffentliche Sicht der Erklaerung: version, knopf und die Items in Anzeigereihenfolge (nurPrivatInFrist nur, wo es gesetzt ist). */
|
||||||
|
function oeffentlicheErklaerung(consent) {
|
||||||
|
if (!consent) return null
|
||||||
|
return {
|
||||||
|
version: consent.version,
|
||||||
|
knopf: consent.knopf,
|
||||||
|
items: consent.items.map((item) => ({
|
||||||
|
id: item.id,
|
||||||
|
text: item.text,
|
||||||
|
haken: Boolean(item.haken),
|
||||||
|
...(item.nurPrivatInFrist ? { nurPrivatInFrist: true } : {}),
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Katalogeintraege in der Reihenfolge von katalogReihenfolge(); die Analyse nur bei config.analyse.enabled.
|
||||||
|
* @param {{ email?: string }} p email: Konto des Kunden (Schalter und Pilotlisten der Funktionen)
|
||||||
|
* @returns {Promise<Array<{ key: string, name: string, beschreibung: string, bild: string, enthaltenIn: 'hosting'|'wartung', verfuegbar: boolean,
|
||||||
|
* ablauf: string, einrichtung: string, unterlagen: object, nurBetrieb: boolean, consent: object|null, hinweis: string|null }>>}
|
||||||
|
*/
|
||||||
|
export async function katalogFuerKunde({ email } = {}) {
|
||||||
|
const eintraege = []
|
||||||
|
for (const key of katalogReihenfolge()) {
|
||||||
|
if (key === 'analyse' && !config.analyse.enabled) continue
|
||||||
|
const felder = katalogFelder(key)
|
||||||
|
const consent = oeffentlicheErklaerung(consentAbo(key)) // null bei analyse
|
||||||
|
eintraege.push({
|
||||||
|
key,
|
||||||
|
name: key === 'analyse' ? config.aboFunktionen.analyse.name : featureConfig(key).label.name,
|
||||||
|
beschreibung: felder.beschreibung,
|
||||||
|
bild: felder.bild,
|
||||||
|
enthaltenIn: ENTHALTEN_IN[key][0], // das kleinste Abo, das die Funktion enthaelt
|
||||||
|
verfuegbar: key === 'analyse' ? true : featureSperre(key, email) === null,
|
||||||
|
ablauf: felder.ablauf,
|
||||||
|
einrichtung: felder.einrichtung,
|
||||||
|
unterlagen: unterlagenFuer(key),
|
||||||
|
nurBetrieb: felder.nurBetrieb,
|
||||||
|
consent,
|
||||||
|
hinweis: consent?.items.find((item) => item.id === 'hinweis')?.text ?? null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return eintraege
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Anfragestatus einer Funktion zum Dokument: shopConfig (shop) bzw. ukEinstellungen (unterkunft). Lesefehler ergeben '' und einen Logeintrag. */
|
||||||
|
async function leseAnfrageStatus(key, doc) {
|
||||||
|
if (!doc) return ''
|
||||||
|
try {
|
||||||
|
const gelesen = key === 'shop' ? await leseShopConfig(doc.$id) : await ladeEinstellungen(doc.$id)
|
||||||
|
return typeof gelesen?.anfrageStatus === 'string' ? gelesen.anfrageStatus : ''
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[funktionen] Anfragestatus (${key}) nicht lesbar: ${err?.name ?? 'Fehler'} ${err?.status ?? ''}`.trim())
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Anfragestatus von Webshop und Buchungskalender je Projekt: Map<projectId, { shop, unterkunft }> mit '' | 'offen' | 'angenommen' |
|
||||||
|
* 'abgelehnt' | 'zurueckgezogen'. Je Projekt mit Funktionsdokument wird genau eine Konfiguration gelesen, ohne Dokument gilt ''.
|
||||||
|
* @param {{ docs: object[], projects: Array<{ projectId: string }> }} p docs: portalFeatures-Dokumente des Kunden; projects: Projekte der Liste
|
||||||
|
*/
|
||||||
|
export async function anfrageStatusJeProjekt({ docs, projects }) {
|
||||||
|
const ergebnis = new Map()
|
||||||
|
await Promise.all(
|
||||||
|
projects.map(async (projekt) => {
|
||||||
|
const dokument = (key) => docs.find((d) => d.projectId === projekt.projectId && d.featureKey === key)
|
||||||
|
const [shop, unterkunft] = await Promise.all([leseAnfrageStatus('shop', dokument('shop')), leseAnfrageStatus('unterkunft', dokument('unterkunft'))])
|
||||||
|
ergebnis.set(projekt.projectId, { shop, unterkunft })
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return ergebnis
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
import { test, after, beforeEach, afterEach, mock } from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { startAppwriteMock } from './support/appwriteMock.js'
|
||||||
|
import { startFakeStripe } from './support/fakeStripe.js'
|
||||||
|
import { mailSink } from './support/mailSink.js'
|
||||||
|
import { schemaKeys } from './support/schema.js'
|
||||||
|
import { useTestEnv } from './support/testEnv.js'
|
||||||
|
import { ZEITEN_DATETIMES } from '../server/services/zeiten/schema.js'
|
||||||
|
|
||||||
|
// GET /api/funktionen im Abo-Modell (V5): modell, kundenart, abo, katalog und anfrageStatus je Projekt; ohne neue Ansicht unveraendert
|
||||||
|
const aw = await startAppwriteMock({
|
||||||
|
schema: {
|
||||||
|
portalFeatures: schemaKeys('portalFeatures'),
|
||||||
|
subscriptions: schemaKeys('subscriptions'),
|
||||||
|
contractEvents: schemaKeys('contractEvents'),
|
||||||
|
featureConsents: schemaKeys('featureConsents'),
|
||||||
|
shopConfig: schemaKeys('shopConfig'),
|
||||||
|
ukEinstellungen: schemaKeys('ukEinstellungen'),
|
||||||
|
oeffnungszeiten: schemaKeys('oeffnungszeiten'),
|
||||||
|
zeitenErinnerungen: schemaKeys('zeitenErinnerungen'),
|
||||||
|
},
|
||||||
|
datetimes: {
|
||||||
|
...ZEITEN_DATETIMES,
|
||||||
|
portalFeatures: ['orderedAt', 'activatedAt', 'disabledAt', 'consentAt', 'aboEndeMailAt', 'aboEndeAt', 'einschaltMailAt'],
|
||||||
|
featureConsents: ['acceptedAt'],
|
||||||
|
},
|
||||||
|
defaultLimit: 25,
|
||||||
|
})
|
||||||
|
const st = await startFakeStripe()
|
||||||
|
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
|
||||||
|
const { config } = await import('../server/config.js')
|
||||||
|
const { startTestApp, get } = await import('./support/testApp.js')
|
||||||
|
const { customerCookie, customers, kundenansichtCookie, seedFixtures, subscriptionDoc, wartungDoc } = await import('./support/fixtures.js')
|
||||||
|
const { deleteDocument } = await import('../server/services/appwriteAdmin.js')
|
||||||
|
const { setMailTransportForTests } = await import('../server/services/mailer.js')
|
||||||
|
const { default: funktionenRoutes } = await import('../server/routes/funktionen.js')
|
||||||
|
const { resetZeitenCacheForTests } = await import('../server/services/zeiten/cache.js')
|
||||||
|
const { einschaltenDeps } = await import('../server/services/zeiten/einschalten.js')
|
||||||
|
|
||||||
|
const app = await startTestApp({ '/api/funktionen': funktionenRoutes })
|
||||||
|
after(() => Promise.all([app.close(), aw.close(), st.close()]))
|
||||||
|
after(() => setMailTransportForTests(null))
|
||||||
|
|
||||||
|
const JETZT = new Date('2026-10-17T08:00:00.000Z') // ein Samstag
|
||||||
|
const c1 = customerCookie('c1')
|
||||||
|
const vorher = {
|
||||||
|
neu: { mode: config.kundenansichtNeu.mode, pilotEmails: [...config.kundenansichtNeu.pilotEmails] },
|
||||||
|
termine: config.termine.mode,
|
||||||
|
shop: { mode: config.shop.mode, pilotEmails: [...config.shop.pilotEmails] },
|
||||||
|
zeiten: { mode: config.zeiten.mode, pilotEmails: [...config.zeiten.pilotEmails] },
|
||||||
|
analyse: config.analyse.enabled,
|
||||||
|
}
|
||||||
|
after(() => {
|
||||||
|
Object.assign(config.kundenansichtNeu, vorher.neu)
|
||||||
|
config.termine.mode = vorher.termine
|
||||||
|
Object.assign(config.shop, vorher.shop)
|
||||||
|
Object.assign(config.zeiten, vorher.zeiten)
|
||||||
|
config.analyse.enabled = vorher.analyse
|
||||||
|
})
|
||||||
|
|
||||||
|
const abschluss = (contractAt = JETZT.toISOString()) =>
|
||||||
|
aw.seed('contractEvents', { $id: 'ev_a', customerId: 'c1', subscriptionId: 'sub_h', type: 'abschluss', productKey: 'wartung', status: 'erledigt', consentAt: contractAt, details: JSON.stringify({ contractAt }) })
|
||||||
|
// aw.docs() liefert eine Kopie: Dokumente werden ueber die API geloescht
|
||||||
|
const leere = async (...collections) => {
|
||||||
|
for (const col of collections) for (const id of aw.docs(col).keys()) await deleteDocument(col, id)
|
||||||
|
}
|
||||||
|
const projekt = (id, over = {}) => aw.seed('websiteProjects', { $id: id, customerId: 'c1', projectName: `Projekt ${id}`, subdomain: id, repoFullName: `JUSN/${id}`, previewMode: 'auto', previewDetected: 'static', ...over })
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await leere('subscriptions', 'portalFeatures', 'featureConsents', 'oeffnungszeiten', 'workorders', 'websiteProjects', 'contractEvents', 'shopConfig', 'ukEinstellungen')
|
||||||
|
resetZeitenCacheForTests()
|
||||||
|
seedFixtures(aw)
|
||||||
|
aw.seed('subscriptions', subscriptionDoc('sub_h', wartungDoc))
|
||||||
|
projekt('p1')
|
||||||
|
projekt('p2')
|
||||||
|
config.kundenansichtNeu.mode = 'pilot'
|
||||||
|
config.kundenansichtNeu.pilotEmails = ['k@example.invalid', 'test@example.invalid']
|
||||||
|
config.termine.mode = 'on'
|
||||||
|
config.shop.mode = 'on'
|
||||||
|
config.zeiten.mode = 'pilot'
|
||||||
|
config.zeiten.pilotEmails = ['*']
|
||||||
|
config.analyse.enabled = false
|
||||||
|
einschaltenDeps.jetzt = () => JETZT
|
||||||
|
setMailTransportForTests(mailSink())
|
||||||
|
mock.method(console, 'error', () => {})
|
||||||
|
mock.method(console, 'warn', () => {})
|
||||||
|
mock.method(console, 'log', () => {})
|
||||||
|
})
|
||||||
|
afterEach(() => {
|
||||||
|
mock.restoreAll()
|
||||||
|
einschaltenDeps.jetzt = () => new Date()
|
||||||
|
})
|
||||||
|
|
||||||
|
const OLD_KEYS = ['consentText', 'features', 'legal', 'projects', 'setupEstimate', 'shop', 'termine', 'unterkunft', 'ustHinweis', 'zeiten']
|
||||||
|
|
||||||
|
test('ohne neue Ansicht: Top-Level-Schlüssel unverändert, keine Anfragestatus', async () => {
|
||||||
|
config.kundenansichtNeu.mode = 'off'
|
||||||
|
const b = await get('/api/funktionen', { cookie: c1 })
|
||||||
|
assert.deepEqual(Object.keys(b).sort(), OLD_KEYS)
|
||||||
|
assert.equal(Object.hasOwn(b.projects[0].features.shop, 'anfrageStatus'), false)
|
||||||
|
config.kundenansichtNeu.mode = 'pilot'
|
||||||
|
config.kundenansichtNeu.pilotEmails = ['k2@example.invalid'] // Kunde nicht in der Liste
|
||||||
|
assert.deepEqual(Object.keys(await get('/api/funktionen', { cookie: c1 })).sort(), OLD_KEYS)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('mit neuer Ansicht: Modell, Kundenart, Abo, Katalog in fester Reihenfolge', async () => {
|
||||||
|
abschluss() // offener Abschluss vom 17.10.
|
||||||
|
const b = await get('/api/funktionen', { cookie: c1 })
|
||||||
|
assert.deepEqual([b.modell, b.kundenart, b.abo], ['abo', 'privat', { produkt: 'wartung', laeuft: true, gekuendigt: false, widerrufsfristBis: '2026-11-02T22:59:59.999Z' }])
|
||||||
|
aw.seed('customers', { ...customers.c1, kundenart: 'unternehmen' }) // Kundenart aus dem Dokument, auch in der Mitarbeiter-Kundenansicht
|
||||||
|
assert.equal((await get('/api/funktionen', { cookie: kundenansichtCookie({ customerId: 'c1' }) })).kundenart, 'unternehmen')
|
||||||
|
aw.seed('customers', customers.c1)
|
||||||
|
assert.deepEqual(b.katalog.map((e) => e.key), ['news', 'termine', 'shop', 'unterkunft', 'zeiten']) // analyse nur bei ANALYSE_ENABLED
|
||||||
|
config.analyse.enabled = true
|
||||||
|
const mit = await get('/api/funktionen', { cookie: c1 })
|
||||||
|
assert.equal(mit.katalog.at(-1).key, 'analyse')
|
||||||
|
assert.equal(mit.katalog.at(-1).enthaltenIn, 'hosting')
|
||||||
|
const t = b.katalog.find((e) => e.key === 'termine')
|
||||||
|
assert.deepEqual(Object.keys(t).sort(), ['ablauf', 'beschreibung', 'bild', 'consent', 'einrichtung', 'enthaltenIn', 'hinweis', 'key', 'name', 'nurBetrieb', 'unterlagen', 'verfuegbar'])
|
||||||
|
assert.deepEqual([t.name, t.enthaltenIn, t.ablauf, t.bild, t.nurBetrieb, t.consent.items.map((i) => i.id)], ['Terminplaner', 'wartung', 'fragebogen', '/neu/bilder/termine.webp', false, ['einschalten-zeile', 'hinweis', 'beginn']])
|
||||||
|
assert.deepEqual(t.consent.items.at(-1), { id: 'beginn', text: config.aboFunktionen.texte.beginn, haken: true, nurPrivatInFrist: true })
|
||||||
|
assert.deepEqual([t.consent.version, t.consent.knopf], [config.aboFunktionen.fassung, 'Einschalten'])
|
||||||
|
const hinweise = Object.fromEntries(b.katalog.map((e) => [e.key, e.hinweis])) // Pflichten der alten Häkchen: Server liefert den Satz, aus demselben Item wie der Nachweis
|
||||||
|
assert.deepEqual(Object.keys(hinweise).filter((k) => hinweise[k] !== null), ['termine', 'shop', 'unterkunft'])
|
||||||
|
assert.deepEqual([hinweise.shop, hinweise.termine], [b.katalog.find((e) => e.key === 'shop').consent.items[1].text, t.consent.items[1].text])
|
||||||
|
assert.match(hinweise.shop, /eigenem Namen.*eigenes Stripe-Konto/)
|
||||||
|
assert.deepEqual([hinweise.news, hinweise.zeiten, mit.katalog.at(-1).hinweis], [null, null, null])
|
||||||
|
assert.equal(mit.katalog.at(-1).consent, null)
|
||||||
|
assert.equal(b.katalog.find((e) => e.key === 'shop').nurBetrieb, true)
|
||||||
|
assert.doesNotMatch(JSON.stringify(b.katalog), /€|priceLabel/)
|
||||||
|
assert.deepEqual(t.unterlagen.leistungsbeschreibung, { version: config.termine.avvVersion, url: config.features.termine.leistungsbeschreibung.url })
|
||||||
|
assert.equal(mit.katalog.at(-1).unterlagen.avv.url, `/rechtliches/avv-website-analyse-${config.analyse.avvVersion}.html`)
|
||||||
|
config.analyse.enabled = false
|
||||||
|
})
|
||||||
|
|
||||||
|
test('verfuegbar folgt dem Schalter je Funktion; nicht verfügbare stehen trotzdem im Katalog', async () => {
|
||||||
|
config.termine.mode = 'off'
|
||||||
|
config.shop.mode = 'pilot'
|
||||||
|
config.shop.pilotEmails = ['k2@example.invalid']
|
||||||
|
const katalog = (await get('/api/funktionen', { cookie: c1 })).katalog
|
||||||
|
const v = Object.fromEntries(katalog.map((e) => [e.key, e.verfuegbar]))
|
||||||
|
assert.deepEqual([v.news, v.termine, v.shop], [true, false, false])
|
||||||
|
assert.equal(katalog.length, 5)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('anfrageStatus je Projekt für Shop und Buchungskalender', async () => {
|
||||||
|
aw.seed('portalFeatures', { $id: 'f_p1_shop', customerId: 'c1', projectId: 'p1', featureKey: 'shop', siteKey: 'abcdefghijklmnop', state: 'off', enabled: false })
|
||||||
|
aw.seed('shopConfig', { $id: 'f_p1_shop', customerId: 'c1', projectId: 'p1', siteKey: 'abcdefghijklmnop', anfrageStatus: 'offen', version: 0 })
|
||||||
|
aw.seed('portalFeatures', { $id: 'f_p1_unterkunft', customerId: 'c1', projectId: 'p1', featureKey: 'unterkunft', siteKey: 'bbcdefghijklmnop', state: 'off', enabled: false })
|
||||||
|
aw.seed('ukEinstellungen', { $id: 'f_p1_unterkunft', customerId: 'c1', projectId: 'p1', anfrageStatus: 'angenommen' })
|
||||||
|
const b = await get('/api/funktionen', { cookie: c1 })
|
||||||
|
const f = (id) => b.projects.find((p) => p.projectId === id).features
|
||||||
|
assert.deepEqual([f('p1').shop.anfrageStatus, f('p1').unterkunft.anfrageStatus, f('p2').shop.anfrageStatus, f('p2').unterkunft.anfrageStatus], ['offen', 'angenommen', '', ''])
|
||||||
|
assert.equal(Object.hasOwn(f('p1').news, 'anfrageStatus'), false)
|
||||||
|
// ein Lesefehler einer Konfiguration kostet die Liste nichts: '' statt Fehler
|
||||||
|
aw.failNext({ collection: 'shopConfig', method: 'GET', status: 500 })
|
||||||
|
const c = await get('/api/funktionen', { cookie: c1 })
|
||||||
|
assert.equal(c.projects.find((p) => p.projectId === 'p1').features.shop.anfrageStatus, '')
|
||||||
|
})
|
||||||
Reference in new issue
Block a user