diff --git a/server/pruef-chat-optik.mjs b/server/pruef-chat-optik.mjs
index 9f4f2e40..5baaf6a7 100644
--- a/server/pruef-chat-optik.mjs
+++ b/server/pruef-chat-optik.mjs
@@ -957,6 +957,114 @@ melde("\n=== Die Schreibzeile auf dem Handy ===");
} finally { await laptop.ctx.close(); }
}
+/* =======================================================================
+ WER HAT REAGIERT? -- AUCH OHNE MAUS (02.10.2026)
+
+ VanVan: „Wenn man auf dem Handy auf die Reaktionen unter einer
+ Nachricht geht, dann sieht man nicht wer darauf reagiert hat. Auf
+ dem PC funktioniert es."
+
+ Die Namen standen im `title` der Kachel. Ein `title` erscheint beim
+ UEBERFAHREN MIT DER MAUS -- auf einem Finger gibt es das nicht, und
+ das Antippen schaltet stattdessen die eigene Reaktion um. Die
+ Auskunft war nicht versteckt, sondern an ein Geraet gebunden.
+
+ GEPRUEFT WIRD AUF BEIDEN GERAETEN, sonst beweist es nichts: Am
+ Handy MUSS die Namenszeile da sein, am Rechner MUSS sie fehlen und
+ der `title` die Namen tragen. Ohne die zweite Haelfte waere eine
+ Regel, die immer gilt, genauso gruen -- und wuerde am Rechner unter
+ jeder zweiten Nachricht eine Zeile anhaengen, die niemand bestellt
+ hat.
+ ======================================================================= */
+melde("\n=== Wer hat reagiert? ===");
+{
+ /* DogFather schreibt, Patrick reagiert -- dann steht ein FREMDER
+ Name da. Mit der eigenen Reaktion waere „Du" zu sehen, und die
+ Pruefung haette den Fall nicht gemessen, um den es geht. */
+ const dogi = await alsRolle("admin", "CODE-DOGI-0001", "chat.html", 1280);
+ let nid = null;
+ let raumId = null;
+ try {
+ const raum = await dogi.seite.evaluate(async (id) => {
+ const a = await fetch("/workspace/api/chat/direkt", {
+ method: "POST", credentials: "same-origin",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ mit: id }),
+ });
+ return (await a.json())?.raum_id;
+ }, idPat);
+ raumId = raum;
+ nid = await dogi.seite.evaluate(async (r) => {
+ const a = await fetch(`/workspace/api/chat/raeume/${r}/nachrichten`, {
+ method: "POST", credentials: "same-origin",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ text: "Woran reagiert ihr?" }),
+ });
+ return (await a.json())?.nachricht?.id;
+ }, raum);
+ ok(Number.isInteger(nid), `eine Nachricht zum Reagieren da (#${nid})`);
+
+ const pat = await alsRolle("scout", "CODE-SCOU-0001", "chat.html", 1280);
+ try {
+ const code = await pat.seite.evaluate(async (n) => {
+ const a = await fetch(`/workspace/api/chat/nachrichten/${n}/reaktion`, {
+ method: "PUT", credentials: "same-origin",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ zeichen: "👍" }),
+ });
+ return a.status;
+ }, nid);
+ ok(code === 200 || code === 201, `Patrick reagiert mit 👍 (HTTP ${code})`);
+ } finally { await pat.ctx.close(); }
+ } finally { await dogi.ctx.close(); }
+
+ /** Was auf einem Geraet wirklich zu sehen ist. */
+ const ansehen = async (breite) => {
+ /* MIT DEM RAUM IN DER ADRESSE. Ohne ihn steht die Seite auf der
+ Gespraechsliste, es ist keine Nachricht offen -- und die
+ Pruefung wartet zehn Sekunden auf eine Kachel, die es dort
+ gar nicht geben kann. */
+ const wer = await alsRolle("admin", "CODE-DOGI-0001",
+ `chat.html?raum=${raumId}`, breite);
+ try {
+ await wer.seite.waitForSelector(".reaktionen__stueck", { timeout: 10000 });
+ await wer.seite.waitForTimeout(500);
+ return await wer.seite.evaluate(() => {
+ const z = document.querySelector(".reaktionen__wer");
+ const k = document.querySelector(".reaktionen__stueck");
+ const r = z ? z.getBoundingClientRect() : null;
+ return {
+ zeileDa: !!z,
+ sichtbar: !!z && getComputedStyle(z).display !== "none"
+ && !!r && r.height > 0,
+ text: (z?.textContent || "").trim(),
+ titel: k?.getAttribute("title") || "",
+ vorlesewort: k?.getAttribute("aria-label") || "",
+ fuerVorleser: z?.getAttribute("aria-hidden") || "",
+ };
+ });
+ } finally { await wer.ctx.close(); }
+ };
+
+ const handy = await ansehen(390);
+ ok(handy.sichtbar, `auf dem Handy steht die Zeile da (${handy.sichtbar})`);
+ ok(/Patrick/.test(handy.text),
+ ` und sie nennt den Namen: „${handy.text}"`);
+ ok(/👍/.test(handy.text), " mit dem Zeichen davor, damit klar ist, worauf");
+ /* Jede Kachel traegt ihre Namen schon im `aria-label`. Ohne
+ `aria-hidden` hoerte ein Vorleseprogramm alles doppelt. */
+ ok(handy.fuerVorleser === "true",
+ " und ein Vorleseprogramm hoert sie nicht doppelt (aria-hidden)");
+
+ const rechner = await ansehen(1280);
+ ok(rechner.zeileDa && !rechner.sichtbar,
+ "am Rechner bleibt sie weg — dort funktioniert das Überfahren");
+ ok(/Patrick/.test(rechner.titel),
+ ` und die Namen stehen wie bisher im Titel („${rechner.titel.slice(0, 40)}…")`);
+ ok(/Patrick/.test(rechner.vorlesewort),
+ " das Vorlesewort der Kachel nennt sie auf beiden Geräten");
+}
+
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 44db2211..b7ecf886 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`. -->
-
-
+
+
-
+