Panel unter der Zeile Öffnungszeiten live (Header-Selektor, Kontakt-/Hinweis-Leiste aus, Speichern mit teamVersion, Ersatz-HTML kopieren je Plan), Meldung zum Ersatz-Ticket nach dem Abschalten, Hindernis an aktionen, Statustext der Zeiten, Team-API (GET ersatz, PATCH team), Karte Leistungsbeschreibung Öffnungszeiten live in setup-legal (Schlüssel höchstens 36 Zeichen). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
335 lines
19 KiB
JavaScript
335 lines
19 KiB
JavaScript
import test from 'node:test'
|
||
import assert from 'node:assert/strict'
|
||
import { loadModule } from './load.mjs'
|
||
|
||
// Öffnungszeiten live im Funktionen-Reiter: Statustexte, Prüfung des Navigations-Selektors (Spiegel des Portals) und Rumpf der Team-Einstellungen
|
||
const {
|
||
zeitenStatusText, navSelektorFehler, NAV_SELEKTOR_MAX, NAV_SELEKTOR_FEHLER, teamBody,
|
||
ersatzPfad, zeitenTeamPfad, teamFormular, teamGeaendert, mergeTeam, teamSpeichernFehler,
|
||
ersatzKnoepfe, ersatzKopieren, ersatzMeldung, abschaltMeldung, KEINE_ZEITEN_TEXT, ZWISCHENABLAGE_FEHLT,
|
||
} = await loadModule('src/lib/zeitenView.js')
|
||
|
||
const EINBAU = 'Leisten live – Einbau läuft'
|
||
const WARTET = 'wartet auf die Zeiten des Kunden'
|
||
|
||
test('zeitenStatusText: Einrichtung ohne Hindernis – Leisten sind live, der Einbau läuft', () => {
|
||
assert.equal(zeitenStatusText({ state: 'setup', hindernis: null }), EINBAU)
|
||
assert.equal(zeitenStatusText({ state: 'setup' }), EINBAU, 'ohne Feld hindernis')
|
||
assert.equal(zeitenStatusText({ state: 'setup', hindernis: '' }), EINBAU)
|
||
})
|
||
|
||
test('zeitenStatusText: Einrichtung mit hindernis "zeiten_fehlen" – wartet auf die Zeiten des Kunden', () => {
|
||
assert.equal(zeitenStatusText({ state: 'setup', hindernis: 'zeiten_fehlen' }), WARTET)
|
||
})
|
||
|
||
test('zeitenStatusText: unbekanntes Hindernis zeigt nichts Erfundenes, sondern den Einbau-Text', () => {
|
||
assert.equal(zeitenStatusText({ state: 'setup', hindernis: 'irgendwas_neues' }), EINBAU)
|
||
})
|
||
|
||
test('zeitenStatusText: Abo-Ende nennt die Frist (Berliner Tag des Abo-Endes plus 30 Tage)', () => {
|
||
const abo = (state, aboEndeAt) => zeitenStatusText({ state, hindernis: null, aboEndeAt })
|
||
const hinweis = (datum) => `Abo beendet – Abschaltung am ${datum}, falls der Kunde die Funktion nicht behält`
|
||
assert.equal(abo('active', '2026-10-04T10:00:00.000Z'), hinweis('03.11.2026'))
|
||
// 22:30 UTC ist in Berlin schon der nächste Tag (Sommerzeit, +2 h): 05.10. -> 04.11.
|
||
assert.equal(abo('active', '2026-10-04T22:30:00.000Z'), hinweis('04.11.2026'))
|
||
// Monats- und Jahreswechsel, Zeitumstellung am 25.10. spielt bei Kalendertagen keine Rolle
|
||
assert.equal(abo('active', '2026-10-20T12:00:00.000Z'), hinweis('19.11.2026'))
|
||
assert.equal(abo('active', '2026-12-15T12:00:00.000Z'), hinweis('14.01.2027'))
|
||
// Appwrite liefert auch +00:00
|
||
assert.equal(abo('active', '2026-10-04T10:00:00.000+00:00'), hinweis('03.11.2026'))
|
||
})
|
||
|
||
test('zeitenStatusText: Abo-Ende in der Einrichtung steht zusätzlich zum Einbau- bzw. Warte-Text', () => {
|
||
const aboEndeAt = '2026-10-04T10:00:00.000Z'
|
||
const frist = 'Abo beendet – Abschaltung am 03.11.2026, falls der Kunde die Funktion nicht behält'
|
||
assert.equal(zeitenStatusText({ state: 'setup', hindernis: null, aboEndeAt }), `${EINBAU} · ${frist}`)
|
||
assert.equal(zeitenStatusText({ state: 'setup', hindernis: 'zeiten_fehlen', aboEndeAt }), `${WARTET} · ${frist}`)
|
||
})
|
||
|
||
test('zeitenStatusText: Abo-Ende mit unlesbarem Datum nennt 30 Tage statt eines erfundenen Tages', () => {
|
||
const text = zeitenStatusText({ state: 'active', aboEndeAt: 'kein-datum' })
|
||
assert.equal(text, 'Abo beendet – Abschaltung nach 30 Tagen, falls der Kunde die Funktion nicht behält')
|
||
})
|
||
|
||
test('zeitenStatusText: aktiv ohne Abo-Ende, aus, bestellt, abgeschaltet, ohne Ansicht – leer', () => {
|
||
assert.equal(zeitenStatusText({ state: 'active', hindernis: null, aboEndeAt: null }), '')
|
||
assert.equal(zeitenStatusText({ state: 'active' }), '')
|
||
assert.equal(zeitenStatusText({ state: 'off' }), '')
|
||
assert.equal(zeitenStatusText({ state: 'ordered' }), '')
|
||
// abgeschaltet: Grund und Löschdatum zeigt abgeschaltetText, nichts doppelt
|
||
assert.equal(zeitenStatusText({ state: 'disabled', disabledReason: 'abo_ende', loeschungAm: '2026-11-04', aboEndeAt: '2026-10-04T10:00:00.000Z' }), '')
|
||
assert.equal(zeitenStatusText({ state: 'off', aboEndeAt: '2026-10-04T10:00:00.000Z' }), '')
|
||
assert.equal(zeitenStatusText(null), '')
|
||
assert.equal(zeitenStatusText(undefined), '')
|
||
})
|
||
|
||
test('zeitenStatusText: kein Text nennt "24 Uhr"', () => {
|
||
for (const feature of [
|
||
{ state: 'setup' },
|
||
{ state: 'setup', hindernis: 'zeiten_fehlen' },
|
||
{ state: 'active', aboEndeAt: '2026-10-04T10:00:00.000Z' },
|
||
{ state: 'setup', hindernis: 'zeiten_fehlen', aboEndeAt: '2026-10-04T22:30:00.000Z' },
|
||
]) {
|
||
assert.doesNotMatch(zeitenStatusText(feature), /24 Uhr/)
|
||
}
|
||
})
|
||
|
||
test('navSelektorFehler: leer ist erlaubt (kein Selektor)', () => {
|
||
for (const wert of [undefined, null, '', ' ', '\n\t ']) {
|
||
assert.equal(navSelektorFehler(wert), null, JSON.stringify(wert))
|
||
}
|
||
})
|
||
|
||
test('navSelektorFehler: übliche Selektoren sind gültig', () => {
|
||
for (const wert of [
|
||
'header',
|
||
'#nav',
|
||
'.site-header',
|
||
'.site-header > nav',
|
||
'header#main-nav.fixed',
|
||
'nav[role="navigation"]',
|
||
"a[href='x']",
|
||
'ul.menu:first-child',
|
||
'div_wrap .a-b',
|
||
'ABC xyz 019',
|
||
' header ',
|
||
]) {
|
||
assert.equal(navSelektorFehler(wert), null, wert)
|
||
}
|
||
})
|
||
|
||
test('navSelektorFehler: erlaubt sind nur A–Z a–z 0–9, Leerzeichen und # . - _ > : [ ] = " \'', () => {
|
||
const zeichen = 'AZaz09 #.-_>:[]="\''
|
||
assert.equal(navSelektorFehler(zeichen), null)
|
||
assert.equal(navSelektorFehler('a'.repeat(NAV_SELEKTOR_MAX)), null)
|
||
})
|
||
|
||
test('navSelektorFehler: < { } ; und alle übrigen Zeichen werden abgelehnt', () => {
|
||
for (const zeichen of ['<', '{', '}', ';', '(', ')', ',', '*', '+', '~', '/', '\\', '@', '!', '|', '$', '%', '^', '&', '?', '`', 'ä', 'ß', '€', '\n', '\t']) {
|
||
assert.equal(navSelektorFehler(`a${zeichen}b`), NAV_SELEKTOR_FEHLER, JSON.stringify(zeichen))
|
||
}
|
||
assert.equal(navSelektorFehler('header { display: none }'), NAV_SELEKTOR_FEHLER)
|
||
assert.equal(navSelektorFehler('</style><script>'), NAV_SELEKTOR_FEHLER)
|
||
assert.equal(navSelektorFehler('a\nb'), NAV_SELEKTOR_FEHLER, 'Zeilenumbruch mitten im Text')
|
||
})
|
||
|
||
test('navSelektorFehler: höchstens 120 Zeichen, gezählt nach dem Trimmen', () => {
|
||
assert.equal(NAV_SELEKTOR_MAX, 120)
|
||
assert.equal(navSelektorFehler('a'.repeat(120)), null)
|
||
assert.equal(navSelektorFehler(` ${'a'.repeat(120)} `), null, 'Leerraum außen zählt nicht')
|
||
assert.equal(navSelektorFehler('a'.repeat(121)), NAV_SELEKTOR_FEHLER)
|
||
assert.equal(navSelektorFehler(`${'a'.repeat(60)} ${'b'.repeat(60)}`), NAV_SELEKTOR_FEHLER, '121 mit Leerzeichen innen')
|
||
})
|
||
|
||
test('navSelektorFehler: kein Text wird abgelehnt wie auf dem Server (Zahl, Objekt, Wahrheitswert)', () => {
|
||
for (const wert of [5, true, {}, []]) {
|
||
assert.equal(navSelektorFehler(wert), NAV_SELEKTOR_FEHLER, JSON.stringify(wert))
|
||
}
|
||
})
|
||
|
||
test('NAV_SELEKTOR_FEHLER: wörtlich wie die Meldung des Portals', () => {
|
||
assert.equal(NAV_SELEKTOR_FEHLER, 'Der Selektor darf höchstens 120 Zeichen lang sein und nur A–Z, 0–9, Leerzeichen und # . - _ > : [ ] = " \' enthalten.')
|
||
})
|
||
|
||
test('teamBody: genau die fünf Felder der Team-Route, Selektor getrimmt, leer wird null', () => {
|
||
assert.deepEqual(
|
||
teamBody({ projectId: 'p1', teamVersion: 3, navSelektor: ' header > nav ', kontaktleisteAus: true, hinweisleisteAus: false }),
|
||
{ projectId: 'p1', teamVersion: 3, navSelektor: 'header > nav', kontaktleisteAus: true, hinweisleisteAus: false }
|
||
)
|
||
for (const leer of ['', ' ', null, undefined]) {
|
||
assert.equal(teamBody({ projectId: 'p1', teamVersion: 0, navSelektor: leer, kontaktleisteAus: false, hinweisleisteAus: false }).navSelektor, null, JSON.stringify(leer))
|
||
}
|
||
})
|
||
|
||
test('teamBody: Schalter sind immer echte Wahrheitswerte, nur true gilt als an', () => {
|
||
const body = (kontaktleisteAus, hinweisleisteAus) => teamBody({ projectId: 'p1', teamVersion: 1, navSelektor: '', kontaktleisteAus, hinweisleisteAus })
|
||
assert.deepEqual(body(true, true), { projectId: 'p1', teamVersion: 1, navSelektor: null, kontaktleisteAus: true, hinweisleisteAus: true })
|
||
assert.deepEqual(body(undefined, undefined), { projectId: 'p1', teamVersion: 1, navSelektor: null, kontaktleisteAus: false, hinweisleisteAus: false })
|
||
assert.deepEqual(body('true', 1), { projectId: 'p1', teamVersion: 1, navSelektor: null, kontaktleisteAus: false, hinweisleisteAus: false })
|
||
})
|
||
|
||
test('teamBody: die Version wird unverändert durchgereicht (das Portal prüft sie), auch 0', () => {
|
||
assert.equal(teamBody({ projectId: 'p1', teamVersion: 0, navSelektor: '', kontaktleisteAus: false, hinweisleisteAus: false }).teamVersion, 0)
|
||
assert.equal(teamBody({ projectId: 'p1', teamVersion: 41, navSelektor: '', kontaktleisteAus: false, hinweisleisteAus: false }).teamVersion, 41)
|
||
})
|
||
|
||
test('teamBody: ein ungültiger Selektor wird nicht "repariert", sondern unverändert gesendet (das Formular sperrt vorher, das Portal prüft noch einmal)', () => {
|
||
assert.equal(teamBody({ projectId: 'p1', teamVersion: 1, navSelektor: 'a{b}', kontaktleisteAus: false, hinweisleisteAus: false }).navSelektor, 'a{b}')
|
||
})
|
||
|
||
// --- Panel: Pfade, Formular, Speichern, Ersatz-HTML, Abschalt-Meldung ---
|
||
|
||
test('ersatzPfad: GET .../zeiten/ersatz mit kodierten Parametern projectId und plan', () => {
|
||
assert.equal(ersatzPfad('c1', 'p1', 'haupt'), '/funktionen/c1/zeiten/ersatz?projectId=p1&plan=haupt')
|
||
assert.equal(ersatzPfad('c/1', 'p 1', 'a&b=c'), '/funktionen/c%2F1/zeiten/ersatz?projectId=p%201&plan=a%26b%3Dc')
|
||
})
|
||
|
||
test('ersatzPfad: ohne plan fehlt der Parameter (Hauptplan), ein leerer plan bleibt stehen (das Portal antwortet dann 404)', () => {
|
||
assert.equal(ersatzPfad('c1', 'p1'), '/funktionen/c1/zeiten/ersatz?projectId=p1')
|
||
assert.equal(ersatzPfad('c1', 'p1', null), '/funktionen/c1/zeiten/ersatz?projectId=p1')
|
||
assert.equal(ersatzPfad('c1', 'p1', ''), '/funktionen/c1/zeiten/ersatz?projectId=p1&plan=')
|
||
})
|
||
|
||
test('zeitenTeamPfad: PATCH .../zeiten/team', () => {
|
||
assert.equal(zeitenTeamPfad('c1'), '/funktionen/c1/zeiten/team')
|
||
assert.equal(zeitenTeamPfad('c/1'), '/funktionen/c%2F1/zeiten/team')
|
||
})
|
||
|
||
test('teamFormular: Startwerte aus feature.team, null-Selektor wird ein leeres Feld', () => {
|
||
assert.deepEqual(
|
||
teamFormular({ team: { navSelektor: 'header > nav', kontaktleisteAus: true, hinweisleisteAus: false } }),
|
||
{ navSelektor: 'header > nav', kontaktleisteAus: true, hinweisleisteAus: false }
|
||
)
|
||
assert.deepEqual(
|
||
teamFormular({ team: { navSelektor: null, kontaktleisteAus: false, hinweisleisteAus: true } }),
|
||
{ navSelektor: '', kontaktleisteAus: false, hinweisleisteAus: true }
|
||
)
|
||
})
|
||
|
||
test('teamFormular: ohne Team-Feld (ältere Antwort, keine Ansicht) alles aus und leer', () => {
|
||
const leer = { navSelektor: '', kontaktleisteAus: false, hinweisleisteAus: false }
|
||
assert.deepEqual(teamFormular({}), leer)
|
||
assert.deepEqual(teamFormular({ team: null }), leer)
|
||
assert.deepEqual(teamFormular(null), leer)
|
||
assert.deepEqual(teamFormular(undefined), leer)
|
||
// Schalter nur bei true an, Selektor nur als Text
|
||
assert.deepEqual(teamFormular({ team: { navSelektor: 5, kontaktleisteAus: 'true', hinweisleisteAus: 1 } }), leer)
|
||
})
|
||
|
||
test('teamGeaendert: nur ein echter Unterschied zum gespeicherten Stand zählt, Leerraum um den Selektor nicht', () => {
|
||
const feature = { team: { navSelektor: 'header', kontaktleisteAus: false, hinweisleisteAus: true } }
|
||
const gleich = { navSelektor: 'header', kontaktleisteAus: false, hinweisleisteAus: true }
|
||
assert.equal(teamGeaendert(gleich, feature), false)
|
||
assert.equal(teamGeaendert({ ...gleich, navSelektor: ' header ' }, feature), false)
|
||
assert.equal(teamGeaendert({ ...gleich, navSelektor: 'header nav' }, feature), true)
|
||
assert.equal(teamGeaendert({ ...gleich, kontaktleisteAus: true }, feature), true)
|
||
assert.equal(teamGeaendert({ ...gleich, hinweisleisteAus: false }, feature), true)
|
||
// gespeichert ohne Selektor: leeres oder leeres-mit-Leerraum Feld ist keine Änderung
|
||
const ohne = { team: { navSelektor: null, kontaktleisteAus: false, hinweisleisteAus: false } }
|
||
assert.equal(teamGeaendert({ navSelektor: ' ', kontaktleisteAus: false, hinweisleisteAus: false }, ohne), false)
|
||
assert.equal(teamGeaendert({ navSelektor: 'x', kontaktleisteAus: false, hinweisleisteAus: false }, ohne), true)
|
||
})
|
||
|
||
test('mergeTeam: übernimmt team und teamVersion in genau dieses Projekt, alles andere bleibt', () => {
|
||
const zeiten = { state: 'active', plaene: [{ kurz: 'a', name: 'A', haupt: true }], team: { navSelektor: null, kontaktleisteAus: false, hinweisleisteAus: false }, teamVersion: 2 }
|
||
const projects = [
|
||
{ projectId: 'p1', projectName: 'Eins', features: { news: { state: 'off' }, zeiten } },
|
||
{ projectId: 'p2', projectName: 'Zwei', features: { zeiten: { ...zeiten, teamVersion: 7 } } },
|
||
]
|
||
const neu = mergeTeam(projects, 'p1', { team: { navSelektor: 'header', kontaktleisteAus: true, hinweisleisteAus: false }, teamVersion: 3 })
|
||
assert.notEqual(neu, projects, 'neue Liste')
|
||
assert.deepEqual(neu[0].features.zeiten, { ...zeiten, team: { navSelektor: 'header', kontaktleisteAus: true, hinweisleisteAus: false }, teamVersion: 3 })
|
||
assert.deepEqual(neu[0].features.news, { state: 'off' })
|
||
assert.equal(neu[0].projectName, 'Eins')
|
||
assert.equal(neu[1], projects[1], 'anderes Projekt unverändert')
|
||
assert.equal(projects[0].features.zeiten.teamVersion, 2, 'Eingabe nicht verändert')
|
||
})
|
||
|
||
test('mergeTeam: unbrauchbare Antwort oder Projekt ohne Zeiten ändert nichts', () => {
|
||
const projects = [{ projectId: 'p1', features: { zeiten: { state: 'active', teamVersion: 1, team: {} } } }, { projectId: 'p2', features: {} }]
|
||
assert.equal(mergeTeam(projects, 'p1', null), projects)
|
||
assert.equal(mergeTeam(projects, 'p1', {}), projects)
|
||
assert.equal(mergeTeam(projects, 'p1', { team: { navSelektor: null }, teamVersion: 'x' }), projects)
|
||
assert.equal(mergeTeam(projects, 'p1', { teamVersion: 2 }), projects)
|
||
assert.deepEqual(mergeTeam(projects, 'p2', { team: {}, teamVersion: 1 }), projects, 'p2 hat keine Zeiten: nichts erfunden')
|
||
assert.deepEqual(mergeTeam(projects, 'p9', { team: {}, teamVersion: 1 }), projects)
|
||
})
|
||
|
||
test('teamSpeichernFehler: 409 ZEITEN_VERALTET – „Inzwischen geändert – bitte neu laden“, danach neu laden', () => {
|
||
const err = Object.assign(new Error('Inzwischen geändert – bitte neu laden.'), { status: 409, code: 'ZEITEN_VERALTET' })
|
||
assert.deepEqual(teamSpeichernFehler(err), { text: 'Inzwischen geändert – bitte neu laden.', neuLaden: true })
|
||
// der feste Text gilt auch, wenn der Server-Text anders lautet
|
||
const anders = Object.assign(new Error('irgendwas'), { status: 409, code: 'ZEITEN_VERALTET' })
|
||
assert.equal(teamSpeichernFehler(anders).text, 'Inzwischen geändert – bitte neu laden.')
|
||
})
|
||
|
||
test('teamSpeichernFehler: anderer 409 (z. B. nicht eingeschaltet) zeigt den Text des Portals, die Ansicht ist trotzdem veraltet', () => {
|
||
const err = Object.assign(new Error('Öffnungszeiten live ist für dieses Projekt nicht eingeschaltet.'), { status: 409 })
|
||
assert.deepEqual(teamSpeichernFehler(err), { text: 'Öffnungszeiten live ist für dieses Projekt nicht eingeschaltet.', neuLaden: true })
|
||
})
|
||
|
||
test('teamSpeichernFehler: 400 und Netzfehler zeigen ihren Text unverändert, ohne Neuladen; ohne Text eine Ersatzmeldung', () => {
|
||
assert.deepEqual(teamSpeichernFehler(Object.assign(new Error(NAV_SELEKTOR_FEHLER), { status: 400 })), { text: NAV_SELEKTOR_FEHLER, neuLaden: false })
|
||
assert.deepEqual(teamSpeichernFehler(Object.assign(new Error('Das Kundenportal ist gerade nicht erreichbar.'), { status: 0 })), { text: 'Das Kundenportal ist gerade nicht erreichbar.', neuLaden: false })
|
||
assert.deepEqual(teamSpeichernFehler(new Error('')), { text: 'Speichern fehlgeschlagen.', neuLaden: false })
|
||
assert.deepEqual(teamSpeichernFehler(null), { text: 'Speichern fehlgeschlagen.', neuLaden: false })
|
||
})
|
||
|
||
test('ersatzKnoepfe: ein Plan – „Ersatz-HTML kopieren“, der Hauptplan zuerst bleibt in der Reihenfolge des Portals', () => {
|
||
assert.deepEqual(ersatzKnoepfe({ plaene: [{ kurz: 'haupt', name: 'Öffnungszeiten', haupt: true }] }), [
|
||
{ kurz: 'haupt', label: 'Ersatz-HTML kopieren', bezeichnung: 'Öffnungszeiten (haupt)' },
|
||
])
|
||
})
|
||
|
||
test('ersatzKnoepfe: mehrere Pläne – je Plan ein Knopf mit Name und Kurzname', () => {
|
||
const knoepfe = ersatzKnoepfe({
|
||
plaene: [
|
||
{ kurz: 'laden', name: 'Laden', haupt: true },
|
||
{ kurz: 'cafe', name: 'Café', haupt: false },
|
||
{ kurz: 'lieferung', name: '', haupt: false },
|
||
{ kurz: 'abholung', name: 'abholung', haupt: false },
|
||
],
|
||
})
|
||
assert.deepEqual(knoepfe.map((k) => k.kurz), ['laden', 'cafe', 'lieferung', 'abholung'])
|
||
assert.deepEqual(knoepfe.map((k) => k.label), [
|
||
'Ersatz-HTML kopieren: Laden (laden)',
|
||
'Ersatz-HTML kopieren: Café (cafe)',
|
||
'Ersatz-HTML kopieren: lieferung',
|
||
'Ersatz-HTML kopieren: abholung',
|
||
])
|
||
})
|
||
|
||
test('ersatzKnoepfe: Pläne ohne Kurzname und Unbrauchbares werden übergangen, ohne Pläne [] (Hinweis statt Knopf)', () => {
|
||
assert.deepEqual(ersatzKnoepfe({ plaene: [{ kurz: '', name: 'x' }, null, { name: 'y' }, { kurz: 5 }] }), [])
|
||
assert.deepEqual(ersatzKnoepfe({ plaene: [] }), [])
|
||
assert.deepEqual(ersatzKnoepfe({}), [])
|
||
assert.deepEqual(ersatzKnoepfe(null), [])
|
||
assert.equal(KEINE_ZEITEN_TEXT, 'Der Kunde hat noch keine Zeiten eingetragen.')
|
||
})
|
||
|
||
test('ersatzKopieren: schreibt das HTML in die Zwischenablage', async () => {
|
||
const geschrieben = []
|
||
const clipboard = { writeText: async (text) => { geschrieben.push(text) } }
|
||
assert.equal(await ersatzKopieren('<table>ä</table>', clipboard), 'kopiert')
|
||
assert.deepEqual(geschrieben, ['<table>ä</table>'])
|
||
})
|
||
|
||
test('ersatzKopieren: ohne Zwischenablage-API oder bei Ablehnung – manuell kopieren; leeres HTML wird nie kopiert', async () => {
|
||
assert.equal(await ersatzKopieren('<p>x</p>', undefined), 'manuell')
|
||
assert.equal(await ersatzKopieren('<p>x</p>', null), 'manuell')
|
||
assert.equal(await ersatzKopieren('<p>x</p>', {}), 'manuell')
|
||
assert.equal(await ersatzKopieren('<p>x</p>', { writeText: 'kein Funktion' }), 'manuell')
|
||
assert.equal(await ersatzKopieren('<p>x</p>', { writeText: async () => { throw new Error('NotAllowedError') } }), 'manuell')
|
||
assert.equal(await ersatzKopieren('<p>x</p>', { writeText: () => { throw new Error('sofort') } }), 'manuell')
|
||
let aufgerufen = 0
|
||
const clipboard = { writeText: async () => { aufgerufen++ } }
|
||
for (const leer of ['', undefined, null, 5]) assert.equal(await ersatzKopieren(leer, clipboard), 'leer', String(leer))
|
||
assert.equal(aufgerufen, 0)
|
||
})
|
||
|
||
test('ersatzMeldung: bestätigt das Kopieren sichtbar, nennt den Plan und warnt, wenn die Zwischenablage fehlt', () => {
|
||
assert.deepEqual(ersatzMeldung('kopiert'), { ok: true, text: 'Ersatz-HTML kopiert.' })
|
||
assert.deepEqual(ersatzMeldung('kopiert', 'Café (cafe)'), { ok: true, text: 'Ersatz-HTML kopiert: Café (cafe).' })
|
||
assert.deepEqual(ersatzMeldung('manuell'), { ok: false, text: ZWISCHENABLAGE_FEHLT })
|
||
assert.match(ZWISCHENABLAGE_FEHLT, /Zwischenablage/)
|
||
assert.equal(ersatzMeldung('leer').ok, false)
|
||
assert.equal(ersatzMeldung('irgendwas').ok, false)
|
||
})
|
||
|
||
test('abschaltMeldung: angelegt, aktuell, fehler und ohne Angabe', () => {
|
||
assert.deepEqual(abschaltMeldung('angelegt'), { ok: true, text: 'Gespeichert. Ticket „Ersatz-Tabelle aktualisieren“ angelegt.' })
|
||
assert.deepEqual(abschaltMeldung('aktuell'), { ok: true, text: 'Gespeichert. Die Ersatz-Tabelle ist aktuell, kein Ticket nötig.' })
|
||
assert.deepEqual(abschaltMeldung(undefined), { ok: true, text: 'Gespeichert.' })
|
||
assert.deepEqual(abschaltMeldung('etwas-neues'), { ok: true, text: 'Gespeichert.' })
|
||
})
|
||
|
||
test('abschaltMeldung: bei „fehler“ eine sichtbare Warnung – Ticket von Hand anlegen, ein zweites Abschalten gibt es nicht', () => {
|
||
const m = abschaltMeldung('fehler')
|
||
assert.equal(m.ok, false)
|
||
assert.match(m.text, /^Gespeichert, aber/)
|
||
assert.match(m.text, /„Ersatz-Tabelle aktualisieren“/)
|
||
assert.match(m.text, /konnte nicht angelegt werden/)
|
||
assert.match(m.text, /von Hand/)
|
||
assert.doesNotMatch(m.text, /24 Uhr/)
|
||
})
|