Files
dogfather-universe/server/pruef-reaktion-schmal.mjs
T
DogFatherGitandClaude Opus 5 b808171704 Der Kamerahinweis wird wieder sichtbar -- und eine eigene Regression weg
ICH HATTE ES GESTERN FALSCH BEHAUPTET UND HEUTE SELBST VERSCHLIMMERT.
Beides steht hier, weil beides zum Befund gehoert.

WAS ICH GESAGT HATTE: „Auf 390 px ist der Hinweis 0 px hoch und
deshalb nicht zu lesen -- wo er hingehoert, ist eine Gestaltungsfrage
und gehoert Filipe."

Der zweite Halbsatz war eine Ausrede. Im Quelltext stand die ganze
Zeit eine 6-Sekunden-Uhr (reaktion.js, `sagFehler`), die ihn wieder
ausblendet. Statt das nachzusehen, habe ich aus zwei Messungen zu
verschiedenen Zeitpunkten einen Widerspruch gebaut (62 px hier, 0 px
dort) und ihn fuer eine Eigenschaft der Breite gehalten.

ALSO ABGETASTET STATT HERGELEITET (390x844, nach dem Laden):

    nach  500 ms  Text 104 Zeichen · hidden=nein ·   0 px
    nach 5000 ms  Text 104 Zeichen · hidden=nein ·   0 px
    nach 7000 ms  Text 104 Zeichen · hidden=ja   ·   0 px

Die Uhr stimmt also -- und trotzdem ist er die ganzen sechs Sekunden
NULL PIXEL hoch. Ein `role="alert"`, den niemand sehen kann. Das war
auf 390 px schon vorher so.

UND AUF 320 PIXELN HABE ICH ES HEUTE SELBST KAPUTTGEMACHT. Vorher
bekam `#fehler` dort eine stillschweigende fuenfte Rasterzeile mit
71 Pixeln -- sichtbar, aber auf Kosten des Bildes (56 statt 127).
Seit die Regie aus dem Fluss ist, bleibt fuer diese Zeile nichts
uebrig: Der Hinweis kostete nichts mehr und war dafuer unsichtbar.
Von „sichtbar und zu teuer" auf „gratis und wirkungslos" ist keine
Verbesserung.

DIE URSACHE, und sie steht seit dem 30.09. im Haus beschrieben:
`#fehler` ist ein Kind des Saals ohne Platzangabe. Der Saal hat vier
Zeilen; das Feld landet in einer fuenften, die es nicht gibt.

ERSTER REPARATURVERSUCH, GEMESSEN UND ZURUECKGENOMMEN: `grid-row: 2`
ohne `position: absolute`. Damit belegte das Feld die Zelle, der Raum
wich in eine stillschweigende zweite SPALTE aus, und das BILD fiel auf
0 px (320) bzw. 2 px (360). Bei `.raum` steht der gleiche Satz fuer
eine zweite ZEILE -- derselbe Fehler, andere Achse. Ich habe den
Kommentar gelesen, nachdem die Messung ihn mir bestaetigt hatte, nicht
davor.

SO GEHT ES: dasselbe Muster wie beim Pult. `position: absolute` nimmt
das Feld aus dem Fluss -- es belegt keine Zelle und verdraengt nichts.
`grid-row: 2` sagt dann nur noch, WORIN es liegt, `align-self: end`
setzt es an die Unterkante. Keine gerechnete Zahl.

GEMESSEN DANACH:

    320x568   Hinweis 62 px · im Fenster · obenauf · Bild 180 -> 180
    390x844   Hinweis 42 px · im Fenster · obenauf · Bild 219 -> 219
    430x932                                        · Bild 242 -> 242

Sichtbar, wenn er kommt. Nach sechs Sekunden wieder weg. Und er
kostet dem Bild kein Pixel mehr.

DIE PRUEFUNG STELLT DEN ZUSTAND SELBST HER (pruef-reaktion-schmal,
38 -> 44). Im Betrieb erscheint der Hinweis, wenn Kamera oder Mikrofon
fehlen -- auf einem Messrechner immer, auf einem Handy mit Freigabe
nie. Eine Pruefung, die darauf wartet, prueft die Messumgebung. Sie
schreibt den Text jetzt selbst hinein, macht ihn sichtbar, misst Hoehe
UND Bildhoehe, und raeumt wieder auf.

GEPRUEFT: pruef-reaktion-schmal 44 ok · pruef-reaktion 421 ok ·
pruef-css-klassen 37 ok · pruef-community-sicht 10 ok ·
pruef-breiten 23 ok.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-02 14:04:05 +02:00

414 lines
19 KiB
JavaScript

/* DIE REACTION AUF EINEM KLEINEN HANDY (02.10.2026)
=====================================================================
WARUM ES DIESE DATEI GIBT
Auf meiner eigenen Merkliste stand seit dem 01.10.: „320x568 zeigt
noch ein 320x75 grosses Videofeld und eine 1 px schmale
Chatleiste." Nachgemessen stimmte davon keine einzige Zahl:
Regie ZU bei 320x568: Video 127 px, Transport-Unterkante 568
bei Fensterhoehe 568 -- alles erreichbar
Regie AUF bei 320x568: Video 0 px, Transport-Unterkante 1585
Regie AUF bei 390/430: Video unveraendert, Transport am Rand
Und die Chatleiste ist auf JEDER Handybreite `display: none` --
sie liegt dort im Registerstreifen, das ist Absicht und keine
1-px-Leiste.
Eine Bestandsliste ist ein Wegweiser, keine Wahrheit. Auch meine
eigene.
UND WAS SEITDEM PASSIERT IST
Filipe: „auf so apps wie youtube und so geht es doch auch auf dem
handy also perfektionier es endlich."
Er hatte recht. Bei offener Regie stand auf 320 px das Pult IM
RASTER statt darueber -- Bild 0 px, Transportleiste 1017 Pixel
unter dem Fensterrand. Ab 360 px war genau das laengst geloest;
es war dieselbe Seite, nur schmaler. Die 360-Pixel-Sperre ist am
02.10.2026 gefallen (Begruendung und Messreihe in reaktion.css).
Gemessen danach, Regie offen:
320x568 Bild 180 px · Kopf 147 · Schublade 280 · Inhalt 132
360x640 Bild 203 px (unveraendert)
390x844 Bild 219 px (unveraendert)
WAS HIER BEWACHT WIRD -- auf JEDER Breite, ohne Ausnahme:
1. Das Bild behaelt seine Hoehe, zu UND auf.
2. Die Transportleiste bleibt im Fenster.
3. Der Inhalt der Schublade ist erreichbar -- er passt hinein
oder er rollt. Eine Schublade, deren Inhalt man nicht
erreicht, verdeckt nur eleganter als vorher.
4. Die drei grossen Knoepfe behalten ihre 44 Pixel, keiner ist
abgeschnitten, alle liegen obenauf. Schmaler ja, beschnitten
nein.
5. In JEDEM Zustand steht der Knopf, der die Regie wieder
zumacht, im Fenster UND obenauf.
Punkt 2 ist die eigentliche Lehre aus dem Tag: Die alte Fassung
dieser Datei hat den Mangel unter 360 px nur PROTOKOLLIERT statt
behauptet, damit eine spaetere Reparatur hier nicht rot wird. Das
war richtig, solange es keine Reparatur gab -- und ist falsch,
sobald es eine gibt. Eine Pruefung, die eine Reparatur nicht
bemerkt, hat sie nie abgesichert.
DREI AUSGAENGE: 0 in Ordnung · 1 Befunde · 2 konnte nicht arbeiten.
===================================================================== */
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
const ordner = mkdtempSync(join(tmpdir(), "ws-rschmal-"));
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
import { notbremse } from "./helfer-notbremse.mjs";
const { eigenerPort } = await import("./helfer-port.mjs");
const PORT = await eigenerPort(import.meta, "die schmale Reaction");
process.env.PORT = `${PORT}`;
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");
notbremse(300_000, "pruef-reaktion-schmal");
await new Promise((r) => setTimeout(r, 900));
const BASIS = `http://127.0.0.1:${PORT}`;
let fehler = 0, geprueft = 0;
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
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 { chromium } = await import("playwright");
const browser = await chromium.launch();
/* So hoch muss ein Bild mindestens sein, um ein Bild zu sein.
Gemessen am 02.10.2026 mit offener Regie: 180 px auf 320,
203 auf 360, 219 auf 390. 100 laesst Luft nach unten, ohne 0 zu
decken.
(Hier stand bis heute `AUS_DEM_FLUSS_AB = 360` -- die Breite, ab
der die Regie aus dem Fluss genommen wurde. Die Grenze gibt es
nicht mehr, also auch die Zahl nicht. Eine Konstante, die nichts
mehr trennt, ist der Anfang einer Erklaerung, die nicht stimmt.) */
const BILD_MIN = 100;
const GERAETE = [[320, 568, "Handy klein"], [390, 844, "Handy"], [430, 932, "Handy gross"]];
/** Was am Bildschirm wirklich steht -- in einem Zustand der Regie. */
const messen = (seite) => seite.evaluate(() => {
const bild = document.getElementById("leinwand").getBoundingClientRect();
const transport = document.querySelector(".transport")?.getBoundingClientRect();
const zu = document.getElementById("reiter-auf");
const zr = zu?.getBoundingClientRect();
/* IM FENSTER heisst nicht ANKLICKBAR. `elementFromPoint` fragt, was
an dieser Stelle wirklich obenauf liegt -- ein Knopf unter einer
Schublade ist sichtbar und trotzdem nicht zu treffen. */
const obenauf = zr
? document.elementFromPoint(zr.left + zr.width / 2, zr.top + zr.height / 2)
: null;
return {
bild: Math.round(bild.height),
transportUnten: transport ? Math.round(transport.bottom) : null,
fenster: window.innerHeight,
zuImFenster: !!zr && zr.height > 0 && zr.top >= 0 && zr.bottom <= window.innerHeight
&& zr.left >= 0 && zr.right <= window.innerWidth,
zuObenauf: !!obenauf && (obenauf.id === "reiter-auf" || !!obenauf.closest("#reiter-auf")),
};
});
for (const [breite, hoehe, wie] of GERAETE) {
console.log(`\n=== ${wie} (${breite}x${hoehe}) ===`);
const kontext = await browser.newContext({
viewport: { width: breite, height: hoehe },
hasTouch: true, isMobile: true });
const seite = await kontext.newPage();
const konsole = [];
seite.on("pageerror", (e) => konsole.push("Absturz: " + e.message.slice(0, 90)));
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: 20000 });
/* DIE SENDUNG MUSS LAUFEN. Im Zustand „zu" sind Kino und Chat
`display: none` -- wer das misst, misst den geschlossenen Saal
und haelt das Video fuer kaputt. Genau dieser Fehler ist mir
heute beim ersten Anlauf passiert.
Gestellt wird der Zustand ueber die SCHNITTSTELLE, nicht ueber
`dataset.stand` im Browser: Sonst pruefte die Messung einen
Zustand, den das Haus so nie herstellt. */
await seite.evaluate(async () => {
await fetch("/workspace/api/reaktion", {
method: "PUT", credentials: "same-origin",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ titel: "Schmale Probe", video: "dQw4w9WgXcQ" }),
});
await fetch("/workspace/api/reaktion/stand", {
method: "POST", credentials: "same-origin",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ stand: "live" }),
});
});
await seite.goto(BASIS + "/workspace/reaktion.html", { waitUntil: "networkidle" });
await seite.waitForTimeout(1200);
const stand = await seite.evaluate(() => document.getElementById("saal")?.dataset.stand);
ok(stand === "live", `die Sendung laeuft wirklich (data-stand="${stand}") — sonst misst alles darunter nichts`);
/* ==== DIE KAMERAMELDUNG WIRD WEGGERAEUMT, UND ZWAR MIT GRUND ====
Ein kopfloser Browser hat keine Kamera. `getUserMedia`
scheitert, und die Seite schreibt zu Recht „Kamera oder Mikrofon
sind nicht freigegeben…" in `#fehler`. Gemessen werden soll hier
aber die Aufteilung einer LAUFENDEN SENDUNG, nicht die Warnung
eines Rechners ohne Kamera -- sonst misst diese Pruefung eine
Eigenschaft der Messumgebung und nennt sie einen Befund.
WAS DIE MELDUNG KOSTET, IST TROTZDEM GEMESSEN und steht unten im
Protokoll. Am 02.10.2026:
320x568 Bild mit Meldung 56 px · ohne Meldung 127 px
390x844 Bild 601 px -- die Meldung ist dort 0 px HOCH
Beides ist auffaellig und beides gehoert Filipe vorgelegt, nicht
hier stillschweigend repariert: Auf dem kleinen Handy frisst die
Meldung 71 Pixel aus einer impliziten FUENFTEN Rasterzeile (der
Saal deklariert vier), auf dem normalen ist sie ueberhaupt nicht
zu lesen. Eine Meldung, die erklaert, wie man die Kamera
freigibt, und die man dabei nicht sehen kann, ist keine. */
/* GEMESSEN WIRD, WAS SIE KOSTET -- nicht, wie hoch sie gerade ist.
Erster Anlauf las die Hoehe des Feldes selbst. Die hing davon ab,
WANN gelesen wurde: in einem Lauf 62 px, im naechsten 0 px, weil
die Meldung erst eintrifft, wenn `getUserMedia` aufgegeben hat.
Eine Protokollzeile, die mal so und mal so ausfaellt, ist
wertlos.
Der Unterschied VORHER/NACHHER im selben Lauf ist dagegen
stabil: Bild messen, Meldung leeren, Bild noch einmal messen. */
/* ZUERST DIE REGIE ZUMACHEN. Steht sie offen, ist das Bild auf
320 px ohnehin 0 -- dann misst die Kostenrechnung „0 -> 0" und
sagt nichts. Genau das war mein erster Anlauf. */
await seite.evaluate(() => { document.getElementById("pult").dataset.auf = "nein"; });
await seite.waitForTimeout(350);
const kosten = await (async () => {
const vorher = await seite.evaluate(() => Math.round(
document.getElementById("raum").getBoundingClientRect().height));
const text = await seite.evaluate(() => {
const f = document.getElementById("fehler");
const t = (f?.textContent || "").trim();
if (f) f.textContent = "";
return t;
});
await seite.waitForTimeout(350);
const nachher = await seite.evaluate(() => Math.round(
document.getElementById("raum").getBoundingClientRect().height));
return { vorher, nachher, text };
})();
console.log(` Hinweisfeld: ${kosten.text ? `"${kosten.text.slice(0, 44)}…"` : "leer"}`
+ ` · kostet das Bild ${kosten.nachher - kosten.vorher} px`
+ ` (${kosten.vorher} -> ${kosten.nachher})`);
/* ---- Regie zu ---------------------------------------------------- */
await seite.evaluate(() => { document.getElementById("pult").dataset.auf = "nein"; });
await seite.waitForTimeout(350);
const zu = await messen(seite);
console.log(` Regie zu : Bild ${zu.bild} px · Transport endet bei `
+ `${zu.transportUnten} von ${zu.fenster}`);
ok(zu.bild >= BILD_MIN,
`mit geschlossener Regie ist das Bild ${zu.bild} px hoch (mindestens ${BILD_MIN})`);
ok(zu.transportUnten !== null && zu.transportUnten <= zu.fenster + 1,
` und die Transportleiste endet im Fenster (${zu.transportUnten} von ${zu.fenster})`);
ok(zu.zuImFenster && zu.zuObenauf,
" der Knopf zum Auf- und Zumachen steht im Fenster und liegt obenauf");
/* ---- Regie auf --------------------------------------------------- */
await seite.evaluate(() => { document.getElementById("pult").dataset.auf = "ja"; });
await seite.waitForTimeout(350);
const auf = await messen(seite);
console.log(` Regie auf: Bild ${auf.bild} px · Transport endet bei `
+ `${auf.transportUnten} von ${auf.fenster}`);
/* DIE ZUSAGE GILT SEIT DEM 02.10.2026 AUF JEDER BREITE.
Bis dahin stand hier eine Ausnahme fuer alles unter 360 px:
Dort blieb die Regie im Raster, das Bild fiel auf 0 und die
Transportleiste rutschte 1017 Pixel unter den Fensterrand. Die
Ausnahme ist weg, weil der Mangel weg ist -- und genau deshalb
wird hier jetzt BEHAUPTET statt protokolliert. Eine Pruefung,
die eine Reparatur nicht bemerkt, hat die Reparatur nie
abgesichert. */
ok(auf.bild >= BILD_MIN,
`auch mit offener Regie bleibt das Bild ${auf.bild} px hoch`);
ok(auf.transportUnten !== null && auf.transportUnten <= auf.fenster + 1,
` und die Transportleiste bleibt im Fenster (${auf.transportUnten})`);
/* DER INHALT DER SCHUBLADE MUSS ERREICHBAR SEIN.
Eine Schublade, die ueber dem Bild liegt und deren Inhalt man
nicht erreicht, ist kein Fortschritt gegenueber der alten --
sie verdeckt dann nur eleganter. Gemessen wird deshalb, dass
der Inhalt entweder vollstaendig hineinpasst ODER rollt, und
dass die drei grossen Knoepfe ihre 44 Pixel behalten und
obenauf liegen. */
const innen = await seite.evaluate(() => {
const i = document.getElementById("pult-innen");
const p = document.getElementById("pult");
const rollt = (e) => !!e && e.scrollHeight > e.clientHeight + 2;
const knoepfe = [...document.querySelectorAll(".pult__tun .tun")].map((b) => {
const r = b.getBoundingClientRect();
const oben = document.elementFromPoint(
Math.min(innerWidth - 1, Math.max(0, r.left + r.width / 2)),
Math.min(innerHeight - 1, Math.max(0, r.top + r.height / 2)));
return {
wort: (b.textContent || "").trim().slice(0, 14),
hoch: Math.round(r.height),
/* `scrollWidth <= clientWidth` heisst: der Text ist ganz da.
Knoepfe schmaler zu machen ist erlaubt, sie abzuschneiden
nicht. */
ganz: b.scrollWidth <= b.clientWidth + 1,
erreichbar: r.top >= 0 && r.bottom <= innerHeight
&& !!oben && (oben === b || b.contains(oben) || !!oben.closest(".pult__tun")),
};
});
return {
passtOderRollt: !!i && (i.scrollHeight <= i.clientHeight + 2 || rollt(i) || rollt(p)),
sichtbar: i ? Math.round(i.getBoundingClientRect().height) : 0,
knoepfe,
};
});
ok(innen.passtOderRollt,
` der Inhalt der Schublade ist erreichbar (${innen.sichtbar} px sichtbar, Rest rollt)`);
ok(innen.knoepfe.length === 3 && innen.knoepfe.every((k) => k.hoch >= 44),
` die drei grossen Knoepfe behalten ihre 44 Pixel `
+ `(${innen.knoepfe.map((k) => k.hoch).join("/")})`);
ok(innen.knoepfe.every((k) => k.ganz),
" und keiner ist abgeschnitten — schmaler ja, beschnitten nein");
ok(innen.knoepfe.every((k) => k.erreichbar),
` alle drei sind im Fenster und liegen obenauf `
+ `(${innen.knoepfe.map((k) => k.wort).join(", ")})`);
ok(auf.zuImFenster && auf.zuObenauf,
" und der Knopf zum Zumachen ist auch jetzt da und anklickbar — "
+ "das trennt einen Schoenheitsfehler von einer Falle");
/* ==== DER HINWEIS: SICHTBAR, ABER NICHT AUF KOSTEN DES BILDES ===
Zwei Fehler an einer Stelle, beide am 02.10.2026 gemessen:
· Er war NULL PIXEL hoch, obwohl `hidden=false` und 104
Zeichen Text -- ein `role="alert"`, den niemand sieht.
· Und auf 320 px hatte er sich vorher eine stillschweigende
fuenfte Rasterzeile genommen: 71 Pixel, die dem Bild
fehlten (56 statt 127).
HIER WIRD DER ZUSTAND SELBST HERGESTELLT und nicht abgewartet.
Im Betrieb erscheint der Hinweis, wenn Kamera oder Mikrofon
fehlen -- auf einem Messrechner ist das immer so, auf einem
Handy mit Freigabe nie. Eine Pruefung, die auf die Kamera des
Messrechners baut, prueft die Messumgebung. Also: Text
hineinschreiben, sichtbar machen, messen. */
const hinweis = await seite.evaluate(() => {
const f = document.getElementById("fehler");
const bildVorher = Math.round(
document.getElementById("leinwand").getBoundingClientRect().height);
f.textContent = "Kamera oder Mikrofon sind nicht freigegeben. "
+ "Im Browser oben in der Adresszeile laesst sich das aendern.";
f.hidden = false;
return { bildVorher };
});
await seite.waitForTimeout(300);
const hinweisLage = await seite.evaluate(() => {
const f = document.getElementById("fehler");
const r = f.getBoundingClientRect();
const mitte = document.elementFromPoint(
Math.min(innerWidth - 1, Math.max(0, r.left + r.width / 2)),
Math.min(innerHeight - 1, Math.max(0, r.top + r.height / 2)));
return {
hoch: Math.round(r.height),
imFenster: r.height > 0 && r.top >= 0 && r.bottom <= innerHeight,
obenauf: !!mitte && (mitte === f || f.contains(mitte)),
bildNachher: Math.round(
document.getElementById("leinwand").getBoundingClientRect().height),
};
});
ok(hinweisLage.hoch > 0 && hinweisLage.imFenster && hinweisLage.obenauf,
`der Kamerahinweis ist lesbar, wenn er erscheint (${hinweisLage.hoch} px, im Fenster`
+ `, obenauf) — vorher war er 0 px hoch`);
ok(hinweisLage.bildNachher === hinweis.bildVorher,
` und er kostet das Bild nichts (${hinweis.bildVorher} -> ${hinweisLage.bildNachher} px)`
+ " — vorher nahm er sich auf 320 px eine eigene Rasterzeile");
await seite.evaluate(() => {
const f = document.getElementById("fehler");
f.textContent = ""; f.hidden = true;
});
ok(konsole.length === 0,
konsole.length ? "Fehler im Browser: " + konsole.join(" | ") : "kein Absturz im Browser");
/* ---- GEGENPROBE: kann diese Messung ueberhaupt rot werden? --------
Nur einmal, am schmalsten Geraet. Beide Behauptungen werden
absichtlich gebrochen und muessen dann durchfallen. */
if (breite === 320) {
await seite.evaluate(() => {
document.getElementById("pult").dataset.auf = "nein";
document.getElementById("leinwand").style.height = "0px";
});
await seite.waitForTimeout(250);
const kaputt = await messen(seite);
ok(kaputt.bild < BILD_MIN,
`Gegenprobe: ein auf 0 gedruecktes Bild faellt durch (${kaputt.bild} px)`);
await seite.evaluate(() => {
document.getElementById("leinwand").style.height = "";
const deckel = document.createElement("div");
deckel.id = "deckel-probe";
deckel.style.cssText = "position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.01)";
document.body.append(deckel);
});
await seite.waitForTimeout(250);
const verdeckt = await messen(seite);
ok(verdeckt.zuImFenster && !verdeckt.zuObenauf,
" und ein zugedeckter Knopf gilt nicht als anklickbar, obwohl er im Fenster steht");
await seite.evaluate(() => document.getElementById("deckel-probe")?.remove());
}
await kontext.close();
}
await browser.close();
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
console.log(`\n${geprueft} Pruefungen, ${fehler} Fehler`);
console.log(fehler === 0 ? "ALLES IN ORDNUNG" : "NICHT IN ORDNUNG");
process.exit(fehler === 0 ? 0 : 1);