Drei Fehlalarme abgestellt -- und zwei Schriften, die wirklich zu klein waren

ERSTENS: die drei Ladehinweise, die der Rollen-Rundgang meldete.

#liste auf werdegang.html, #personen und #meine-karte auf
entwicklung.html sollten Vorleseprogrammen dauerhaft "wird geladen"
melden. Nachgemessen stimmt das nicht: Alle drei stehen in einem
Abschnitt mit `hidden`, und gate.css setzt hausweit
`[hidden] { display: none !important; }`. Sie werden nicht
dargestellt und sind damit aus dem Baum draussen, den Vorleseprogramme
lesen. Dort hoert nie jemand etwas.

Meine eigene Notiz behauptete das Gegenteil -- ausfuehrlich begruendet
und trotzdem falsch, weil ich sie hergeleitet statt gemessen hatte.
Genau der Fall, vor dem die Projektnotiz warnt.

Die Messung zaehlt jetzt nur noch, was auch dasteht. Mit
`checkVisibility()` und nicht mit der Kasten-Rechnung daneben: Ein
sichtbarer, aber noch leerer Ladebehaelter hat Hoehe 0 -- die
Kasten-Rechnung haette ausgerechnet den durchgewunken, fuer den die
Zeile da ist.

Dazu zwei Gegenproben je Rolle, mit DERSELBEN Funktion, die der
Rundgang benutzt: Ein sichtbarer Ladehinweis MUSS gefunden werden, ein
verborgener darf es nicht. 16 von 16 gruen -- die Pruefung kann also
weiterhin rot werden, sie sieht nur nicht mehr dorthin, wo niemand
hinsieht. Und der Befund nennt jetzt das Element, statt nur zu zaehlen.

ZWEITENS: zwei Schriften unter 11,5 px.

pruef-css-klassen zaehlte 44 statt 42. Welche zwei neu waren, sagte die
Meldung nicht -- sie zeigte `zuKlein.slice(-6)`, und das ist nach
DATEINAMEN sortiert, nicht nach Alter. Sie zeigte damit auf
uebersicht.css und wissen.css, die seit Wochen unveraendert dastehen.
Gefunden wurden die echten durch Nachzaehlen ueber die letzten vierzig
Commits:

  bereich.css  .ev-mitmacher__schild    .7rem  = 11,20 px  (03.10.)
  chat.css     .chat-nachricht__bearbeitet .68rem = 10,88 px (04.10.)

Beide bekommen .72rem -- nicht geraten, sondern der Wert ihrer
direkten Nachbarn: Die beiden anderen __schild in bereich.css stehen
schon auf .72rem, und der Chat-Vermerk soll laut seinem eigenen
Kommentar "so leise wie die Zeit daneben" sein, und die hat .72rem.
Ueberlaufen kann dadurch nichts, beide Elternelemente haben
`flex-wrap: wrap`.

Die irrefuehrende Meldung ist mit korrigiert: Sie sagt jetzt, was sie
weiss (Verteilung je Datei), und nennt den Weg zu dem, was sie nicht
wissen kann -- statt mit "vermutlich" auf Unschuldige zu zeigen.

DRITTENS: `erklaert` wurde seit dem ersten Tag gemessen und nie benutzt.

Im Kopf von pruef-rollen steht "Ein leerer Bereich OHNE ERKLAERUNG
sieht aus wie ein Fehler". Die Erklaerung wurde auch ermittelt -- und
dann verworfen; gemeldet wurde jede kurze Seite. Eine Seite, die zu
Recht leer ist und das ordentlich sagt, waere als Fehler dagestanden,
und der naheliegende "Fix" waere gewesen, die Grenze fuer alle zu
senken. Jetzt wirkt das Feld. Heute aendert es nichts: keiner der 384
Durchgaenge liegt unter 120 Zeichen.

Gemessen
  pruef-rollen       384 Pruefungen, 0 Fehler, 1 nicht nachsehbar
                     (vorher 368 -- die 16 neuen sind die Gegenproben)
  pruef-css-klassen  alles in Ordnung, 42 Stellen unter 11,5 px,
                     Grundlinie wieder erreicht

Der erste Lauf endete mit Rueckgabewert 3, weil ich waehrenddessen
eine Datei gespeichert habe -- die Pruefung hat ihren eigenen Schutz
gegen "misst einen Stand, den es nicht mehr gibt" an mir vorgefuehrt.
Die Zahlen oben stammen aus dem sauberen Lauf danach.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-10-06 10:30:26 +02:00
co-authored by Claude Opus 5
parent 0e45afc719
commit 26aec420f4
50 changed files with 886 additions and 719 deletions
+166 -30
View File
@@ -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