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 VON_HAND = 'Einbau läuft (Team setzt die Skript-Zeile von Hand)' const WARTET = 'wartet auf die Zeiten des Kunden' test('zeitenStatusText: Einrichtung ohne Hindernis, Einbau angestossen – Leisten sind live, der Einbau läuft', () => { assert.equal(zeitenStatusText({ state: 'setup', hindernis: null, einbau: 'angestossen' }), EINBAU) assert.equal(zeitenStatusText({ state: 'setup', einbau: 'angestossen' }), EINBAU, 'ohne Feld hindernis') assert.equal(zeitenStatusText({ state: 'setup', hindernis: '', einbau: 'angestossen' }), EINBAU) }) test('zeitenStatusText: Einrichtung mit einbau manuell oder leer – nie "Leisten live", das Team setzt die Zeile von Hand', () => { for (const einbau of ['manuell', '', null, undefined, 'irgendwas_neues']) { assert.equal(zeitenStatusText({ state: 'setup', hindernis: null, einbau }), VON_HAND, JSON.stringify(einbau)) } assert.equal(zeitenStatusText({ state: 'setup' }), VON_HAND, 'ohne Feld einbau') assert.doesNotMatch(VON_HAND, /Leisten live/) }) test('zeitenStatusText: Einrichtung mit hindernis "zeiten_fehlen" – wartet auf die Zeiten des Kunden (geht jedem Einbau-Text vor)', () => { assert.equal(zeitenStatusText({ state: 'setup', hindernis: 'zeiten_fehlen' }), WARTET) for (const einbau of ['angestossen', 'manuell', '', null]) { assert.equal(zeitenStatusText({ state: 'setup', hindernis: 'zeiten_fehlen', einbau }), WARTET, JSON.stringify(einbau)) } }) test('zeitenStatusText: unbekanntes Hindernis zeigt nichts Erfundenes, sondern den Einbau-Text', () => { assert.equal(zeitenStatusText({ state: 'setup', hindernis: 'irgendwas_neues', einbau: 'angestossen' }), EINBAU) assert.equal(zeitenStatusText({ state: 'setup', hindernis: 'irgendwas_neues', einbau: 'manuell' }), VON_HAND) }) 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, einbau: 'angestossen', aboEndeAt }), `${EINBAU} · ${frist}`) assert.equal(zeitenStatusText({ state: 'setup', hindernis: null, einbau: 'manuell', aboEndeAt }), `${VON_HAND} · ${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', einbau: 'angestossen' }, { state: 'setup', einbau: 'manuell' }, { 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('