diff --git a/server/pruef-chat-neu-stelle.mjs b/server/pruef-chat-neu-stelle.mjs index f58945e9..8d680061 100644 --- a/server/pruef-chat-neu-stelle.mjs +++ b/server/pruef-chat-neu-stelle.mjs @@ -368,6 +368,161 @@ melde("=== 2. Gegenproben (jede MUSS anschlagen) ==="); + "-- die Messung kann also auch nein sagen"); } +/* ===================================================================== + DIE STELLE HAELT AUCH, WENN DIE LINIE VERSCHWINDET (05.10.2026) + + Miss: "Wenn ich auf den Chat gehen, komme ich zuerst auf die erste + neue Nachricht, aber kurze Zeit spaeter springt er auf die zuletzt + geschrieben Nachricht." + + Die Pruefungen oben messen den Zustand nach 2,2 Sekunden -- und der + war immer richtig. Ihr Fehler passiert DANACH: Beim Oeffnen meldet + die Seite "gelesen bis hier". Beim naechsten sanften Nachladen + steht die letzte Nachricht schon in `gelesen_bis`, die Linie + entsteht nicht mehr, und dieselbe Bedingung bedeutete ploetzlich + das Gegenteil -- nicht "das Neue ist jetzt gelesen", sondern + "nichts Neues, also ans Ende". + + Deshalb wird hier ZWEIMAL gemessen: einmal wie oben, und einmal + nach weiteren sechs Sekunden, in denen niemand etwas anfasst. Eine + Messung, die nur den ersten Moment ansieht, haette diesen Fehler + nie gefunden -- sie hat ihn wochenlang nicht gefunden. + ===================================================================== */ +melde(""); +melde("=== 4. Ein sanftes Nachladen reisst niemanden ans Ende ==="); +{ + const person = LEUTE[0]; + const keks = await anmelden(person.host, person.rolle, person.code); + const kPartner = await anmelden(person.partnerHost, person.partnerRolle, person.partnerCode); + const gr = await roh(person.host, keks, "/workspace/api/chat/gruppe", "POST", + { name: `${person.wer} Miss-Fall`, mit: [person.partner] }); + const raumId = gr.json().raum_id; + + let letzteAlte = 0; + for (let i = 1; i <= 20; i++) { + const a = await roh(person.partnerHost, kPartner, + `/workspace/api/chat/raeume/${raumId}/nachrichten`, "POST", + { text: `Alte Nachricht ${i} -- lange genug, damit sie auf einem Handy umbricht.` }); + letzteAlte = a.json().nachricht?.id || a.json().id || letzteAlte; + } + await roh(person.host, keks, `/workspace/api/chat/raeume/${raumId}/gelesen`, + "PUT", { bis: letzteAlte }); + for (let i = 1; i <= 12; i++) { + await roh(person.partnerHost, kPartner, + `/workspace/api/chat/raeume/${raumId}/nachrichten`, "POST", + { text: `NEUE Nachricht ${i} -- auch diese ist lang genug zum Umbrechen.` }); + } + + const kontext = await browser.newContext({ + viewport: { width: 390, height: 844 }, + deviceScaleFactor: 2, isMobile: true, hasTouch: true, + }); + await kontext.addCookies(keks.split("; ").map((k) => { + const [x, ...r] = k.split("="); + return { name: x, value: r.join("="), domain: person.host, path: "/" }; + })); + const seite = await kontext.newPage(); + const konsole = []; + seite.on("pageerror", (e) => konsole.push("Absturz: " + e.message.slice(0, 120))); + await seite.goto(`https://${person.host}:${HP}/workspace/chat.html?raum=${raumId}`, + { waitUntil: "networkidle" }); + + const messen = () => seite.evaluate(() => { + const v = document.getElementById("verlauf"); + if (!v) return { da: false }; + const l = v.querySelector(".chat-neu"); + return { + da: true, + stelle: Math.round(v.scrollTop), + amEnde: v.scrollHeight - v.scrollTop - v.clientHeight < 40, + linieDa: !!l, + }; + }); + + await seite.waitForTimeout(2200); + const erst = await messen(); + ok(erst.da && erst.linieDa && !erst.amEnde, + `beim Oeffnen steht sie an der neuen Stelle (Linie ${erst.linieDa}, am Ende ${erst.amEnde})`); + + /* ==== DER FINGERTIPP IST DER AUSLOESER ======================== + + Der erste Anlauf liess die Seite einfach sechs Sekunden in Ruhe + -- und war mit dem ALTEN Code genauso gruen. Die Messung hat + Miss' Fall gar nicht hergestellt. + + Es fehlte das, was ein Mensch beim Lesen zwangslaeufig tut: Er + tippt einmal auf den Verlauf. `wache` nimmt das (zu Recht) als + "er hat die Stelle gesehen" und loescht den Sprung-Merker. Erst + DANACH ist der Weg zu `if (unten)` frei -- und `unten` stand bei + jedem sanften Nachladen wieder auf true. + + Getippt wird an den linken Rand, nicht auf eine Nachricht: Es + geht um die Beruehrung, nicht um einen Knopf. */ + /* Der Griff muss den Verlauf WIRKLICH treffen -- ein Tipp auf + Koordinaten kann danebengehen, und dann bleibt der Merker + stehen (gemessen: die Ansicht sprang danach zur Linie zurueck, + nicht ans Ende, weil `wache` nie lief). */ + await seite.locator("#verlauf").dispatchEvent("touchstart"); + await seite.waitForTimeout(400); + + /* ==== UND JETZT NACH OBEN LESEN =============================== + + Hier wird der Defekt selbst gemessen statt Miss' Ablauf geraten + -- drei Versuche, ihn nachzustellen, waren mit dem ALTEN Code + gruen, und eine Pruefung, die den Fehler nicht herstellt, misst + nichts. + + Der Defekt ist eine Zeile: Beim Laden eines Raums wurde `unten` + auf true gesetzt, und zwar auch beim SANFTEN Nachladen. Wer also + irgendwo im Verlauf steht und dessen Strom neu verbindet, wird + ans Ende gerissen -- unabhaengig davon, wie er dorthin kam. + + Das ist allgemeiner als Miss' Fall und schliesst ihn ein. */ + await seite.evaluate(() => { document.getElementById("verlauf").scrollTop = 500; }); + await seite.waitForTimeout(400); + const obenGelesen = await messen(); + ok(obenGelesen.stelle <= 600 && !obenGelesen.amEnde, + `sie liest weiter oben (${obenGelesen.stelle} px, am Ende ${obenGelesen.amEnde})`); + + /* ==== UND DER ZWEITE AUSLOESER: EIN SANFTER NACHLAUF =========== + + Auch mit dem Fingertipp blieb die Messung mit dem alten Code + gruen -- es fehlte das, was `unten` ueberhaupt wieder auf true + setzt: ein erneutes Laden des Raums. Das passiert nicht von + selbst, sondern wenn der Ereignisstrom neu verbindet. + + Erst versucht mit einem Verbindungsabriss (offline/online) -- der + loeste in dieser Umgebung keinen aus, und die Messung blieb + gruen, ohne etwas hergestellt zu haben. Jetzt heftet jemand + anderes eine Nachricht an: Das schickt ein `pin`-Ereignis an + alle im Raum, und die Seite laedt sanft nach. Im "Rudel" mit zehn + Leuten ist das Alltag. */ + const angeheftet = await roh(person.host, keks, + `/workspace/api/chat/nachrichten/${letzteAlte}/anheften`, "PUT", { an: true }); + ok(angeheftet.status === 200, + `jemand heftet etwas an -- das loest den sanften Nachlauf aus (${angeheftet.status})`); + + /* Und jetzt sechs Sekunden nichts weiter tun -- genau das, was Miss + beschreibt: Sie liest, und "kurze Zeit spaeter" springt es. */ + await seite.waitForTimeout(6000); + const spaeter = await messen(); + + ok(spaeter.amEnde === false, + `nach dem Nachladen steht sie nicht am Ende (amEnde ${spaeter.amEnde})`); + /* Die eigentliche Zahl: Die Bildlaufposition darf sich nicht + nennenswert bewegt haben. "Nicht am Ende" allein koennte auch + heissen, dass sie irgendwohin anders gesprungen ist. */ + ok(Math.abs(spaeter.stelle - obenGelesen.stelle) <= 60, + `und zwar da, wo sie gelesen hat (${obenGelesen.stelle} -> ${spaeter.stelle} px)`); + /* Dass die Linie inzwischen WEG sein darf, ist kein Fehler -- + gelesen ist gelesen. Nur springen darf es deswegen nicht. */ + melde(` (die Linie ist jetzt ${spaeter.linieDa ? "noch da" : "weg -- gelesen"},` + + " das ist in Ordnung)"); + ok(konsole.length === 0, `keine Abstuerze (${konsole.join(" | ") || "keine"})`); + + await kontext.close(); +} + await browser.close(); vorbau.close(); diff --git a/server/pruef-suchfeld.mjs b/server/pruef-suchfeld.mjs index 559e7de8..72ceb21c 100644 --- a/server/pruef-suchfeld.mjs +++ b/server/pruef-suchfeld.mjs @@ -189,6 +189,96 @@ try { ok(konsole.length === 0, konsole.length ? konsole[0] : "keine Abstuerze"); await seite.screenshot({ path: "pruef-suchfeld.png", fullPage: false }); await kontext.close(); + +/* ===================================================================== + EINE GROESSENAENDERUNG DARF DIE LISTE NICHT SCHLIESSEN (05.10.2026) + + Diene: "Beim Kalender laesst sich die Art noch nicht einstellen, das + Menue ploppt nur ganz kurz auf und verschwindet direkt wieder. Das + selbe bei den Wiederholungen." + + Auf dem Rechner faellt das nie auf: Dort aendert sich die + Fenstergroesse nur, wenn jemand sie aendert. Auf einem Handy aendert + sie sich beim Bedienen -- Adressleiste, Tastatur -- und `resize` + feuerte `schliessen()`. + + Gemessen wird an GENAU IHREM Fall: Kalenderformular, die beiden + Auswahlfelder "Art" und "Wiederholung", und eine echte + Groessenaenderung dazwischen. Die Liste muss offen bleiben UND + danach an ihrem Knopf sitzen -- offen, aber verrutscht waere nur + die andere Haelfte. + ===================================================================== */ +{ + const kontext = await browser.newContext({ + viewport: { width: 390, height: 844 }, hasTouch: true, isMobile: true, + }); + const seite = await kontext.newPage(); + 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: 15000 }); + await seite.goto(BASIS + "/workspace/kalender.html", { waitUntil: "networkidle" }); + await seite.waitForTimeout(1200); + await seite.evaluate(() => document.getElementById("neu-oeffnen")?.click()); + await seite.waitForTimeout(600); + + for (const [feldId, wie] of [["f-art", "Art"], ["f-takt", "Wiederholung"]]) { + const auf = await seite.evaluate((id) => { + const k = document.getElementById(id)?.closest(".wahl2")?.querySelector(".wahl2__knopf"); + if (!k) return "kein Knopf"; + k.click(); + return !!document.querySelector(".wahl2__liste"); + }, feldId); + ok(auf === true, `${wie}: die Liste geht auf (${auf})`); + + /* Die Groessenaenderung, die auf dem Handy beim Bedienen entsteht: + Die Adressleiste faehrt ein, das Fenster wird hoeher. */ + await seite.setViewportSize({ width: 390, height: 788 }); + await seite.waitForTimeout(350); + + const danach = await seite.evaluate(() => { + const l = document.querySelector(".wahl2__liste"); + if (!l) return { offen: false }; + const k = document.querySelector(".wahl2__knopf[aria-expanded='true']") + || document.querySelector(".wahl2 .wahl2__knopf"); + const lr = l.getBoundingClientRect(); + const kr = k ? k.getBoundingClientRect() : null; + return { + offen: true, + /* Sitzt sie noch an ihrem Knopf? Mehr als 40 px Abstand waere + "offen, aber verrutscht" -- und das ist kein Erfolg. */ + abstand: kr ? Math.round(Math.min(Math.abs(lr.top - kr.bottom), + Math.abs(lr.bottom - kr.top))) : null, + imBild: lr.top >= 0 && lr.bottom <= innerHeight + 1, + }; + }); + ok(danach.offen === true, + `${wie}: sie bleibt nach der Groessenaenderung offen (${danach.offen})`); + ok(danach.offen && danach.abstand !== null && danach.abstand <= 40, + `${wie}: und sitzt weiter an ihrem Knopf (${danach.abstand} px Abstand)`); + + await seite.setViewportSize({ width: 390, height: 844 }); + await seite.keyboard.press("Escape"); + await seite.waitForTimeout(250); + } + + /* GEGENPROBE: Escape schliesst sie weiterhin. Sonst haette ich aus + "schliesst zu frueh" ein "schliesst nie" gemacht -- und das waere + schlimmer, weil man dann gar nicht mehr herauskommt. */ + await seite.evaluate(() => document.getElementById("f-art") + ?.closest(".wahl2")?.querySelector(".wahl2__knopf")?.click()); + await seite.waitForTimeout(300); + const vorEsc = await seite.evaluate(() => !!document.querySelector(".wahl2__liste")); + await seite.keyboard.press("Escape"); + await seite.waitForTimeout(300); + const nachEsc = await seite.evaluate(() => !!document.querySelector(".wahl2__liste")); + ok(vorEsc && !nachEsc, + `Gegenprobe: Escape schliesst sie weiterhin (offen ${vorEsc} -> ${nachEsc})`); + + await kontext.close(); +} + } finally { await browser.close(); try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ } diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index be00c96e..4ef33eb5 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`. --> - - + + - +