diff --git a/server/pruef-livepunkt.mjs b/server/pruef-livepunkt.mjs new file mode 100644 index 00000000..b4d64586 --- /dev/null +++ b/server/pruef-livepunkt.mjs @@ -0,0 +1,253 @@ +/* ===================================================================== + pruef-livepunkt.mjs — Der LIVE-Punkt, wenn er sendet + --------------------------------------------------------------------- + Filipe, 22.09.2026: „in dieser kachel soll wenn ich live bin um 20h + bis 22h ein live button der richtig geil ist mit einem live roten + punkt symbol am besten, das soll aufblinken für zwei stunden. man + soll nicht drauf drücken können aber so dass es auffällt. … die + leute sollen auch automatisch eine benarichtigung bekommen um 20 + uhr dass ich live bin." + + DIE WICHTIGSTE FRAGE IST NICHT „BLINKT ES" + Sie lautet: Was passiert, wenn der Live-Dienst NICHT antwortet? + Die öffentliche Seite macht daraus seit jeher ein „nicht live" + (`assets/js/streamplan.js`, `.catch()` setzt `istLive = false`). + Wer das liest, macht die Seite zu und verpasst den Stream — und + niemand merkt je, dass es an einer Zeile liegt. + + Deshalb prüft diese Datei vor allem den dritten Ausgang: live / + wartet / weiß nicht, an sechs Antworten des Dienstes durchgespielt. + + DIE ZWEITWICHTIGSTE: BLINKT ES AUCH DANN, WENN ES NICHT SOLL? + Wer Bewegungsarmut eingestellt hat, macht das meist wegen + Schwindel oder Migräne. Ein Punkt, der trotzdem pulst, ist dann + keine Information, sondern eine Zumutung — und man sieht es beim + Bauen nicht, weil der eigene Rechner die Einstellung nicht hat. + ===================================================================== */ + +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; + +const HIER = dirname(fileURLToPath(import.meta.url)); +const W = join(HIER, "..", "workspace"); + +let ok = 0, fehl = 0; +const pruef = (name, bedingung, zusatz = "") => { + if (bedingung) { ok++; console.log(` OK ${name}`); } + else { fehl++; console.log(` FEHL ${name}${zusatz ? " — " + zusatz : ""}`); } +}; +const lies = (p) => readFileSync(join(W, p), "utf8"); + +console.log("\n===== DER LIVE-PUNKT =====\n"); + +/* ===================================================================== + 1. DIE AUSLEGUNG — live / wartet / weiss nicht + ===================================================================== */ +console.log("=== Drei Antworten, nicht zwei ==="); +{ + /* Die Funktion wird aus der ausgelieferten Datei geholt, nicht + nachgebaut. Eine Nachbildung wäre die zweite Fassung, die anders + altert — dann prüfte diese Datei sich selbst. */ + const quelle = lies("assets/js/live.js"); + const fenster = { + window: {}, document: { addEventListener() {} }, + fetch: () => { throw new Error("nicht benutzt"); }, + setInterval: () => 0, clearInterval: () => {}, + }; + const fn = new Function("window", "document", "fetch", "setInterval", "clearInterval", + quelle + "\nreturn window.LiveStatus;"); + const L = fn(fenster.window, fenster.document, fenster.fetch, + fenster.setInterval, fenster.clearInterval); + + pruef("live.js liefert eine Auslegung", typeof L?.auswerten === "function"); + + const faelle = [ + { was: "er sendet", d: { ok: true, live: true, title: "Abendrunde" }, soll: "live" }, + { was: "er sendet nicht", d: { ok: true, live: false, source: "automatisch", autoHealthy: true }, soll: "wartet" }, + { was: "der Pruefer ist gestoert", d: { ok: true, live: false, source: "automatisch", autoHealthy: false }, soll: "weissnicht" }, + { was: "von Hand auf 'nicht live' gesetzt", d: { ok: true, live: false, source: "hand", autoHealthy: false }, soll: "wartet" }, + { was: "der Dienst meldet einen Fehler", d: { ok: false }, soll: "weissnicht" }, + { was: "gar keine Antwort", d: null, soll: "weissnicht" }, + ]; + for (const f of faelle) { + const r = L.auswerten(f.d); + pruef(`${f.was} -> ${f.soll}`, r.zustand === f.soll, `kam: ${r.zustand}`); + } + + /* DER KERN. Ohne diese Zeile wäre alles oben Dekoration. */ + pruef('"weiss nicht" ist NICHT dasselbe wie "wartet"', + L.auswerten({ ok: true, live: false, source: "automatisch", autoHealthy: false }).zustand + !== L.auswerten({ ok: true, live: false, source: "automatisch", autoHealthy: true }).zustand); + + pruef("der Titel kommt mit, wenn er sendet", + L.auswerten({ ok: true, live: true, title: "Abendrunde" }).titel === "Abendrunde"); + + /* Das Abzeichen -- Wort UND Punkt, damit ein Vorleseprogramm nicht + nur einen leeren Kreis ansagt. */ + fenster.document.createElement = (tag) => ({ + tagName: tag.toUpperCase(), className: "", textContent: "", + kinder: [], appendChild(k) { this.kinder.push(k); }, + setAttribute(a, w) { this["_" + a] = w; }, + }); + const a = L.abzeichen("LIVE"); + pruef("das Abzeichen traegt einen Punkt und ein Wort", + a.kinder.length === 2 && a.kinder.some((k) => /wort/.test(k.className)) + && a.kinder.some((k) => /punkt/.test(k.className))); + pruef("und einen lesbaren Text (nicht nur einen Kreis)", + a.kinder.some((k) => k.textContent === "LIVE")); + pruef("der Punkt allein ist fuer Vorleseprogramme verborgen", + a.kinder.some((k) => /punkt/.test(k.className) && k["_aria-hidden"] === "true")); +} + +/* ===================================================================== + 2. ES IST KEIN KNOPF + ===================================================================== */ +console.log("\n=== Man kann nicht draufdruecken ==="); +{ + const css = lies("assets/css/module.css"); + const block = css.slice(css.indexOf(".live-marke {")); + pruef("die Marke nimmt keine Klicks an (pointer-events: none)", + /\.live-marke\s*\{[^}]*pointer-events:\s*none/s.test(css)); + pruef("und laesst sich nicht markieren (user-select: none)", + /\.live-marke\s*\{[^}]*user-select:\s*none/s.test(css)); + pruef("sie ist kein