Filipe zum Bildschirmfoto der Wissen-Seite: "erstens sieht das richtig
scheisse aus". Zu sehen war die Kategorie-Auswahl quer ueber "STUFE",
"GERAET", "VEROEFFENTLICHT AM" und den Tag-Knoepfen. Text auf Text.
Zweimal dieselbe Ursache, zweimal ein Raster, das Kinder in eine Zelle
zwingt, die zu klein ist:
1. FORMULAR "NEUE ANLEITUNG" (aufgaben.css). Die Regel
`grid-template-rows: 1fr 44px` gibt jeder Zelle eine feste
Eingabezeile -- richtig und wichtig, damit alle Felder auf einer
Linie sitzen. Die Zelle "Hauptkategorie" enthaelt aber keine
Eingabe, sondern sechs Gruppen mit zwanzig Knoepfen. Gemessen: der
Kasten 40 px hoch, sein Inhalt 442 -- 402 px liefen ueber alles
darunter. Unter 560 px passierte das nicht, weil dort ohnehin
`auto auto` gilt; deshalb sah es am Handy richtig aus.
Es ist exakt die Falle, die eine Regel darueber schon einmal
zugeschnappt ist ("EINE ZELLE, DIE AUFKLAPPT ..."). Dieselbe
Antwort, diesmal fuer .katwahl.
2. DATEILISTE (dateien.css). .datei hat die Spalten `34px 1fr auto`.
Die ersten vier Kinder sitzen richtig; alles danach wird automatisch
platziert und landet in der ZEICHENSPALTE. Gemessen: "Fassung 2" in
34 px Breite, 22 px herausragend; auf 390 px zusaetzlich die
Metazeile ("1,9 MB" in zwei Zeilen) und "Sichtbar fuer" mit 49 px
Ueberstand.
ZWEI NEUE PRUEFUNGEN, und die erste war erst selbst falsch:
pruef-wissen-formular.mjs verglich zunaechst den KASTEN der Auswahl mit
dem der Zelle und meldete gruen, waehrend das Bildschirmfoto die
Ueberlappung deutlich zeigte. Der Kasten ist ja brav 44 px hoch --
herausgelaufen ist sein INHALT. Jetzt wird scrollHeight gegen
clientHeight gemessen, und die Pruefung wird rot, bevor der Fix
dazukommt. pruef-dateien-liste.mjs legt bewusst zweimal dieselbe Datei
ab, damit "Fassung 2" und "nicht mehr aktuell" ueberhaupt entstehen.
Versionsstempel neu gesetzt (202610011426, 670 Verweise in 45 Dateien).
Ohne ihn liegt die Korrektur auf dem Server und kommt bei niemandem an:
Cache-Control steht auf immutable.
Co-Authored-By: Claude Opus 5 <[email protected]>
192 lines
9.0 KiB
JavaScript
192 lines
9.0 KiB
JavaScript
/* 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);
|