diff --git a/server/pruef-css-klassen.mjs b/server/pruef-css-klassen.mjs index 45a65aa7..95c75faa 100644 --- a/server/pruef-css-klassen.mjs +++ b/server/pruef-css-klassen.mjs @@ -355,13 +355,44 @@ console.log("\n=== Ist irgendeine Schrift zu klein? ==="); jetzt .72rem. Die Grundlinie wird MITGEZOGEN -- sonst duerfte der Fortschritt lautlos wieder verlorengehen, und eine Grundlinie, die nur nach oben nachgibt, ist keine. */ + /* DIE MELDUNG HAT AUF DIE FALSCHEN DATEIEN GEZEIGT (06.10.2026). + + Hier stand frueher „Neu hinzugekommen ist vermutlich eine davon" + und dann `zuKlein.slice(-6)`. Diese Liste ist aber nach + DATEINAMEN sortiert, nicht nach Alter -- die letzten sechs sind + schlicht die am Ende des Alphabets. Bei zwei neuen Stellen in + `bereich.css` und `chat.css` zeigte die Meldung auf + `uebersicht.css` und `wissen.css`, die seit Wochen unveraendert + dastanden. Ich habe daraufhin eine Stunde an der falschen Stelle + gesucht. + + Ein Hinweis, der mit „vermutlich" auf Unschuldige zeigt, ist + schlechter als gar keiner: Ohne ihn haette ich gleich gesucht. + Sie sagt jetzt, was sie WEISS (wie viele, in welcher Datei), und + nennt den Weg zu dem, was sie nicht wissen kann. Der Weg ist + nachgemessen und hat die beiden Stellen in zwei Minuten + gefunden. */ const GRUNDLINIE = 42; + const jeDatei = {}; + for (const z of zuKlein) { + const n = z.split(":")[0]; + jeDatei[n] = (jeDatei[n] || 0) + 1; + } + const verteilung = Object.entries(jeDatei) + .sort((a, b) => b[1] - a[1]) + .map(([n, z]) => `${n} ${z}`) + .join(" · "); ok(zuKlein.length <= GRUNDLINIE && gemessen > 150, zuKlein.length > GRUNDLINIE ? `${zuKlein.length} Stellen unter ${GRENZE_PX} px — das sind ` - + `${zuKlein.length - GRUNDLINIE} MEHR als die Grundlinie ${GRUNDLINIE}. ` - + `Neu hinzugekommen ist vermutlich eine davon:\n ` - + zuKlein.slice(-6).join("\n ") + + `${zuKlein.length - GRUNDLINIE} MEHR als die Grundlinie ${GRUNDLINIE}.` + + `\n Verteilung: ${verteilung}` + + "\n WELCHE neu ist, steht hier nicht -- die Liste ist nach" + + " Dateinamen sortiert, nicht nach Alter." + + "\n So findet man sie (nachgemessen, dauert zwei Minuten):" + + " dasselbe Zaehlen ueber `git archive workspace/assets/css`" + + "\n fuer die letzten Commits laufen lassen und den Sprung" + + " von 42 auf 43 suchen." : `${zuKlein.length} Stellen unter ${GRENZE_PX} px ` + `(Grundlinie ${GRUNDLINIE}, nicht mehr geworden)`); diff --git a/server/pruef-rollen.mjs b/server/pruef-rollen.mjs index 3e91c624..cfe0743f 100644 --- a/server/pruef-rollen.mjs +++ b/server/pruef-rollen.mjs @@ -322,6 +322,81 @@ const istEgal = (t) => EGAL.some((r) => r.test(t)); const berichte = []; +/* ===================================================================== + DIE MESSUNG EINER SEITE -- an EINER Stelle, weil sie zweimal + gebraucht wird: einmal fuer die Seite selbst, einmal fuer die + Gegenprobe weiter unten. Eine zweite, danebengeschriebene Fassung + waere die, die beim naechsten Umbau auseinanderlaeuft -- und dann + prueft die Gegenprobe etwas anderes als der Rundgang. + + Sie laeuft IM BROWSER (Playwright schickt den Quelltext hinueber), + darf also nichts aus diesem Modul benutzen. + ===================================================================== */ +function zustandMessen() { + const sichtbar = (e) => { + const k = e.getBoundingClientRect(); + return k.width > 0 && k.height > 0 && getComputedStyle(e).visibility !== "hidden"; + }; + /* Verweise, die ins Leere zeigen. Ein Knopf, der nichts tut, ist + schlimmer als kein Knopf: Man klickt ihn immer wieder. */ + const tote = [...document.querySelectorAll("a[href]")] + .filter(sichtbar) + .filter((a) => { + const h = a.getAttribute("href"); + return !h || h === "#" || h === "javascript:void(0)"; + }) + .map((a) => a.textContent.trim().slice(0, 40)); + + /* ================================================================== + EIN LADEHINWEIS ZAEHLT NUR, WENN ER AUCH DASTEHT (06.10.2026) + + Diese Prüfung meldete drei Befunde, und alle drei waren falsch: + `#liste` auf werdegang.html, `#personen` und `#meine-karte` auf + entwicklung.html. Alle drei stehen in einem Abschnitt, der + `hidden` traegt, und `gate.css` setzt hausweit + `[hidden] { display: none !important; }`. Sie werden also gar + nicht dargestellt und sind damit auch aus dem Baum draussen, den + Vorleseprogramme lesen -- niemand bekommt dort je ein "wird + geladen" zu hoeren. + + Ich hatte das Gegenteil notiert und sauber begruendet ("meldet + Vorleseprogrammen dauerhaft, die Seite laedt"). Die Begruendung + klang schluessig und war trotzdem falsch, weil ich sie nicht + nachgemessen hatte. Und eine Warnung, die bei jedem Lauf kommt, + ohne dass jemand etwas davon hat, ist keine Warnung mehr -- sie + bringt einem bei, die naechste zu ueberlesen, und die koennte + echt sein. + + `checkVisibility()` statt der Kasten-Rechnung oben: Ein + Ladebehaelter, der sichtbar, aber noch LEER ist, hat Hoehe 0 -- + die Kasten-Rechnung wuerde ihn durchwinken, und das waere genau + der Fall, den diese Zeile fangen soll. `checkVisibility` fragt + dagegen, ob das Element ueberhaupt dargestellt wird. + + Kann der Browser die Frage nicht beantworten (sehr alt), wird + NICHT stillschweigend gefiltert, sondern alles gemeldet und das + Nichtwissen weitergereicht -- siehe `kannSichtbarkeit`. + ================================================================== */ + const kannSichtbarkeit = typeof Element.prototype.checkVisibility === "function"; + const ladend = [...document.querySelectorAll('[aria-busy="true"]')]; + const haengt = (kannSichtbarkeit ? ladend.filter((e) => e.checkVisibility()) : ladend) + .map((e) => e.id || e.getAttribute("class") || e.tagName.toLowerCase()); + + /* Sichtbarer Text insgesamt -- eine Seite, auf der praktisch + nichts steht, ist entweder leer oder kaputt. */ + const text = (document.querySelector("main")?.innerText || "").trim(); + return { + tote, + laenge: text.length, + /* Ein sichtbarer Hinweis, der die Leere erklärt. */ + erklaert: [...document.querySelectorAll(".leer-hinweis, .leer, .k-leer, .block__frage")] + .some((e) => e.getBoundingClientRect().height > 0 && e.textContent.trim().length > 25), + haengt, + kannSichtbarkeit, + ueber: document.documentElement.scrollWidth - document.documentElement.clientWidth, + }; +} + for (const r of ROLLEN) { /* `--ignore-certificate-errors` als ARGUMENT und nicht nur `ignoreHTTPSErrors` am Kontext: Die Domain steht in der @@ -408,47 +483,108 @@ for (const r of ROLLEN) { await seite.goto(wurzel + "/workspace/" + pfad, { waitUntil: "networkidle" }); await seite.waitForTimeout(700); - const zustand = await seite.evaluate(() => { - const sichtbar = (e) => { - const k = e.getBoundingClientRect(); - return k.width > 0 && k.height > 0 && getComputedStyle(e).visibility !== "hidden"; - }; - /* Verweise, die ins Leere zeigen. Ein Knopf, der nichts tut, ist - schlimmer als kein Knopf: Man klickt ihn immer wieder. */ - const tote = [...document.querySelectorAll("a[href]")] - .filter(sichtbar) - .filter((a) => { - const h = a.getAttribute("href"); - return !h || h === "#" || h === "javascript:void(0)"; - }) - .map((a) => a.textContent.trim().slice(0, 40)); - /* Sichtbarer Text insgesamt -- eine Seite, auf der praktisch - nichts steht, ist entweder leer oder kaputt. */ - const text = (document.querySelector("main")?.innerText || "").trim(); - return { - tote, - laenge: text.length, - /* Ein sichtbarer Hinweis, der die Leere erklärt. */ - erklaert: [...document.querySelectorAll('.leer-hinweis, .leer, .k-leer, .block__frage')] - .some((e) => e.getBoundingClientRect().height > 0 && e.textContent.trim().length > 25), - /* Steht ein Ladehinweis noch da, ist etwas haengengeblieben. */ - haengt: [...document.querySelectorAll('[aria-busy="true"]')].length, - ueber: document.documentElement.scrollWidth - document.documentElement.clientWidth, - }; - }); + const zustand = await seite.evaluate(zustandMessen); + /* DER DRITTE AUSGANG: Kann der Browser die Sichtbarkeit nicht + beantworten, meldet die Messung ALLE Ladehinweise -- auch die + verborgenen. Dann ist ein Befund hier kein Befund am Code, + sondern ein Hinweis auf die Messung. Darf nicht schweigen. */ + if (!zustand.kannSichtbarkeit) { + unklar(`${r.name.padEnd(18)} ${pfad.padEnd(22)} dieser Browser kennt` + + " checkVisibility() nicht -- Ladehinweise ungefiltert gezaehlt"); + } const probleme = []; if (konsole.length) probleme.push("Konsole: " + konsole.slice(0, 2).join(" | ").slice(0, 160)); if (schlechteAntworten.length) probleme.push("Server: " + [...new Set(schlechteAntworten)].slice(0, 3).join(", ")); if (zustand.tote.length) probleme.push("tote Verweise: " + zustand.tote.slice(0, 3).join(", ")); - if (zustand.haengt) probleme.push(`${zustand.haengt}× haengt beim Laden`); - if (zustand.laenge < 120) probleme.push(`fast kein Inhalt (${zustand.laenge} Zeichen)`); + /* Mit NAMEN und nicht nur als Zahl: "3x haengt beim Laden" schickt + einen auf die Suche, "haengt beim Laden: liste" nicht. */ + if (zustand.haengt.length) probleme.push("haengt beim Laden: " + zustand.haengt.join(", ")); + /* ================================================================= + „WORTLOS leer" -- das Wort stand im Kopf dieser Datei und war + nie umgesetzt (gefunden 06.10.2026). + + Oben heisst es seit dem ersten Tag: „Ein leerer Bereich OHNE + ERKLAERUNG sieht aus wie ein Fehler." Gemessen wurde die + Erklaerung auch -- `erklaert` stand im Rueckgabewert. Benutzt + hat sie nie jemand. Eine Seite, die zu Recht leer ist und das + ordentlich sagt („Noch nichts hier"), waere also als Fehler + gemeldet worden. + + Das ist keine Kleinigkeit, sondern eine gestellte Falle: Wer + eines Tages so eine Seite baut, bekommt Rot ohne Fehler und + senkt am wahrscheinlichsten die Grenze von 120 -- fuer alle + Seiten, auch die wirklich kaputten. Eine Messung, die + danebenliegt und nicht benutzt wird, liest sich im Quelltext + wie eine Zusage, die es nicht gibt. + + HEUTE AENDERT DAS NICHTS: Keine der 368 Durchgaenge liegt unter + 120 Zeichen, der Lauf davor war mit der strengen Fassung + ebenso gruen. Die Zeile wirkt erst in dem Fall, fuer den sie + von Anfang an gedacht war -- und sie schweigt ihn nicht weg, + sondern schreibt ihn in den Bericht. */ + if (zustand.laenge < 120) { + if (zustand.erklaert) { + /* NICHT `unklar()`: Das ist der dritte Ausgang fuer „konnte + nicht nachsehen". Hier wurde nachgesehen, und das Ergebnis + ist in Ordnung -- nur erwaehnenswert. Die Seite bekommt + ihren normalen Haken wie jede andere. */ + console.log(` INFO ${r.name.padEnd(18)} ${pfad.padEnd(22)} ist leer` + + ` (${zustand.laenge} Zeichen), sagt aber warum -- kein Fehler`); + } else { + probleme.push(`wortlos leer (${zustand.laenge} Zeichen, kein Hinweis, der es erklaert)`); + } + } if (zustand.ueber > 0) probleme.push(`ragt ${zustand.ueber}px heraus`); ok(!probleme.length, `${r.name.padEnd(18)} ${pfad.padEnd(22)} ${probleme.join(" · ")}`); if (probleme.length) berichte.push({ rolle: r.name, pfad, probleme }); } + /* ===================================================================== + DIE GEGENPROBE ZUM LADEHINWEIS (06.10.2026) + + Seit die Messung oben verborgene Ladehinweise nicht mehr zaehlt, + kann sie auf zwei Arten falsch sein: Sie koennte gar nichts mehr + finden. Eine Pruefung, die immer bestaetigt, bestaetigt nichts -- + also wird hier beides bewiesen, und zwar MIT DERSELBEN Funktion, + die der Rundgang benutzt: + + 1. Ein SICHTBARER Ladehinweis wird gefunden -> sie kann rot. + 2. Ein VERBORGENER wird nicht gefunden -> sie filtert + wirklich nach Sichtbarkeit und nicht nach irgendetwas. + + Beides auf der Seite, auf der die Rolle gerade steht. Die beiden + Knoten verschwinden sofort wieder; der naechste Aufruf laedt die + Seite ohnehin neu. + ===================================================================== */ + await seite.evaluate(() => { + const wo = document.querySelector("main") || document.body; + const sicht = document.createElement("p"); + sicht.id = "gegenprobe-sichtbar"; + sicht.setAttribute("aria-busy", "true"); + sicht.textContent = "wird geladen …"; + wo.append(sicht); + + const huelle = document.createElement("section"); + huelle.hidden = true; + const still = document.createElement("p"); + still.id = "gegenprobe-verborgen"; + still.setAttribute("aria-busy", "true"); + still.textContent = "wird geladen …"; + huelle.append(still); + wo.append(huelle); + }); + const probe = await seite.evaluate(zustandMessen); + ok(probe.haengt.includes("gegenprobe-sichtbar"), + `${r.name.padEnd(18)} Gegenprobe: ein SICHTBARER Ladehinweis wird gefunden`); + ok(!probe.haengt.includes("gegenprobe-verborgen"), + `${r.name.padEnd(18)} Gegenprobe: ein VERBORGENER wird nicht mitgezaehlt`); + await seite.evaluate(() => { + document.getElementById("gegenprobe-sichtbar")?.remove(); + document.getElementById("gegenprobe-verborgen")?.closest("section")?.remove(); + }); + /* ===================================================================== JEDE KACHEL, DIE EINE ROLLE BEKOMMT, MUSS AUCH AUFGEHEN diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index 48e7c690..ab291add 100644 --- a/workspace/anruf-probe.html +++ b/workspace/anruf-probe.html @@ -16,8 +16,8 @@ verdeckt. Gefunden hat es pruef-struktur. Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. --> - - + + - +