/* WISSEN: DAS FORMULAR "NEUE ANLEITUNG" (01.10.2026) Filipe zum Bildschirmfoto: "erstens sieht das richtig scheisse aus". Zu sehen war: die Kategorie-Auswahl lag quer ueber "STUFE", "GERAET", "VEROEFFENTLICHT AM" und den Tag-Knoepfen. Text auf Text. URSACHE (gemessen): aufgaben.css zwingt JEDES direkte Kind von .neu__raster in zwei Rasterzeilen -- `grid-template-rows: 1fr 44px`. Das ist richtig und wichtig, damit alle Eingabefelder auf derselben Linie sitzen. Die Zelle "Hauptkategorie" enthaelt aber keine 44 px hohe Eingabe, sondern .katwahl: sechs Gruppen mit zwanzig Knoepfen. Die passen nicht in 44 px und laufen ueber alles darunter. Dieselbe Falle war schon einmal da und ist im CSS dokumentiert ("EINE ZELLE, DIE AUFKLAPPT, PASST NICHT IN EINE FESTE ZEILE") -- damals fuer die aufklappbare Personenliste. Diese Pruefung sorgt dafuer, dass sie nicht ein drittes Mal zuschnappt. GEMESSEN WIRD NICHT "sieht gut aus", sondern: Ueberlappen sich zwei Felder des Rasters? Das ist eine Zahl, und sie ist eindeutig. DREI AUSGAENGE: 0 in Ordnung / 1 Befunde / 2 konnte nicht arbeiten. */ import { mkdtempSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; const ordner = mkdtempSync(join(tmpdir(), "ws-wissenform-")); process.env.WORKSPACE_DB = join(ordner, "workspace.db"); const { eigenerPort } = await import("./helfer-port.mjs"); const PORT = await eigenerPort(import.meta, "die Wissen-Formularpruefung"); process.env.PORT = `${PORT}`; process.env.SITE_ACCESS_SECRET = "lokaler-test"; 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"); const { notbremse } = await import("./helfer-notbremse.mjs"); notbremse(420_000, "pruef-wissen-formular"); await new Promise((r) => setTimeout(r, 1200)); const BASIS = `http://127.0.0.1:${PORT}`; let geprueft = 0, fehler = 0; const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; }; /* ---- eine Person, die ablegen darf ---------------------------------- */ const { DatabaseSync } = await import("node:sqlite"); const { scryptSync, randomBytes } = await import("node:crypto"); await fetch(BASIS + "/workspace/api/anmelden", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ rolle: "admin", code: "x" }) }).catch(() => {}); 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)); } } const d = new DatabaseSync(process.env.WORKSPACE_DB); const salt = randomBytes(16).toString("hex"); const hash = scryptSync("CODE-DOGI-0001", 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("Filipe", "admin", hash, salt, 32768, new Date().toISOString()); d.close(); const pw = await import("file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs"); const browser = await pw.chromium.launch(); /* Die Breiten sind nicht geraten: 1165 ist die aus Filipes Bildschirmfoto, 1280 der haeufigste Notebook-Wert, 900 und 412 die beiden Umbruchstufen des Rasters (860 und 560) von oben bzw. unten her. */ const BREITEN = [1440, 1280, 1165, 900, 412]; for (const breite of BREITEN) { const ctx = await browser.newContext({ viewport: { width: breite, height: 1000 }, hasTouch: breite <= 860 }); const seite = await ctx.newPage(); const konsole = []; seite.on("pageerror", (f) => konsole.push("Absturz: " + f.message)); seite.on("console", (m) => { if (m.type() === "error") konsole.push("Konsole: " + m.text()); }); await seite.goto(BASIS + "/workspace/", { waitUntil: "networkidle" }); await seite.click('.rolle[data-rolle="admin"]'); await seite.fill("#code", "CODE-DOGI-0001"); await seite.click("button[type=submit]"); await seite.waitForURL("**/start.html", { timeout: 25000 }); await seite.goto(BASIS + "/workspace/wissen.html", { waitUntil: "networkidle" }); await seite.waitForTimeout(800); console.log(`\n--- ${breite} px ---`); const aufKnopf = seite.locator("#neu-auf"); if (!(await aufKnopf.count()) || !(await aufKnopf.isVisible())) { console.log(" KONNTE NICHT ARBEITEN: Knopf \"PDF hinzufuegen\" nicht sichtbar."); await ctx.close(); await browser.close(); process.exit(2); } await aufKnopf.click(); await seite.waitForTimeout(700); /* Eine Kategorie waehlen: Erst dann erscheint der Beschreibungstext unter der Auswahl -- und genau der lag auf Filipes Bild quer ueber der naechsten Ueberschrift. Ohne Auswahl waere die Pruefung blind fuer den schlimmeren der beiden Faelle. */ const ersteKat = seite.locator("#n-kategorie button").first(); if (await ersteKat.count()) { await ersteKat.click(); await seite.waitForTimeout(400); } const befund = await seite.evaluate(() => { const raster = document.querySelector("#neu-form .neu__raster"); if (!raster) return { fehlt: true }; const kinder = [...raster.children]; const kasten = (el) => { const r = el.getBoundingClientRect(); return { o: r.top, u: r.bottom, l: r.left, r: r.right, h: r.height }; }; /* 1. Ueberlappen sich zwei Felder? */ const ueber = []; for (let i = 0; i < kinder.length; i++) { for (let j = i + 1; j < kinder.length; j++) { const a = kasten(kinder[i]), b = kasten(kinder[j]); const sen = Math.min(a.u, b.u) - Math.max(a.o, b.o); const waa = Math.min(a.r, b.r) - Math.max(a.l, b.l); if (sen > 1 && waa > 1) { ueber.push({ a: i + 1, b: j + 1, px: Math.round(sen), at: (kinder[i].textContent || "").trim().slice(0, 28), bt: (kinder[j].textContent || "").trim().slice(0, 28) }); } } } /* 2. Laeuft der INHALT der Kategorie-Auswahl aus ihr heraus? ERST FALSCH GEMESSEN (01.10.2026): Der erste Versuch verglich den KASTEN der Auswahl mit dem der Zelle -- und meldete gruen, waehrend das Bildschirmfoto die Ueberlappung deutlich zeigte. Der Kasten ist naemlich brav 44 px hoch; herausgelaufen ist sein INHALT. Eine Pruefung, die den Kasten misst, kann das nie sehen. Gemessen wird deshalb scrollHeight gegen clientHeight und zusaetzlich das unterste Kind. */ const wahl = raster.querySelector(".katwahl"); let raus = null, ueberHang = null, hoch = null; if (wahl) { ueberHang = Math.round(wahl.scrollHeight - wahl.clientHeight); const w = wahl.getBoundingClientRect(); hoch = Math.round(w.height); let tiefstes = w.bottom; for (const kind of wahl.children) { const k = kind.getBoundingClientRect(); if (k.bottom > tiefstes) tiefstes = k.bottom; } raus = Math.round(tiefstes - w.bottom); } return { fehlt: false, anzahl: kinder.length, ueber, raus, ueberHang, hoch }; }); if (befund.fehlt) { console.log(" KONNTE NICHT ARBEITEN: Das Formularraster wurde nicht gefunden."); await ctx.close(); await browser.close(); process.exit(2); } ok(befund.anzahl >= 6, `das Raster hat ${befund.anzahl} Felder`); ok(befund.raus !== null, `die Kategorie-Auswahl ist da (${befund.hoch} px hoch)`); ok(befund.ueberHang !== null && befund.ueberHang <= 1, `der Inhalt passt in die Auswahl (${befund.ueberHang} px Ueberhang)`); ok(befund.raus !== null && befund.raus <= 1, `nichts ragt unten heraus (${befund.raus} px)`); ok(befund.ueber.length === 0, `kein Feld liegt auf einem anderen (${befund.ueber.length} Ueberlappungen)`); for (const u of befund.ueber) { console.log(` Feld ${u.a} ("${u.at}") liegt ${u.px} px auf Feld ${u.b} ("${u.bt}")`); } ok(konsole.length === 0, `keine Fehler in der Konsole${konsole.length ? " -> " + konsole.join(" | ") : ""}`); /* Zum Hinsehen, wenn eine Zahl nicht reicht: SCHUSS=ja node pruef-wissen-formular.mjs */ if (process.env.SCHUSS === "ja") { const { mkdirSync } = await import("node:fs"); mkdirSync(join(process.cwd(), "schuss-wissenform"), { recursive: true }); await seite.locator("#neu-block").screenshot({ path: join(process.cwd(), "schuss-wissenform", `form-${breite}.png`) }); console.log(` Bild: schuss-wissenform/form-${breite}.png`); } await ctx.close(); } await browser.close(); console.log(`\n${geprueft} Pruefungen, ${fehler} Befunde`); /* Die Zahl gehoert in die BEDINGUNG: Eine Pruefung, die nichts gemessen hat, darf nicht gruen sein. */ if (geprueft < BREITEN.length * 6) { console.log("ERGEBNIS: KONNTE NICHT ARBEITEN (zu wenige Messungen)"); process.exit(2); } console.log(fehler ? "ERGEBNIS: NICHT IN ORDNUNG" : "ERGEBNIS: IN ORDNUNG"); process.exit(fehler ? 1 : 0);