/* Prueft die Formularzeilen auf allen Seiten. Beanstandet am 01.09.2026: "das sieht scheisse aus, so verzogen". Gemeint war die Zeile im Kalender -- "Art" schmal, "Beginn" breit, "Dauer (Minuten)" zweizeilig umgebrochen, alle Felder auf verschiedenen Hoehen. Ursache war eine Regel, die nur fuer Kinder mit der Klasse .feld galt. Der Kalender benutzt schlichte
ohne Klasse; die fielen hindurch, bekamen von zwoelf Rasterspalten je EINE und wurden nur so breit, wie ihr Inhalt sie zwang. Geprueft wird deshalb nicht "sieht gut aus", sondern messbar: * alle Eingabefelder einer Zeile gleich hoch * ihre Unterkanten auf einer Linie (sonst haengt eines tiefer) * kein Feld absurd schmal * keine Beschriftung mehrzeilig */ import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; const ordner = mkdtempSync(join(tmpdir(), "ws-form-")); process.env.WORKSPACE_DB = join(ordner, "workspace.db"); process.env.PORT = "4203"; process.env.SITE_ACCESS_SECRET = "lokaler-test"; process.env.SITE_ACCESS_CODE_DOGI = "DOGI-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, 700)); const BASIS = "http://127.0.0.1:4203"; setTimeout(() => { console.log("ABBRUCH"); process.exit(1); }, 250_000).unref?.(); 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)); } } 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 { chromium } = await import( "file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs"); const browser = await chromium.launch(); /* Seite, Knopf zum Aufklappen des Formulars. */ const FORMULARE = [ ["kalender.html", "#neu-oeffnen"], ["aufgaben.html", "#neu-oeffnen"], ["personen.html", "#neu-oeffnen"], ["bereich.html?b=live", "#neu-oeffnen"], ["scouting.html", "#neu-oeffnen"], ]; for (const [breite, wie] of [[1280, "Computer"], [390, "Handy"]]) { console.log(`\n=== ${wie} (${breite}px)`); const kontext = await browser.newContext({ viewport: { width: breite, height: 1000 } }); const seite = await kontext.newPage(); await seite.goto(BASIS + "/workspace/", { waitUntil: "domcontentloaded" }); 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: 15000 }); for (const [s, knopf] of FORMULARE) { await seite.goto(BASIS + "/workspace/" + s, { waitUntil: "networkidle" }); await seite.waitForTimeout(700); const da = await seite.locator(knopf).count(); if (!da) { console.log(` -- ${s}: kein Formularknopf`); continue; } await seite.click(knopf).catch(() => {}); await seite.waitForTimeout(450); const r = await seite.evaluate(() => { const raster = document.querySelector(".neu__raster"); if (!raster) return null; const felder = [...raster.children].filter((k) => k.offsetParent !== null); const eingaben = felder .map((f) => f.querySelector("input, select, textarea")) .filter(Boolean) .map((e) => e.getBoundingClientRect()); const schilder = felder .map((f) => f.querySelector(".feld-schild")) .filter(Boolean); /* Mehrzeilige Beschriftung: tatsaechliche Hoehe gegen eine Zeile. */ const mehrzeilig = schilder.filter((s2) => { const zeile = parseFloat(getComputedStyle(s2).lineHeight) || 16; return s2.getBoundingClientRect().height > zeile * 1.6; }).map((s2) => s2.textContent.trim()); return { anzahl: eingaben.length, hoehen: eingaben.map((e) => Math.round(e.height)), /* Auf 3 px genau zusammengefasst: Unterkanten liegen durch Teilpixel-Rundung selten exakt gleich, obwohl sie auf einer Linie sitzen. Ein erster Anlauf meldete deshalb "3 Unterkanten" bei drei Feldern derselben Reihe -- ein Messfehler, kein Gestaltungsfehler. */ unterkanten: eingaben.map((e) => Math.round(e.bottom / 3) * 3), breiten: eingaben.map((e) => Math.round(e.width)), einzeln: felder.map((f) => { const e=f.querySelector("input, select, textarea"); if(!e) return null; const r2=e.getBoundingClientRect(); const fr=f.getBoundingClientRect(); return { id:e.id, tag:e.tagName, top:Math.round(r2.top), bottom:Math.round(r2.bottom), h:Math.round(r2.height), feldTop:Math.round(fr.top), feldH:Math.round(fr.height) }; }).filter(Boolean), mehrzeilig, }; }); if (!r || r.anzahl < 2) { console.log(` -- ${s}: kein Raster mit mehreren Feldern`); continue; } const hoehenGleich = new Set(r.hoehen).size === 1; /* Unterkanten auf einer Linie -- auf dem Handy stehen die Felder untereinander, dort gilt das natuerlich nicht. */ const reihen = new Set(r.unterkanten).size; const buendig = breite < 700 ? true : reihen <= Math.ceil(r.anzahl / 3); const nichtWinzig = Math.min(...r.breiten) >= 90; const gut = hoehenGleich && buendig && nichtWinzig && !r.mehrzeilig.length; if (!gut) fehler++; console.log(` ${gut ? "ok " : "FEHL"} ${String(s).padEnd(20)}` + `${r.anzahl} Felder Höhen=${[...new Set(r.hoehen)].join("/")} ` + `Breiten=${Math.min(...r.breiten)}–${Math.max(...r.breiten)}` + (hoehenGleich ? "" : " UNGLEICH HOCH") + (buendig ? "" : ` NICHT BÜNDIG (Unterkanten ${r.unterkanten.join(",")})`) + (nichtWinzig ? "" : " ZU SCHMAL") + (r.mehrzeilig.length ? ` MEHRZEILIG: ${r.mehrzeilig.join(", ")}` : "") + (gut ? "" : " | " + r.einzeln.map((e) => `${e.id}(${e.tag}) Feld:${e.feldTop}+${e.feldH} Eingabe:${e.top}..${e.bottom}`).join(" "))); } await kontext.close(); } await browser.close(); try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ } console.log(fehler ? `\n${fehler} Formular(e) fehlerhaft.` : "\nAlles in Ordnung."); process.exit(fehler ? 1 : 0);