diff --git a/server/pruef-stelle.mjs b/server/pruef-stelle.mjs new file mode 100644 index 00000000..60989249 --- /dev/null +++ b/server/pruef-stelle.mjs @@ -0,0 +1,247 @@ +/* ===================================================================== + pruef-stelle.mjs — Bleibt man, wo man war? (21.09.2026) + + Filipe, zum vierten Mal: "muss ich jedes mal wenn ich von einer seite + zurück scrolle immer wieder runter scrollen um weiter zu schauen und + das nervt, wieso bleibt es nicht sofort da wo ich dran war anstatt + dass ich immer wieder ganz runter scrollen muss." + + --------------------------------------------------------------------- + WARUM ES DREIMAL ALS BEHOBEN GALT UND NIE BEHOBEN WAR + + Die Wiederherstellung in kopf.js gibt es seit dem 20.09. Sie ist + sorgfaeltig gebaut -- sie merkt sich die Stelle je Adresse, wartet, + bis die nachgeladene Seite hoch genug ist, und hoert auf, sobald der + Mensch selbst scrollt. + + Sie lief nur fast nie. Denn sie lief NUR bei + `navigation.type === "back_forward"`, also beim Zurueck-Knopf des + Browsers. Im Haus geht man aber anders zurueck: ueber die Kopfzeile, + und dort steht ein ganz normales ``. Fuer den + Browser ist das eine Fahrt VORWAERTS. Die Bedingung war falsch, und + der `else`-Zweig loeschte die eben gemerkte Stelle sogar noch. + + IM KOPF DERSELBEN DATEI STAND DIE LOESUNG ALS ABSICHT: "Ein eigener + Merker faengt den Fall ab, dass der Zurueck-Knopf der Kopfleiste + benutzt wurde." Gebaut wurde er nie. Ein Kommentar, der eine Absicht + beschreibt, erfuellt sie nicht. + + UND DESHALB HAT ES IN JEDEM TEST FUNKTIONIERT: Wer mit + `page.goBack()` prueft, prueft den einen Weg, den niemand geht. + Gemessen am 21.09.: ueber `goBack()` sauber wiederhergestellt, + ueber den Link in der Kopfzeile oben gelandet. Man muss den Weg + gehen, den der Mensch geht. + + --------------------------------------------------------------------- + WAS HIER GEMESSEN WIRD + + 1. Ueber den Link in der Kopfzeile zurueck -> Stelle bleibt + 2. Ueber den Zurueck-Knopf des Browsers -> Stelle bleibt + 3. GEGENPROBE: Eine Seite frisch oeffnen -> faengt oben an + + Die dritte ist nicht Zierde. Ohne sie waere die Pruefung auch dann + gruen, wenn die Seite IMMER an die alte Stelle springt -- und das + waere schlimmer als das Problem: Wer eine Kachel antippt, will oben + anfangen. + + Aufrufen mit: node server/pruef-stelle.mjs + ===================================================================== */ + +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { request as httpAnfrage } from "node:http"; +import { eigenerPort } from "./helfer-port.mjs"; + +const PORT = await eigenerPort(import.meta, "pruef-stelle"); +const ordner = mkdtempSync(join(tmpdir(), "ws-stelle-")); +process.env.WORKSPACE_DB = join(ordner, "workspace.db"); +process.env.PORT = String(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 }); +}; +import { notbremse } from "./helfer-notbremse.mjs"; +await import("./index.js"); +notbremse(180_000, "pruef-stelle"); +await new Promise((r) => setTimeout(r, 900)); + +process.on("uncaughtException", (f) => { console.error("ABSTURZ:", f); process.exit(7); }); + +let fehler = 0, geprueft = 0; +const melde = (t) => console.log(t); +const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; }; + +const CREW = "crew.dogfather-universe.com"; + +function roh(pfad, koerper) { + return new Promise((fertig, schief) => { + const a = httpAnfrage({ + host: "127.0.0.1", port: PORT, path: pfad, method: koerper ? "POST" : "GET", + headers: { Host: CREW, ...(koerper ? { "Content-Type": "application/json" } : {}) }, + }, (antwort) => { + let text = ""; + antwort.on("data", (s) => { text += s; }); + antwort.on("end", () => fertig({ code: antwort.statusCode, text, kopf: antwort.headers })); + }); + a.on("error", schief); + if (koerper) a.write(JSON.stringify(koerper)); + a.end(); + }); +} + +const { DatabaseSync } = await import("node:sqlite"); +const { scryptSync, randomBytes } = await import("node:crypto"); +{ + const d = new DatabaseSync(process.env.WORKSPACE_DB); + const salz = randomBytes(16).toString("hex"); + const hash = scryptSync("CODE-DOGI-0001", salz, 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, salz, 32768, new Date().toISOString()); + d.close(); +} + +let an = await roh("/workspace/api/anmelden", { rolle: "admin", code: "CODE-DOGI-0001" }); +if (an.code === 400 && an.text.includes("alter_offen")) { + an = await roh("/workspace/api/anmelden", { rolle: "admin", code: "CODE-DOGI-0001", alter_ok: true }); +} +const keks = [].concat(an.kopf["set-cookie"] || []).map((z) => z.split(";")[0]).join("; "); +ok(!!keks, "angemeldet"); + +/* ------------------------------------------------------------------- */ +let pw = null; +try { + pw = await import("file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs"); +} catch { /* dritter Ausgang */ } + +if (!pw) { + melde(" -- Playwright ist nicht da -- im Browser wurde NICHT nachgesehen."); + melde(" Das ist kein Befund, aber auch kein bestandener Test."); + melde(`\n${geprueft} Pruefungen, ${fehler} Fehler`); + process.exit(fehler ? 1 : 0); +} + +const browser = await pw.chromium.launch(); + +/** Scrollt auf der Startseite nach unten, oeffnet ein Brett und kommt + * auf dem uebergebenen Weg zurueck. Gibt zurueck, wo man landet. */ +async function hinUndZurueck(wieZurueck) { + const ctx = await browser.newContext({ viewport: { width: 1280, height: 800 } }); + await ctx.addCookies(keks.split("; ").filter(Boolean).map((z) => { + const [name, ...rest] = z.split("="); + return { name, value: rest.join("="), domain: "127.0.0.1", path: "/" }; + })); + const seite = await ctx.newPage(); + try { + await seite.goto(`http://127.0.0.1:${PORT}/workspace/start.html`, { waitUntil: "networkidle" }); + await seite.waitForTimeout(1700); + const hoch = await seite.evaluate(() => document.documentElement.scrollHeight); + /* DIE HOEHE GEHOERT IN DIE BEDINGUNG. Auf einer Seite, die kuerzer + ist als das Fenster, kann man gar nicht scrollen -- dann misst + alles Folgende nichts. */ + if (hoch < 1600) return { zuKurz: hoch }; + await seite.evaluate(() => window.scrollTo(0, 1200)); + await seite.waitForTimeout(400); + const vor = await seite.evaluate(() => Math.round(window.scrollY)); + + await seite.goto(`http://127.0.0.1:${PORT}/workspace/bereich.html?b=highlight`, + { waitUntil: "networkidle" }); + await seite.waitForTimeout(1400); + + if (wieZurueck === "link") { + /* Ueber die Kopfzeile -- der Weg, den Filipe im Video geht. + `evaluate` statt `click`, weil der Link je nach Breite hinter + dem Knopf liegen kann; das Ereignis ist dasselbe. */ + const gabEs = await seite.evaluate(() => { + const a = document.querySelector("a.zurueck"); + if (!a) return false; + a.click(); + return true; + }); + if (!gabEs) return { keinLink: true }; + await seite.waitForNavigation({ waitUntil: "networkidle" }).catch(() => {}); + } else { + await seite.goBack({ waitUntil: "load" }); + } + await seite.waitForTimeout(3000); + const nach = await seite.evaluate(() => ({ + y: Math.round(window.scrollY), + art: performance.getEntriesByType("navigation")[0]?.type, + wo: location.pathname, + })); + return { vor, ...nach }; + } finally { + await ctx.close(); + } +} + +try { + melde("\n=== 1. Zurueck ueber die Kopfzeile ==="); + { + const r = await hinUndZurueck("link"); + ok(!r.zuKurz && !r.keinLink, + r.zuKurz ? `die Startseite ist nur ${r.zuKurz}px hoch -- nichts zu messen` + : r.keinLink ? "es gibt keinen Zurueck-Link in der Kopfzeile" + : "der Weg ueber die Kopfzeile ist da"); + if (!r.zuKurz && !r.keinLink) { + ok(r.wo === "/workspace/start.html", `man landet auf der Startseite (${r.wo})`); + /* DASS ES EBEN KEIN "back_forward" IST, ist der Kern der Sache -- + und wird deshalb ausdruecklich festgehalten. Waere es eines, + pruefte dieser Abschnitt denselben Weg wie der naechste. */ + ok(r.art !== "back_forward", + `und zwar als gewoehnliche Fahrt vorwaerts (${r.art}) -- genau der Fall, ` + + "der bisher nicht wiederhergestellt wurde"); + ok(r.y >= r.vor - 80, + `die Stelle bleibt: ${r.vor}px hin, ${r.y}px zurueck`); + } + } + + melde("\n=== 2. Zurueck ueber den Browser ==="); + { + const r = await hinUndZurueck("back"); + ok(!r.zuKurz, r.zuKurz ? `zu kurz (${r.zuKurz}px)` : "die Seite ist hoch genug"); + if (!r.zuKurz) { + ok(r.art === "back_forward", `der Browser meldet ein Zurueck (${r.art})`); + ok(r.y >= r.vor - 80, `die Stelle bleibt: ${r.vor}px hin, ${r.y}px zurueck`); + } + } + + melde("\n=== 3. Gegenprobe: frisch geoeffnet faengt oben an ==="); + { + /* Ohne diese Zeile waere alles oben auch dann gruen, wenn die + Seite IMMER an die alte Stelle springt. Das waere schlimmer als + das Problem: Wer eine Kachel antippt, will oben anfangen. */ + const ctx = await browser.newContext({ viewport: { width: 1280, height: 800 } }); + await ctx.addCookies(keks.split("; ").filter(Boolean).map((z) => { + const [name, ...rest] = z.split("="); + return { name, value: rest.join("="), domain: "127.0.0.1", path: "/" }; + })); + const seite = await ctx.newPage(); + try { + await seite.goto(`http://127.0.0.1:${PORT}/workspace/start.html`, { waitUntil: "networkidle" }); + await seite.waitForTimeout(1500); + await seite.evaluate(() => window.scrollTo(0, 1200)); + await seite.waitForTimeout(400); + /* Neu aufrufen, ohne ueber einen Zurueck-Weg zu kommen. */ + await seite.goto(`http://127.0.0.1:${PORT}/workspace/start.html`, { waitUntil: "networkidle" }); + await seite.waitForTimeout(2600); + const y = await seite.evaluate(() => Math.round(window.scrollY)); + ok(y < 80, `frisch geoeffnet steht die Seite oben (${y}px)`); + } finally { + await ctx.close(); + } + } +} finally { + await browser.close(); +} + +melde(`\n${geprueft} Pruefungen, ${fehler} Fehler`); +melde(fehler ? "NICHT IN ORDNUNG" : "ALLES IN ORDNUNG"); +try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Aufraeumrest */ } +process.exit(fehler ? 1 : 0); diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index 2292549d..72951a88 100644 --- a/workspace/anruf-probe.html +++ b/workspace/anruf-probe.html @@ -16,15 +16,15 @@ verdeckt. Gefunden hat es pruef-struktur. Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. --> - - + + - +