WOMS: Aktionen und Anleitungs-Logik fuer den Terminplaner
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6f0371283e
commit
c0015544c4
3 files changed
+284
-5
No files matched your search
@@ -3,7 +3,21 @@ import assert from 'node:assert/strict'
|
||||
import { loadModule } from './load.mjs'
|
||||
|
||||
// Kunden-Reiter "Funktionen": Register der Funktionen (News, Terminplaner), Aktionen je Zustand, Antwort einarbeiten
|
||||
const { FUNKTIONEN, ZUSTAND, funktionOf, aktionen, mergeFunktion, siteKeyOf, abgeschaltetText } = await loadModule('src/lib/funktionenView.js')
|
||||
const {
|
||||
FUNKTIONEN,
|
||||
ZUSTAND,
|
||||
AKTION_LABEL,
|
||||
funktionOf,
|
||||
aktionen,
|
||||
mergeFunktion,
|
||||
siteKeyOf,
|
||||
abgeschaltetText,
|
||||
hindernisText,
|
||||
anleitungKopien,
|
||||
anleitungPfad,
|
||||
panelModell,
|
||||
anleitungOffen,
|
||||
} = await loadModule('src/lib/funktionenView.js')
|
||||
|
||||
test('FUNKTIONEN: News und Terminplaner mit den Bestätigungstexten', () => {
|
||||
assert.deepEqual(FUNKTIONEN, [
|
||||
@@ -21,6 +35,8 @@ test('FUNKTIONEN: News und Terminplaner mit den Bestätigungstexten', () => {
|
||||
bestaetigen: {
|
||||
activate: 'Terminplaner ohne Zahlung aktivieren?',
|
||||
disable: 'Terminplaner abschalten? Auf der Website sind dann keine Buchungen mehr möglich.',
|
||||
abnahme: 'Terminplaner freischalten? Der Kunde bekommt die Mail „bereit“ und Gäste können buchen.',
|
||||
ticket: 'Fehlendes Einrichtungs-Ticket neu anlegen?',
|
||||
},
|
||||
},
|
||||
])
|
||||
@@ -45,13 +61,191 @@ test('funktionOf: Ansicht der Funktion, ohne Eintrag "off"', () => {
|
||||
assert.deepEqual(funktionOf(null, 'news'), { state: 'off' })
|
||||
})
|
||||
|
||||
test('aktionen: Aktivieren außer bei "active", Abschalten bei Bestellt, Einrichtung und Aktiv', () => {
|
||||
test('aktionen: News unverändert – Aktivieren außer bei "active", Abschalten bei Bestellt, Einrichtung und Aktiv', () => {
|
||||
assert.deepEqual(aktionen('off', 'news'), ['activate'])
|
||||
assert.deepEqual(aktionen('ordered', 'news'), ['activate', 'disable'])
|
||||
assert.deepEqual(aktionen('setup', 'news'), ['activate', 'disable'])
|
||||
assert.deepEqual(aktionen('active', 'news'), ['disable'])
|
||||
assert.deepEqual(aktionen('disabled', 'news'), ['activate'])
|
||||
assert.deepEqual(aktionen(undefined, 'news'), ['activate'])
|
||||
})
|
||||
|
||||
test('aktionen: ohne Schlüssel wie News (auch mit undefiniertem Zustand)', () => {
|
||||
assert.deepEqual(aktionen('off'), ['activate'])
|
||||
assert.deepEqual(aktionen('ordered'), ['activate', 'disable'])
|
||||
assert.deepEqual(aktionen('setup'), ['activate', 'disable'])
|
||||
assert.deepEqual(aktionen('active'), ['disable'])
|
||||
assert.deepEqual(aktionen('disabled'), ['activate'])
|
||||
assert.deepEqual(aktionen(undefined), ['activate'])
|
||||
assert.deepEqual(aktionen('setup', undefined, { ticketMissing: true }), ['activate', 'disable'], 'ticketMissing betrifft nur den Terminplaner')
|
||||
})
|
||||
|
||||
test('aktionen: News ignoriert ticketMissing in jedem Zustand', () => {
|
||||
for (const state of ['off', 'ordered', 'setup', 'active', 'disabled', undefined]) {
|
||||
assert.deepEqual(aktionen(state, 'news', { ticketMissing: true }), aktionen(state, 'news'), String(state))
|
||||
}
|
||||
})
|
||||
|
||||
test('aktionen: Terminplaner – bestellt nur Abschalten (kein Aktivieren ohne Zahlung), Einrichtung mit Abnahme', () => {
|
||||
assert.deepEqual(aktionen('off', 'termine'), ['activate'])
|
||||
assert.deepEqual(aktionen('ordered', 'termine'), ['disable'], 'Aktivieren würde eine spätere Zahlung im Portal verschlucken')
|
||||
assert.deepEqual(aktionen('setup', 'termine'), ['abnahme', 'disable'])
|
||||
assert.deepEqual(aktionen('active', 'termine'), ['disable'])
|
||||
assert.deepEqual(aktionen('disabled', 'termine'), ['activate'])
|
||||
assert.deepEqual(aktionen(undefined, 'termine'), ['activate'])
|
||||
})
|
||||
|
||||
test('aktionen: Terminplaner mit fehlendem Ticket bietet in der Einrichtung zuerst "ticket" an', () => {
|
||||
assert.deepEqual(aktionen('setup', 'termine', { ticketMissing: true }), ['ticket', 'abnahme', 'disable'])
|
||||
assert.deepEqual(aktionen('setup', 'termine', { ticketMissing: false }), ['abnahme', 'disable'])
|
||||
assert.deepEqual(aktionen('setup', 'termine', {}), ['abnahme', 'disable'])
|
||||
for (const state of ['off', 'ordered', 'active', 'disabled', undefined]) {
|
||||
assert.deepEqual(aktionen(state, 'termine', { ticketMissing: true }), aktionen(state, 'termine'), `${String(state)}: ticketMissing nur in setup`)
|
||||
}
|
||||
})
|
||||
|
||||
test('AKTION_LABEL: Beschriftung je Aktion', () => {
|
||||
assert.deepEqual(AKTION_LABEL, {
|
||||
activate: 'Aktivieren (ohne Zahlung)',
|
||||
disable: 'Abschalten',
|
||||
abnahme: 'Freischalten (Abnahme)',
|
||||
ticket: 'Ticket nachlegen',
|
||||
})
|
||||
})
|
||||
|
||||
test('hindernisText: Text je Code, sonst leer', () => {
|
||||
assert.equal(hindernisText('kein_abo'), 'Kein laufendes Wartungs-Abo: Freischalten ist gesperrt.')
|
||||
assert.equal(hindernisText('keine_einstellung'), 'Noch keine Einstellungen gespeichert.')
|
||||
assert.equal(hindernisText('abo_unbekannt'), 'Abo gerade nicht prüfbar.')
|
||||
for (const code of ['x', '', null, undefined, 5, 'constructor', 'toString']) assert.equal(hindernisText(code), '', String(code))
|
||||
})
|
||||
|
||||
// Antwort von GET /funktionen/:customerId/termine/anleitung (Portal, D8) in Kurzform
|
||||
const antwort = () => ({
|
||||
stand: { version: 3, updatedAt: '2026-10-03T10:15:00.000Z' },
|
||||
warnungen: ['Datenschutz-Entwurf', 'Ticket ist älter als der Stand'],
|
||||
abschnitte: [
|
||||
{ id: 'modell', titel: 'Modell', text: 'Modell-Text', kopierbar: false },
|
||||
{ id: 'snippet', titel: 'Einbau-Code (Snippet)', text: '<script></script>', kopierbar: true },
|
||||
{ id: 'css', titel: 'Aussehen (CSS)', text: ':root {}', kopierbar: true },
|
||||
{ id: 'checkliste', titel: 'Abnahme-Checkliste', text: '- [ ] Punkt', kopierbar: false },
|
||||
{ id: 'datenschutz_sie', titel: 'Datenschutz (Sie)', text: 'Absatz Sie', kopierbar: true },
|
||||
{ id: 'datenschutz_du', titel: 'Datenschutz (Du)', text: 'Absatz Du', kopierbar: true },
|
||||
],
|
||||
text: 'GANZE ANLEITUNG',
|
||||
state: 'setup',
|
||||
ticketWoid: '100123',
|
||||
ticketVersion: 3,
|
||||
hindernis: null,
|
||||
})
|
||||
|
||||
test('anleitungKopien: zuerst "alles", dann die kopierbaren Abschnitte in Antwortreihenfolge mit festen Beschriftungen', () => {
|
||||
assert.deepEqual(anleitungKopien(antwort()), [
|
||||
{ id: 'alles', label: 'Alles kopieren', text: 'GANZE ANLEITUNG' },
|
||||
{ id: 'snippet', label: 'Einbau-Code kopieren', text: '<script></script>' },
|
||||
{ id: 'css', label: 'CSS-Variablen kopieren', text: ':root {}' },
|
||||
{ id: 'datenschutz_sie', label: 'Datenschutz-Absatz (Sie) kopieren', text: 'Absatz Sie' },
|
||||
{ id: 'datenschutz_du', label: 'Datenschutz-Absatz (Du) kopieren', text: 'Absatz Du' },
|
||||
])
|
||||
})
|
||||
|
||||
test('anleitungKopien: nicht kopierbare Abschnitte fehlen, unbekannte kopierbare heißen "<Titel> kopieren"', () => {
|
||||
const a = antwort()
|
||||
a.abschnitte = [
|
||||
{ id: 'modell', titel: 'Modell', text: 'x', kopierbar: false },
|
||||
{ id: 'neu', titel: 'Neuer Teil', text: 'Inhalt', kopierbar: true },
|
||||
{ id: 'constructor', titel: 'Eigener Titel', text: 'y', kopierbar: true },
|
||||
]
|
||||
assert.deepEqual(anleitungKopien(a), [
|
||||
{ id: 'alles', label: 'Alles kopieren', text: 'GANZE ANLEITUNG' },
|
||||
{ id: 'neu', label: 'Neuer Teil kopieren', text: 'Inhalt' },
|
||||
{ id: 'constructor', label: 'Eigener Titel kopieren', text: 'y' },
|
||||
])
|
||||
})
|
||||
|
||||
test('anleitungKopien: kopierbarer Abschnitt ohne Text entfällt, null und Antwort ohne Text ergeben []', () => {
|
||||
const a = antwort()
|
||||
a.abschnitte = [{ id: 'snippet', titel: 'Einbau-Code (Snippet)', text: '', kopierbar: true }]
|
||||
assert.deepEqual(anleitungKopien(a), [{ id: 'alles', label: 'Alles kopieren', text: 'GANZE ANLEITUNG' }])
|
||||
assert.deepEqual(anleitungKopien(null), [])
|
||||
assert.deepEqual(anleitungKopien(undefined), [])
|
||||
assert.deepEqual(anleitungKopien({ ...antwort(), text: '' }), [])
|
||||
assert.deepEqual(anleitungKopien({ ...antwort(), text: undefined }), [])
|
||||
assert.deepEqual(anleitungKopien({ abschnitte: antwort().abschnitte }), [])
|
||||
})
|
||||
|
||||
test('anleitungPfad: Kunde und Projekt kodiert', () => {
|
||||
assert.equal(anleitungPfad('c1', 'p1'), '/funktionen/c1/termine/anleitung?projectId=p1')
|
||||
assert.equal(anleitungPfad('c/1', 'p&x=1 ü'), '/funktionen/c%2F1/termine/anleitung?projectId=p%26x%3D1%20%C3%BC')
|
||||
})
|
||||
|
||||
test('panelModell: Beispielantwort liefert alle sechs Felder', () => {
|
||||
const modell = panelModell(antwort(), { state: 'setup', hindernis: 'kein_abo' })
|
||||
assert.deepEqual(Object.keys(modell).sort(), ['abschnitte', 'hinweis', 'kopf', 'kopien', 'ticketPfad', 'warnungen'])
|
||||
assert.equal(modell.kopf, 'Stand Version 3, 03.10.2026')
|
||||
assert.equal(modell.ticketPfad, '/tickets?woid=100123')
|
||||
assert.deepEqual(modell.warnungen, ['Datenschutz-Entwurf', 'Ticket ist älter als der Stand'])
|
||||
assert.equal(modell.hinweis, 'Kein laufendes Wartungs-Abo: Freischalten ist gesperrt.')
|
||||
assert.deepEqual(modell.abschnitte, [
|
||||
{ id: 'modell', titel: 'Modell', text: 'Modell-Text' },
|
||||
{ id: 'snippet', titel: 'Einbau-Code (Snippet)', text: '<script></script>' },
|
||||
{ id: 'css', titel: 'Aussehen (CSS)', text: ':root {}' },
|
||||
{ id: 'checkliste', titel: 'Abnahme-Checkliste', text: '- [ ] Punkt' },
|
||||
{ id: 'datenschutz_sie', titel: 'Datenschutz (Sie)', text: 'Absatz Sie' },
|
||||
{ id: 'datenschutz_du', titel: 'Datenschutz (Du)', text: 'Absatz Du' },
|
||||
])
|
||||
assert.deepEqual(modell.kopien, anleitungKopien(antwort()))
|
||||
})
|
||||
|
||||
test('panelModell: Kopf nach Berliner Kalendertag, ohne Datum nur die Version', () => {
|
||||
const kopf = (stand) => panelModell({ ...antwort(), stand }, {}).kopf
|
||||
assert.equal(kopf({ version: 12, updatedAt: '2026-10-03T23:30:00.000Z' }), 'Stand Version 12, 04.10.2026', 'nach Mitternacht in Berlin schon der nächste Tag')
|
||||
assert.equal(kopf({ version: 2, updatedAt: null }), 'Stand Version 2')
|
||||
assert.equal(kopf({ version: 2 }), 'Stand Version 2')
|
||||
assert.equal(kopf({ version: 2, updatedAt: 'kein Datum' }), 'Stand Version 2')
|
||||
assert.equal(panelModell({ ...antwort(), stand: undefined }, {}).kopf, '')
|
||||
assert.equal(panelModell({ ...antwort(), stand: {} }, {}).kopf, '', 'ohne Version kein Kopf')
|
||||
})
|
||||
|
||||
test('panelModell: null-Antwort ergibt ein leeres Modell', () => {
|
||||
const leer = { kopf: '', ticketPfad: null, warnungen: [], hinweis: '', kopien: [], abschnitte: [] }
|
||||
assert.deepEqual(panelModell(null), leer)
|
||||
assert.deepEqual(panelModell(undefined, undefined), leer)
|
||||
assert.deepEqual(panelModell(null, { hindernis: 'kein_abo' }), leer)
|
||||
})
|
||||
|
||||
test('panelModell: ohne ticketWoid kein Ticketpfad, ohne Hindernis kein Hinweis, Woid wird kodiert', () => {
|
||||
const ohne = panelModell({ ...antwort(), ticketWoid: null }, { state: 'setup' })
|
||||
assert.equal(ohne.ticketPfad, null)
|
||||
assert.equal(ohne.hinweis, '')
|
||||
assert.equal(panelModell({ ...antwort(), ticketWoid: undefined }, null).ticketPfad, null)
|
||||
assert.equal(panelModell({ ...antwort(), ticketWoid: '' }, null).ticketPfad, null)
|
||||
assert.equal(panelModell({ ...antwort(), ticketWoid: 'a b/1' }, null).ticketPfad, '/tickets?woid=a%20b%2F1')
|
||||
assert.equal(panelModell(antwort(), { hindernis: 'unbekannt' }).hinweis, '')
|
||||
})
|
||||
|
||||
test('panelModell: fehlende Listen werden zu leeren Listen', () => {
|
||||
const modell = panelModell({ stand: { version: 1, updatedAt: null }, state: 'active' }, {})
|
||||
assert.deepEqual(modell.warnungen, [])
|
||||
assert.deepEqual(modell.abschnitte, [])
|
||||
assert.deepEqual(modell.kopien, [])
|
||||
})
|
||||
|
||||
test('anleitungOffen: nur die Adresse mit ?anleitung=<projectId> öffnet das Panel', () => {
|
||||
assert.equal(anleitungOffen('?tab=funktionen&anleitung=p%2F1', 'p/1'), true, 'kodiert')
|
||||
assert.equal(anleitungOffen('?tab=funktionen&anleitung=p/1', 'p/1'), true, 'unkodiert')
|
||||
assert.equal(anleitungOffen('?anleitung=p1', 'p1'), true)
|
||||
assert.equal(anleitungOffen('anleitung=p1', 'p1'), true, 'ohne Fragezeichen')
|
||||
assert.equal(anleitungOffen('?tab=funktionen&anleitung=p2', 'p1'), false, 'anderes Projekt')
|
||||
assert.equal(anleitungOffen('?anleitung=p10', 'p1'), false, 'kein Präfix-Treffer')
|
||||
assert.equal(anleitungOffen('?tab=funktionen', 'p1'), false)
|
||||
assert.equal(anleitungOffen('?anleitung=', 'p1'), false)
|
||||
assert.equal(anleitungOffen('', 'p1'), false)
|
||||
assert.equal(anleitungOffen(undefined, 'p1'), false)
|
||||
assert.equal(anleitungOffen(null, 'p1'), false)
|
||||
assert.equal(anleitungOffen('?anleitung=p1', ''), false)
|
||||
assert.equal(anleitungOffen('?anleitung=', ''), false, 'leeres Projekt öffnet nichts')
|
||||
assert.equal(anleitungOffen('?anleitung=p1', undefined), false)
|
||||
})
|
||||
|
||||
const projects = () => [
|
||||
|
||||
Reference in new issue
Block a user