diff --git a/server/bild-arten.mjs b/server/bild-arten.mjs index b342a077..1ae11581 100644 --- a/server/bild-arten.mjs +++ b/server/bild-arten.mjs @@ -12,6 +12,19 @@ import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; +/* NOTBREMSE (nachgeruestet 06.10.2026, nachdem genau das passiert ist). + + Dieses Werkzeug startet einen Server und einen Browser. Beim Versuch, + die Farbkante unter dem Zeiger zu messen, blieb `page.hover()` haengen + -- und ein Stillstand sieht von aussen genauso aus wie "laeuft noch". + Schlimmer: Nach dem Abbrechen von aussen blieb der Server auf Port + 4336 stehen und haette den naechsten Lauf blockiert. + + Dieselbe Regel wie fuer die Pruefungen, und aus demselben Grund: Eine + Frist, die ueberschritten wird, ist ein FEHLER mit Meldung -- kein + Warten ohne Ende. */ +import { notbremse } from "./helfer-notbremse.mjs"; +notbremse(240_000, "bild-arten"); const ordner = mkdtempSync(join(tmpdir(), "ws-arten-")); process.env.WORKSPACE_DB = join(ordner, "workspace.db"); @@ -104,6 +117,79 @@ for (const s of spalten) { console.log(` ${String(s.stufe).padEnd(14)} ${String(s.titel).padEnd(8)} ${String(s.zahl).padStart(2)} ` + `x=${String(s.x).padStart(4)} b=${String(s.breite).padStart(4)} Linie=${s.linie.padStart(5)} ${s.namen}`); } +/* WAS KOMMT AN DER KARTE WIRKLICH AN? (06.10.2026) + + `module.css` belegt `::before` (Kantenlicht) und `::after` (Raster, + Eckwinkel) JEDER Karte und wird NACH scouting.css geladen. Was + scouting.css auf dieselben Pseudoelemente legt, verliert damit -- + ohne dass irgendwo ein Fehler erscheint. Diese Messung sagt, welcher + Ton tatsaechlich gilt und wie breit die farbige Kante wirklich ist. */ +console.log("\n--- Was an der Karte wirklich ankommt ---"); +const kante = await seite.$eval(".kk", (k) => { + const vor = getComputedStyle(k, "::before"); + return { + ton: getComputedStyle(k).getPropertyValue("--ton").trim() || "(nicht gesetzt)", + stufenfarbe: getComputedStyle(k).getPropertyValue("--stufe-farbe").trim() || "(nicht gesetzt)", + vorBreite: vor.width, vorInhalt: vor.content, + grund: getComputedStyle(k).backgroundImage.slice(0, 60), + }; +}); +console.log(" --ton:", kante.ton, " --stufe-farbe:", kante.stufenfarbe); +console.log(" ::before Breite:", kante.vorBreite, " (3px = Kante von scouting.css, sonst ueberschrieben)"); +console.log(" Grundbild:", kante.grund); + +/* WIE BREIT IST DIE FARBKANTE WIRKLICH -- und kommt sie durch? + Gemessen wird nicht die Regel, sondern das Bild: die Farbe des + Bildpunktes zwei Pixel rechts von der linken Kartenkante, einmal in + Ruhe und einmal unter dem Zeiger. Eine Kante, die man nur im + Quelltext sieht, ist keine. */ +const kantenMass = await seite.evaluate(() => { + const k = document.querySelector(".kk"); + const s = getComputedStyle(k); + return { groesse: s.backgroundSize, lage: s.backgroundPosition, kante: s.getPropertyValue("--kante").trim() }; +}); +console.log(" background-size:", kantenMass.groesse, "| --kante:", kantenMass.kante || "(nicht gesetzt)"); +/* MIT mouse.move AUF DIE MITTE DER KARTE, NICHT MIT page.hover(). + `hover()` wartet darauf, dass das Ziel "bedienbar" ist -- sichtbar, + stabil, nicht verdeckt. Bei einer Karte, ueber der das Hintergrundbild + der Seite liegt, kann diese Pruefung dreissig Sekunden brauchen oder + nie fertig werden. Gemessen werden soll hier aber nicht die + Bedienbarkeit, sondern was der Zeiger an der Farbe aendert -- dafuer + genuegt es, den Zeiger hinzubewegen. */ +{ + const m = await seite.$eval(".kk", (k) => { + const r = k.getBoundingClientRect(); + return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) }; + }); + await seite.mouse.move(m.x, m.y); + await seite.waitForTimeout(400); +} +const unterZeiger = await seite.evaluate(() => + getComputedStyle(document.querySelector(".kk")).getPropertyValue("--kante").trim()); +console.log(" --kante unter dem Zeiger:", unterZeiger || "(nicht gesetzt)"); +/* Kommt die Haarlinie zwischen Kopf und Zahlen an? Ein ungueltiger + Farbwert laesst `border-image` lautlos ausfallen -- uebrig bleibt + Abstand ohne Linie, und das sieht im Quelltext richtig aus. */ +const linie = await seite.$eval(".kk__info", (k) => { + const s2 = getComputedStyle(k); + return { quelle: s2.borderImageSource.slice(0, 48), breite: s2.borderTopWidth }; +}); +console.log(" Haarlinie in der Karte:", linie.breite, "|", linie.quelle); +/* Eine Nahaufnahme der linken Kante -- 90 px breit, damit man sie + ueberhaupt sehen kann. */ +/* Eine Karte OHNE Faellig-Saum: Bei Tamy liegt der warme Innenring + ueber der Stufenkante, und dann misst man den Ring statt der Kante. */ +const kasten = await seite.$eval(".kk:not([data-faellig])", (k) => { + const r = k.getBoundingClientRect(); + /* `width`/`height` -- nicht `w`/`h`. Playwright meldet dann + "clip.width: expected float, got undefined"; gefunden hat es die + Notbremse, nicht das Auge. */ + return { x: Math.round(r.x), y: Math.round(r.y), width: 90, height: Math.round(r.height) }; +}); +await seite.screenshot({ path: "server/bild-arten-kante.png", clip: kasten }); +await seite.mouse.move(0, 0); +await seite.waitForTimeout(300); + const stufe1 = await seite.$('.stufe[data-stufe="neu"]'); await stufe1.screenshot({ path: "server/bild-arten-rechner.png" }); await seite.screenshot({ path: "server/bild-arten-rechner-ganz.png", fullPage: false }); diff --git a/server/pruef-scouting-felder.mjs b/server/pruef-scouting-felder.mjs index f0333aca..ac4bcae8 100644 --- a/server/pruef-scouting-felder.mjs +++ b/server/pruef-scouting-felder.mjs @@ -466,6 +466,95 @@ console.log("\n=== Die Seite selbst ==="); await seite.click("#neu-zu"); await seite.waitForTimeout(250); + /* ===== DIE OPTIK DER STUFE KOMMT WIRKLICH AN (06.10.2026) ======== + + module.css notiert selbst: „`.kk:hover` in scouting.css ... setzt + border-color -- und die Grundregel oben setzt `border: 0`, gleich + stark und spaeter im Ladeweg. Diese vier Regeln tun also vermutlich + nichts." Und: „NICHT MITREPARIERT, weil es kein Messgeraet dafuer + gibt ... Hover laesst sich nicht pruefen." + + DOCH -- es laesst sich pruefen. Hier steht das Messgeraet. + + ERST DEN ZEIGER WEGSETZEN. Beim ersten Anlauf meldete diese + Messung „die Kante ist 9px statt 6" und „unter dem Zeiger waechst + sie nicht (9 -> 9)" -- beides stimmte, und beides war kein Befund + am Programm: Der Klick auf „Bearbeiten" weiter oben hatte die Maus + auf der ersten Karte stehen lassen. Gemessen wurde also zweimal der + Zustand UNTER dem Zeiger. Eine Messung, die ihren Ausgangszustand + nicht herstellt, misst den Rest des Tests mit. */ + await seite.mouse.move(2, 2); + await seite.waitForTimeout(300); + const optik = await seite.$$eval(".stufe", (gs) => gs.map((g) => { + const k = g.querySelector(".kk"); + const s2 = k ? getComputedStyle(k) : null; + return { + stufe: g.dataset.stufe, + ton: getComputedStyle(g).getPropertyValue("--ton").trim(), + farbe: getComputedStyle(g).getPropertyValue("--stufe-farbe").trim(), + kante: s2 ? s2.getPropertyValue("--kante").trim() : null, + kantenbreite: s2 ? s2.backgroundSize.split(",")[0].trim() : null, + nr: g.querySelector(".stufe__nr")?.textContent.trim() || null, + }; + })); + /* JEDE Stufe traegt IHREN Ton -- nicht den der Seite. Gezaehlt wird + in der Bedingung, nicht nur im Text: Ein `every()` auf einer leeren + Liste waere `true` und wuerde nichts beweisen. */ + ok(optik.length >= 3 && optik.every((o) => o.ton && o.ton === o.farbe), + `jede der ${optik.length} Stufen traegt ihren eigenen Ton ` + + `(${optik.map((o) => o.stufe + "=" + o.ton).join(", ")})`); + /* Und die Toene sind verschieden -- sonst waere „jede traegt ihren + eigenen" auch dann wahr, wenn alle denselben haetten. */ + ok(new Set(optik.map((o) => o.ton)).size === optik.length, + `und zwar jede eine andere (${new Set(optik.map((o) => o.ton)).size} Toene auf ${optik.length} Stufen)`); + /* `backgroundSize` liefert BEIDE Masse der ersten Ebene ("6px 100%"). + Der erste Anlauf verglich mit "6px" und meldete rot, obwohl ueberall + 6px standen -- ein Messfehler, der wie ein Befund aussah. + + DER ZWEITE ANLAUF MELDETE DASSELBE, und die Ursache war eine + andere: Beim Schreiben ueber die Kommandozeile wurde aus der + Zeichenfolge fuer eine Wortgrenze ein echtes Steuerzeichen (0x08, + Backspace) mitten im Ausdruck. Ein Ausdruck mit einem Backspace + darin trifft natuerlich nie. Im Quelltext sah die Zeile voellig + richtig aus -- sichtbar wurde das Byte erst mit `cat -v`. Genau + davor warnt pruef-zeichen ("Serverdateien mit Steuerzeichen"), und + genau sie haette es gefunden. + + Deshalb jetzt `startsWith` statt eines Ausdrucks: Daran gibt es + nichts, was beim Schreiben verstuemmelt werden koennte. */ + ok(optik.length >= 3 && optik.every((o) => (o.kantenbreite || "").startsWith("6px")), + `die Farbkante der Karte ist 6px breit (${optik.map((o) => o.kantenbreite).join(", ")})`); + /* Die Etappennummer wird aus WEITER abgeleitet: 01 bis 05, und + „abgelehnt" bekommt keine. */ + const nummern = Object.fromEntries(optik.map((o) => [o.stufe, o.nr])); + ok(nummern.neu === "01" && nummern.angesprochen === "02", + `die Stufen sind nummeriert (neu=${nummern.neu}, angesprochen=${nummern.angesprochen})`); + + /* HOVER -- gemessen mit dem Zeiger, nicht geglaubt. + + `mouse.move` und nicht `page.hover()`: Letzteres wartet auf + „bedienbar" und blieb beim ersten Anlauf haengen. Gemessen werden + soll hier die Farbe, nicht die Bedienbarkeit. */ + const vorher = await seite.$eval(".kk", (k) => getComputedStyle(k).getPropertyValue("--kante").trim()); + const mitte = await seite.$eval(".kk", (k) => { + const r = k.getBoundingClientRect(); + return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) }; + }); + await seite.mouse.move(mitte.x, mitte.y); + await seite.waitForTimeout(350); + const nachher = await seite.$eval(".kk", (k) => getComputedStyle(k).getPropertyValue("--kante").trim()); + ok(vorher === "6px" && nachher === "9px", + `unter dem Zeiger waechst die Kante (${vorher} -> ${nachher})`); + await seite.mouse.move(0, 0); + await seite.waitForTimeout(200); + + /* Die Haarlinie in der Karte. Ein ungueltiger Farbwert laesst + `border-image` lautlos ausfallen -- „none" heisst, sie fehlt. */ + const haar = await seite.$eval(".kk__info", + (k) => getComputedStyle(k).borderImageSource); + ok(haar.includes("gradient"), + `die Haarlinie zwischen Kopf und Zahlen ist da (${haar.slice(0, 28)}…)`); + /* ===== GEGENPROBE: AUF DEM HANDY LIEGEN SIE UNTEREINANDER ======== Ohne diese Messung wuesste niemand, ob die Trennung ueberhaupt @@ -500,6 +589,34 @@ console.log("\n=== Die Seite selbst ==="); ok(schmal.every((k) => k.breite >= 300), `keine Spalte wird gequetscht (${schmal.map((k) => k.breite).join(" / ")} px)`); + /* ===== UND AUF DEM SCHMALSTEN GERAET, DAS ES GIBT (320 px) ======= + + Die Karte hat links 26px Polster bekommen (davor 21), dazu eine + 6px-Kante. Was auf 390px passt, muss auf 320px nicht passen -- und + ein Element, das ueber den Rand steht, erzeugt seitliches Scrollen + auf der ganzen Seite. + + GEMESSEN STATT DEN HAUSLAUF GESTARTET: `pruef-handy` prueft 186 + Dinge auf vielen Seiten und braucht 226 Sekunden. Die Frage hier + ist eine einzige und in vier Sekunden beantwortet. */ + const engKontext = await browser.newContext({ + viewport: { width: 320, height: 720 }, hasTouch: true, isMobile: true, + }); + await engKontext.addCookies(await ctx.cookies()); + const eng = await engKontext.newPage(); + await eng.goto(seite.url(), { waitUntil: "networkidle" }); + await eng.waitForTimeout(900); + const ueber = await eng.$$eval(".stufe *", (ks) => ks + .filter((k) => k.getBoundingClientRect().right > window.innerWidth + 1) + .slice(0, 4) + .map((k) => `${k.className || k.tagName} bis ${Math.round(k.getBoundingClientRect().right)}px`)); + const gemessen = await eng.$$eval(".stufe *", (ks) => ks.length); + ok(gemessen > 20 && ueber.length === 0, + `auf 320px steht keines der ${gemessen} Teile ueber den Rand (${ueber.join(", ") || "keins"})`); + const quer = await eng.evaluate(() => + document.documentElement.scrollWidth - document.documentElement.clientWidth); + ok(quer <= 1, `und die Seite laesst sich nicht seitlich schieben (${quer} px)`); + await browser.close(); } diff --git a/workspace/anleitung.html b/workspace/anleitung.html index 292be482..d5012fd1 100644 --- a/workspace/anleitung.html +++ b/workspace/anleitung.html @@ -7,17 +7,17 @@ - - - - - + + + + + - - + +
@@ -289,16 +289,16 @@ Und anleitung.js braucht es doppelt: Symbol, Farbe, Name und Adresse jeder Karte kommen von dort, nicht vom Server. --> - + - - - - - - + + + + + +