diff --git a/pruef-ampel.png b/pruef-ampel.png index 6b2dea56..1b72d289 100644 Binary files a/pruef-ampel.png and b/pruef-ampel.png differ diff --git a/pruef-aufgabenbrett.png b/pruef-aufgabenbrett.png index 2267eda8..642a0130 100644 Binary files a/pruef-aufgabenbrett.png and b/pruef-aufgabenbrett.png differ diff --git a/pruef-buehne.png b/pruef-buehne.png index 8ca07666..b5f73a80 100644 Binary files a/pruef-buehne.png and b/pruef-buehne.png differ diff --git a/pruef-checkliste.png b/pruef-checkliste.png index e2ff793e..6e13a0a5 100644 Binary files a/pruef-checkliste.png and b/pruef-checkliste.png differ diff --git a/pruef-content-computer.png b/pruef-content-computer.png index f89e6c73..be38fe95 100644 Binary files a/pruef-content-computer.png and b/pruef-content-computer.png differ diff --git a/pruef-content-creator.png b/pruef-content-creator.png index c01f76b3..5cdcc1c0 100644 Binary files a/pruef-content-creator.png and b/pruef-content-creator.png differ diff --git a/pruef-content-handy.png b/pruef-content-handy.png index cd976bf5..c3e95276 100644 Binary files a/pruef-content-handy.png and b/pruef-content-handy.png differ diff --git a/pruef-handy.png b/pruef-handy.png index 86b68b04..9b411163 100644 Binary files a/pruef-handy.png and b/pruef-handy.png differ diff --git a/pruef-kalender-computer.png b/pruef-kalender-computer.png index 889447c6..823e8054 100644 Binary files a/pruef-kalender-computer.png and b/pruef-kalender-computer.png differ diff --git a/pruef-kalender-handy.png b/pruef-kalender-handy.png index 754dd5db..b065742f 100644 Binary files a/pruef-kalender-handy.png and b/pruef-kalender-handy.png differ diff --git a/pruef-lesbarkeit-wissen.png b/pruef-lesbarkeit-wissen.png index 95eb76ee..4cc3f9d9 100644 Binary files a/pruef-lesbarkeit-wissen.png and b/pruef-lesbarkeit-wissen.png differ diff --git a/pruef-merkzeichen.png b/pruef-merkzeichen.png index 23e92f7c..bf623aa5 100644 Binary files a/pruef-merkzeichen.png and b/pruef-merkzeichen.png differ diff --git a/pruef-personen.png b/pruef-personen.png index b10fb65b..7c1021dc 100644 Binary files a/pruef-personen.png and b/pruef-personen.png differ diff --git a/pruef-protokoll-auf.png b/pruef-protokoll-auf.png index 28dcdaf7..f7384a75 100644 Binary files a/pruef-protokoll-auf.png and b/pruef-protokoll-auf.png differ diff --git a/pruef-protokoll-zu.png b/pruef-protokoll-zu.png index 712675fc..4d67dde1 100644 Binary files a/pruef-protokoll-zu.png and b/pruef-protokoll-zu.png differ diff --git a/pruef-schulung.png b/pruef-schulung.png index 38d51f60..13d4ee4e 100644 Binary files a/pruef-schulung.png and b/pruef-schulung.png differ diff --git a/pruef-scout-zuteilung.png b/pruef-scout-zuteilung.png index 8b5388d6..eeac0895 100644 Binary files a/pruef-scout-zuteilung.png and b/pruef-scout-zuteilung.png differ diff --git a/pruef-sprung.png b/pruef-sprung.png index 6b48e0bc..ed402e24 100644 Binary files a/pruef-sprung.png and b/pruef-sprung.png differ diff --git a/pruef-start-computer.png b/pruef-start-computer.png index b99db0cf..b017eaf3 100644 Binary files a/pruef-start-computer.png and b/pruef-start-computer.png differ diff --git a/pruef-start-creator.png b/pruef-start-creator.png index e237bfd1..6acff491 100644 Binary files a/pruef-start-creator.png and b/pruef-start-creator.png differ diff --git a/pruef-start-handy.png b/pruef-start-handy.png index 3b6dba53..6fb23d7d 100644 Binary files a/pruef-start-handy.png and b/pruef-start-handy.png differ diff --git a/pruef-team.png b/pruef-team.png index 147068ff..2b4556d6 100644 Binary files a/pruef-team.png and b/pruef-team.png differ diff --git a/pruef-uebersicht-computer.png b/pruef-uebersicht-computer.png index 867d9c84..61c699da 100644 Binary files a/pruef-uebersicht-computer.png and b/pruef-uebersicht-computer.png differ diff --git a/pruef-uebersicht-creator.png b/pruef-uebersicht-creator.png index b3b1255c..8eddd2ff 100644 Binary files a/pruef-uebersicht-creator.png and b/pruef-uebersicht-creator.png differ diff --git a/pruef-uebersicht-handy.png b/pruef-uebersicht-handy.png index 21867ee4..6bb68643 100644 Binary files a/pruef-uebersicht-handy.png and b/pruef-uebersicht-handy.png differ diff --git a/pruef-zustand.png b/pruef-zustand.png index 8cd114a9..077c6581 100644 Binary files a/pruef-zustand.png and b/pruef-zustand.png differ diff --git a/server/pruef-ampel.mjs b/server/pruef-ampel.mjs index 60ad2cec..b6efcf34 100644 --- a/server/pruef-ampel.mjs +++ b/server/pruef-ampel.mjs @@ -24,6 +24,14 @@ await new Promise((r) => setTimeout(r, 700)); const BASIS = "http://127.0.0.1:4206"; setTimeout(() => { console.log("ABBRUCH"); process.exit(1); }, 250_000).unref?.(); +/* ORTSZEIT, nicht UTC. Die Anwendung rechnet "heute" nach der Uhr des + Benutzers; toISOString() liefert dagegen UTC und damit zwischen + Mitternacht und 2 Uhr den VORTAG. In diesem Fenster schlug diese + Pruefung fehl, ohne dass etwas kaputt war. */ +const tagText = (d = new Date()) => + `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}` + + `-${String(d.getDate()).padStart(2, "0")}`; + let fehler = 0; const ok = (b, t) => { console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; }; @@ -57,12 +65,12 @@ d.prepare("INSERT INTO betreuung (creator_id, betreuer_id, seit) VALUES (?,?,?)" d.prepare(`INSERT INTO eintraege (bereich, art, titel, text, datum, status, creator_id, erstellt, erstellt_von) VALUES ('live','vorbereitung','Ton geprüft','Eine Minute aufnehmen.',?,'offen',?,?,?)`) - .run(jetzt.slice(0, 10), idTili, jetzt, idScout); + .run(tagText(), idTili, jetzt, idScout); const eintragId = d.prepare("SELECT last_insert_rowid() AS id").get().id; d.prepare(`INSERT INTO eintraege (bereich, art, titel, text, datum, status, creator_id, erstellt, erstellt_von) VALUES ('live','vorbereitung','Fremder Eintrag','x',?,'offen',?,?,?)`) - .run(jetzt.slice(0, 10), idFremd, jetzt, idDogi); + .run(tagText(), idFremd, jetzt, idDogi); const fremdEintrag = d.prepare("SELECT last_insert_rowid() AS id").get().id; d.close(); diff --git a/server/pruef-aufgabenbrett.mjs b/server/pruef-aufgabenbrett.mjs index 3efe593c..3765e497 100644 --- a/server/pruef-aufgabenbrett.mjs +++ b/server/pruef-aufgabenbrett.mjs @@ -48,6 +48,14 @@ await new Promise((r) => setTimeout(r, 700)); const BASIS = "http://127.0.0.1:4216"; setTimeout(() => { console.log("ABBRUCH"); process.exit(1); }, 300_000).unref?.(); +/* ORTSZEIT, nicht UTC. Die Anwendung rechnet "heute" nach der Uhr des + Benutzers; toISOString() liefert dagegen UTC und damit zwischen + Mitternacht und 2 Uhr den VORTAG. In diesem Fenster schlug diese + Pruefung fehl, ohne dass etwas kaputt war. */ +const tagText = (d = new Date()) => + `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}` + + `-${String(d.getDate()).padStart(2, "0")}`; + let fehler = 0; const ok = (b, t) => { console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; }; @@ -65,9 +73,9 @@ for (let i = 0; i < 60; i++) { } const d = new DatabaseSync(process.env.WORKSPACE_DB); const jetzt = new Date().toISOString(); -const heute = jetzt.slice(0, 10); -const gestern = new Date(Date.now() - 86400_000).toISOString().slice(0, 10); -const morgen = new Date(Date.now() + 86400_000).toISOString().slice(0, 10); +const heute = tagText(); +const gestern = tagText(new Date(Date.now() - 86400_000)); +const morgen = tagText(new Date(Date.now() + 86400_000)); function anlegen(name, rolle, code) { const salt = randomBytes(16).toString("hex"); diff --git a/server/pruef-bereiche-lesend.mjs b/server/pruef-bereiche-lesend.mjs index 2dcb2a30..f5c15f9b 100644 --- a/server/pruef-bereiche-lesend.mjs +++ b/server/pruef-bereiche-lesend.mjs @@ -30,6 +30,14 @@ await new Promise((r) => setTimeout(r, 700)); const BASIS = "http://127.0.0.1:4183"; setTimeout(() => { console.log("ABBRUCH"); process.exit(1); }, 180_000).unref?.(); +/* ORTSZEIT, nicht UTC. Die Anwendung rechnet "heute" nach der Uhr des + Benutzers; toISOString() liefert dagegen UTC und damit zwischen + Mitternacht und 2 Uhr den VORTAG. In diesem Fenster schlug diese + Pruefung fehl, ohne dass etwas kaputt war. */ +const tagText = (d = new Date()) => + `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}` + + `-${String(d.getDate()).padStart(2, "0")}`; + let fehler = 0; const ok = (b, t) => { console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; }; @@ -57,7 +65,7 @@ d.prepare("INSERT INTO betreuung (creator_id, betreuer_id, seit) VALUES (?,?,?)" damit auch das Loeschen "eigener" Eintraege geprueft wird. */ const eigenerId = (() => { d.prepare("INSERT INTO eintraege (bereich, art, titel, text, datum, status, creator_id, erstellt, erstellt_von) VALUES ('live','auswertung','Von Anna selbst','alt',?,'offen',?,?,?)") - .run(jetzt.slice(0, 10), idAnna, jetzt, idAnna); + .run(tagText(), idAnna, jetzt, idAnna); return d.prepare("SELECT last_insert_rowid() AS id").get().id; })(); d.close(); @@ -85,7 +93,7 @@ for (const b of BEREICHE) { const a = await ruf(`/workspace/api/bereich/${b}`, kDogi, { method: "POST", body: JSON.stringify({ art: arten[b], titel: "Von der Betreuung", text: "…", - datum: jetzt.slice(0, 10), creator_id: idAnna }), + datum: tagText(), creator_id: idAnna }), }); const j = await a.json().catch(() => ({})); angelegt[b] = j.id; @@ -96,7 +104,7 @@ console.log("\n2) Der Scout darf ebenfalls – er betreut Anna"); { const a = await ruf("/workspace/api/bereich/live", kScout, { method: "POST", - body: JSON.stringify({ art: "vorbereitung", titel: "Vom Scout", datum: jetzt.slice(0, 10), creator_id: idAnna }), + body: JSON.stringify({ art: "vorbereitung", titel: "Vom Scout", datum: tagText(), creator_id: idAnna }), }); ok(a.ok, `Scout darf eintragen (${a.status})`); } @@ -119,7 +127,7 @@ console.log("\n4) Der Creator kann NICHTS schreiben – in keinem Bereich, auf k for (const b of BEREICHE) { const neu = await ruf(`/workspace/api/bereich/${b}`, kCreator, { method: "POST", - body: JSON.stringify({ art: "idee", titel: "Selbst geschrieben", datum: jetzt.slice(0, 10) }), + body: JSON.stringify({ art: "idee", titel: "Selbst geschrieben", datum: tagText() }), }); ok(neu.status === 403, `${b}: anlegen abgelehnt (${neu.status})`); @@ -159,7 +167,7 @@ console.log("\n6) Nichts hat sich tatsächlich verändert"); console.log("\n7) Die Antwort erklärt, was los ist"); { const a = await ruf("/workspace/api/bereich/live", kCreator, { - method: "POST", body: JSON.stringify({ art: "idee", titel: "x", datum: jetzt.slice(0, 10) }) }); + method: "POST", body: JSON.stringify({ art: "idee", titel: "x", datum: tagText() }) }); const j = await a.json(); console.log(" ->", j.fehler); ok(/Betreuer/.test(j.fehler || ""), "die Meldung sagt, wer einträgt"); diff --git a/server/pruef-content-ansicht.mjs b/server/pruef-content-ansicht.mjs index f39ef77b..958df87d 100644 --- a/server/pruef-content-ansicht.mjs +++ b/server/pruef-content-ansicht.mjs @@ -20,6 +20,14 @@ await new Promise((r) => setTimeout(r, 700)); const BASIS = "http://127.0.0.1:4185"; setTimeout(() => { console.log("ABBRUCH"); process.exit(1); }, 200_000).unref?.(); +/* ORTSZEIT, nicht UTC. Die Anwendung rechnet "heute" nach der Uhr des + Benutzers; toISOString() liefert dagegen UTC und damit zwischen + Mitternacht und 2 Uhr den VORTAG. In diesem Fenster schlug diese + Pruefung fehl, ohne dass etwas kaputt war. */ +const tagText = (d = new Date()) => + `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}` + + `-${String(d.getDate()).padStart(2, "0")}`; + let fehler = 0; const ok = (b, t) => { console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; }; @@ -31,7 +39,7 @@ const { DatabaseSync } = await import("node:sqlite"); const { scryptSync, randomBytes } = await import("node:crypto"); const d = new DatabaseSync(process.env.WORKSPACE_DB); const jetzt = new Date().toISOString(); -const heute = jetzt.slice(0, 10); +const heute = tagText(); function anlegen(name, rolle, code) { const salt = randomBytes(16).toString("hex"); const hash = scryptSync(code, salt, 64, { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex"); @@ -62,7 +70,7 @@ video("skript", "Antwort auf eure Fragen", null, "Community", "Talking Head", nu video("gedreht", "Mein Tag als Creator", "Um 6 klingelt der Wecker – freiwillig.", "Hinter den Kulissen", "Story", null); video("gedreht", "Die 3 häufigsten Fragen", "Nummer 2 fragt mir wirklich jeder.", "Community", "Talking Head", null); video("fertig", "Licht richtig setzen", "Ein Fenster reicht. Ehrlich.", "Tutorials", "Tutorial", - new Date(Date.now() + 2 * 86400_000).toISOString().slice(0, 10)); + tagText(new Date(Date.now() + 2 * 86400_000))); video("fertig", "Verpasst und überfällig", "Guck bis zum Schluss.", "Tutorials", "Tutorial", "2026-01-05"); for (let i = 0; i < 9; i++) video("veroeffentlicht", "Draußen " + i, "x", "Tutorials", "Tutorial", null); for (let i = 0; i < 4; i++) video("veroeffentlicht", "Community " + i, "x", "Community", "Talking Head", null); diff --git a/server/pruef-content.mjs b/server/pruef-content.mjs index bdf26680..74d84e1a 100644 --- a/server/pruef-content.mjs +++ b/server/pruef-content.mjs @@ -22,6 +22,14 @@ await new Promise((r) => setTimeout(r, 700)); const BASIS = "http://127.0.0.1:4184"; setTimeout(() => { console.log("ABBRUCH"); process.exit(1); }, 200_000).unref?.(); +/* ORTSZEIT, nicht UTC. Die Anwendung rechnet "heute" nach der Uhr des + Benutzers; toISOString() liefert dagegen UTC und damit zwischen + Mitternacht und 2 Uhr den VORTAG. In diesem Fenster schlug diese + Pruefung fehl, ohne dass etwas kaputt war. */ +const tagText = (d = new Date()) => + `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}` + + `-${String(d.getDate()).padStart(2, "0")}`; + let fehler = 0; const ok = (b, t) => { console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; }; @@ -33,7 +41,7 @@ const { DatabaseSync } = await import("node:sqlite"); const { scryptSync, randomBytes } = await import("node:crypto"); const d = new DatabaseSync(process.env.WORKSPACE_DB); const jetzt = new Date().toISOString(); -const heute = jetzt.slice(0, 10); +const heute = tagText(); function anlegen(name, rolle, code) { const salt = randomBytes(16).toString("hex"); const hash = scryptSync(code, salt, 64, { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex"); @@ -162,7 +170,7 @@ const ids = {}; method: "POST", body: JSON.stringify({ art, titel, hook, datum: heute, creator_id: idAnna, saeule_id: saeuleA, format: "Tutorial", - geplant: art === "fertig" ? new Date(Date.now() + 2 * 86400_000).toISOString().slice(0, 10) : null }), + geplant: art === "fertig" ? tagText(new Date(Date.now() + 2 * 86400_000)) : null }), }); ids[art] = (await jsn(a)).id; ok(a.ok, `„${titel}" auf Stufe ${art} (${a.status})`); diff --git a/server/pruef-grosscheck.mjs b/server/pruef-grosscheck.mjs new file mode 100644 index 00000000..cc4ca101 --- /dev/null +++ b/server/pruef-grosscheck.mjs @@ -0,0 +1,515 @@ +/* DER GROSSCHECK — jede Seite, jede Rolle, Handy und Rechner. + + Auftrag vom 02.09.2026: "ich will, dass du jetzt einmal einen grossen + Check machst, ob alles klappt, auf dem Handy und PC." + + WAS DIESE PRUEFUNG ANDERS MACHT ALS DIE ANDEREN. + + Die uebrigen dreissig Pruefungen fragen jeweils "stimmt diese eine + Sache?". Diese hier fragt "faellt IRGENDWO etwas auseinander?" -- und + geht dafuer stumpf ueber alles: sechzehn Workspace-Seiten mal vier + Rollen mal zwei Bildschirmgroessen, dazu jede oeffentliche Seite. + + Solche Rundgaenge finden andere Fehler als gezielte Pruefungen: nicht + den falsch gerechneten Wert, sondern die Seite, die bei genau einer + Rolle leer bleibt, oder die auf dem Handy zwei Pixel ueberlaeuft. + + GEMESSEN WIRD, WAS EIN MENSCH MERKEN WUERDE: + 1. Kommt die Seite ueberhaupt an (Inhalt da, nicht nur Geruest)? + 2. Steht etwas seitlich ueber? (Auf dem Handy der schlimmste Fehler.) + 3. Meldet der Browser einen Fehler? + 4. Ist Text abgeschnitten? + 5. Sind Beruehrziele auf dem Handy gross genug (24 px, WCAG 2.5.8)? + 6. Liegt etwas Bedienbares unter etwas anderem? + 7. Ist Schrift zu klein zum Lesen? + + DIE ANZAHL GEHOERT IN DIE BEDINGUNG. Eine Seite, die gar nichts + enthaelt, waere sonst die sauberste von allen -- kein Ueberstand, + keine zu kleine Schrift, kein Fehler. Deshalb wird gezaehlt, WIE VIEL + auf jeder Seite ueberhaupt angesehen wurde, und "nichts gefunden" + gilt als Fehler. + + Am Ende steht eine Gegenprobe: eine absichtlich zu breite Kachel muss + gemeldet werden. Eine Pruefung, die immer bestaetigt, bestaetigt + nichts. */ + +import { mkdtempSync, rmSync, readdirSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +const ordner = mkdtempSync(join(tmpdir(), "ws-gross-")); +process.env.WORKSPACE_DB = join(ordner, "workspace.db"); +process.env.PORT = "4218"; +process.env.SITE_ACCESS_SECRET = "lokaler-test"; +process.env.SITE_ACCESS_CODE_DOGI = "DOGI-TEST-1"; +process.env.SITE_ACCESS_CODE_VANVAN = "VANVAN-TEST-1"; +process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00"; + +const express = (await import("express")).default; +const ec = express.response.cookie; +express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); }; +await import("./index.js"); +await new Promise((r) => setTimeout(r, 800)); +const BASIS = "http://127.0.0.1:4218"; +setTimeout(() => { console.log("ABBRUCH — zu lange gelaufen"); process.exit(1); }, 900_000).unref?.(); + +/* ORTSZEIT, nicht UTC. Die Anwendung rechnet "heute" nach der Uhr des + Benutzers; toISOString() liefert dagegen UTC und damit zwischen + Mitternacht und 2 Uhr den VORTAG. In diesem Fenster schlug diese + Pruefung fehl, ohne dass etwas kaputt war. */ +const tagText = (d = new Date()) => + `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}` + + `-${String(d.getDate()).padStart(2, "0")}`; + +let fehler = 0; +const ok = (b, t) => { console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; }; + +await fetch(BASIS + "/workspace/api/anmelden", { + method: "POST", headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ rolle: "admin", code: "x" }) }).catch(() => {}); + +const { DatabaseSync } = await import("node:sqlite"); +const { scryptSync, randomBytes } = await import("node:crypto"); +for (let i = 0; i < 60; i++) { + try { + const p = new DatabaseSync(process.env.WORKSPACE_DB); + try { p.prepare("SELECT 1 FROM personen LIMIT 1").get(); break; } finally { p.close(); } + } catch { await new Promise((r) => setTimeout(r, 250)); } +} + +/* ---- Ein Bestand, der die Seiten wirklich fuellt ---------------------- + Leere Seiten pruefen nichts. Jede Rolle bekommt Daten, damit auf jeder + Seite tatsaechlich etwas steht -- sonst misst der Rundgang leere + Geruesten und meldet sie als tadellos. */ +const d = new DatabaseSync(process.env.WORKSPACE_DB); +const jetzt = new Date().toISOString(); +const heute = tagText(); +const gestern = tagText(new Date(Date.now() - 86400_000)); +const morgen = tagText(new Date(Date.now() + 86400_000)); + +function anlegen(name, rolle, code) { + const salt = randomBytes(16).toString("hex"); + const hash = scryptSync(code, salt, 64, { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex"); + d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n, aktiv, erstellt) VALUES (?,?,?,?,?,1,?)") + .run(name, rolle, hash, salt, 32768, jetzt); + return d.prepare("SELECT last_insert_rowid() AS id").get().id; +} +/* Lange Namen -- kurze wuerden Ueberlauf-Fehler verstecken. */ +const idDogi = anlegen("Filipe Fernandes Ribeiro", "admin", "CODE-DOGI-0001"); +const idMara = anlegen("Mara Schmidt-Hohenberg", "manager", "CODE-MANA-0001"); +const idPat = anlegen("Patrick von Hohenstein", "scout", "CODE-SCOU-0001"); +const idTili = anlegen("SpongBobSchwammKopf", "creator", "CODE-ANNA-0001"); +const idLuna = anlegen("Luna", "creator", "CODE-LUNA-0001"); + +d.prepare("INSERT INTO betreuung (creator_id, betreuer_id, seit) VALUES (?,?,?)").run(idTili, idPat, jetzt); +d.prepare("INSERT INTO betreuung (creator_id, betreuer_id, seit) VALUES (?,?,?)").run(idLuna, idMara, jetzt); +d.prepare("INSERT INTO scout_zuteilung (scout_id, manager_id, seit) VALUES (?,?,?)").run(idPat, idMara, jetzt); + +const LANG = "Ein absichtlich sehr langer Titel ohne Leerzeichenumbrueche " + + "damit ein Ueberlauf auffaellt statt sich zu verstecken"; +for (const [titel, st, frist, cr, ve] of [ + [LANG, "offen", gestern, idTili, idPat], + ["Kurz", "arbeit", heute, idTili, idPat], + ["Review-Sache", "review", morgen, idLuna, idMara], + ["Fertig", "erledigt", null, idLuna, idMara], +]) { + d.prepare(`INSERT INTO aufgaben (titel, beschreibung, status, prioritaet, frist, + creator_id, verantwortlich_id, erstellt, erstellt_von) VALUES (?,?,?,?,?,?,?,?,?)`) + .run(titel, "Beschreibung mit etwas Text.", st, "hoch", frist, cr, ve, jetzt, idDogi); +} +for (const [t, art, beginn] of [ + ["Wochen-Call mit einem sehr langen Namen", "call", heute + "T10:00"], + ["Review", "review", morgen + "T14:00"], +]) { + d.prepare(`INSERT INTO termine (titel, art, beginn, dauer_min, creator_id, erstellt, erstellt_von) + VALUES (?,?,?,?,?,?,?)`).run(t, art, beginn, 30, idTili, jetzt, idPat); +} +for (const b of ["live", "community", "technik", "schutz", "content"]) { + d.prepare(`INSERT INTO eintraege (bereich, art, titel, text, status, dringlichkeit, + datum, creator_id, erstellt, erstellt_von) + VALUES (?,?,?,?,?,?,?,?,?,?)`) + .run(b, b === "content" ? "idee" : "vorbereitung", LANG, "Text dazu.", + "offen", "hoch", heute, idTili, jetzt, idPat); +} +d.prepare(`INSERT INTO leads (name, plattform, handle, status, prioritaet, scout_id, erstellt, + naechster_followup) VALUES (?,?,?,?,?,?,?,?)`) + .run("Ein Lead mit langem Namen", "tiktok", "@langername", "angesprochen", "hoch", + idPat, jetzt, gestern); +try { + d.prepare(`INSERT INTO wissen (titel, beschreibung, kategorie, stufe, geraet, + name_original, name_datei, groesse, wichtig, veroeffentlicht, hochgeladen, erstellt_von) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`) + .run(LANG, "Eine Anleitung.", "hilfe", "einsteiger", "egal", + "a.pdf", "x1.pdf", 1000, 1, heute, jetzt, idDogi); +} catch { /* Tabelle evtl. anders benannt -- der Rundgang laeuft trotzdem */ } +d.close(); + +/* ---------------------------------------------------------------- */ + +const { chromium } = await import( + "file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs"); +const browser = await chromium.launch(); + +/* Die Messung laeuft IM Browser -- nur dort weiss man, was wirklich + gezeichnet wurde. */ +const messen = () => { + const sichtbar = (e) => { + const r = e.getBoundingClientRect(); + return r.width > 0 && r.height > 0; + }; + const name = (e) => (e.tagName + "." + String(e.className || "").split(" ")[0]).slice(0, 28) + + (e.textContent ? ` "${e.textContent.trim().slice(0, 14)}"` : ""); + + const alle = [...document.querySelectorAll("body *")]; + + /* ABSICHTLICH UNSICHTBAR IST KEIN FEHLER. + wahl.js ersetzt jedes Auswahlfeld durch einen eigenen Knopf; das + echte