diff --git a/server/pruef-modi-katalog.mjs b/server/pruef-modi-katalog.mjs index a8537794..f9287d67 100644 --- a/server/pruef-modi-katalog.mjs +++ b/server/pruef-modi-katalog.mjs @@ -490,8 +490,11 @@ console.log("\n=== 6. Das Brett im echten Browser ===\n"); await seite.fill("#code", "CODE-MODI-0001"); await seite.click("button[type=submit]"); await seite.waitForURL("**/start.html", { timeout: 15000 }); - await seite.goto(BASIS + "/workspace/aufgaben.html", { waitUntil: "networkidle" }); - await seite.waitForTimeout(1200); + /* DER KATALOG STEHT SEIT DEM 23.09.2026 AUF "EURE AUFGABEN". + Filipe: "das soll auch bitte nicht in der kategorie aufgaben + sondern eure aufgaben sein bitte." */ + await seite.goto(BASIS + "/workspace/entwicklung.html", { waitUntil: "networkidle" }); + await seite.waitForTimeout(1600); /* Aufklappen, falls es zugeklappt startet. */ const zu = await seite.evaluate(() => @@ -703,8 +706,9 @@ console.log(""); 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/aufgaben.html", { waitUntil: "networkidle" }); - await seite.waitForTimeout(1400); + /* Siehe oben: der Katalog ist umgezogen. */ + await seite.goto(BASIS + "/workspace/entwicklung.html", { waitUntil: "networkidle" }); + await seite.waitForTimeout(1800); const zu = await seite.evaluate(() => document.getElementById("vorlagenbrett")?.dataset.auf !== "ja"); if (zu) { @@ -712,85 +716,111 @@ console.log(""); await seite.waitForTimeout(800); } - const wahl = await seite.evaluate(() => { - const reihen = [...document.querySelectorAll(".stufenleiter")]; - const letzte = document.querySelector('[aria-label="Person wählen"]'); - const zahl = /^[0-9]+$/; + /* ==== AN WEN? DIE FRAGE IST JETZT SCHON BEANTWORTET ============ + + Bis zum 23.09.2026 brachte der Katalog eine EIGENE Personenreihe + mit -- eine dritte Knopfreihe unter zwei anderen. Sie war noetig, + weil "Aufgaben" keine Personenwahl hat. + + Auf "Eure Aufgaben" gibt es sie: die Kacheln unter "Wen gehst du + durch?". Zwei Auswahlen fuer dieselbe Frage waeren die + Gelegenheit, oben den einen und unten den anderen zu waehlen -- + und dann stuenden zwei Antworten auf einem Bildschirm. + + GEPRUEFT WIRD DESHALB DIE VERBINDUNG: Was die Seite oben sagt, + muss der Katalog unten meinen. */ + const ohne = await seite.evaluate(() => ({ + knopf: document.querySelector(".vorlagenbrett__karte .knopf")?.textContent.trim(), + satz: [...document.querySelectorAll("#vorlagenbrett .stufenleiter__hilfe")] + .map((z) => z.textContent.trim()).join(" | "), + hin: !!document.querySelector("#vorlagenbrett .knopf-still"), + reihen: document.querySelectorAll("#vorlagenbrett .stufenleiter").length, + })); + ok(ohne.reihen === 2, + "zwei Auswahlreihen statt drei -- die Person steht oben (" + ohne.reihen + ")"); + ok(ohne.knopf === "Übernehmen", + 'solange niemand gewaehlt ist, heisst der Knopf schlicht "Übernehmen" (' + + ohne.knopf + ")"); + ok(/niemand gewählt/.test(ohne.satz), "und ein Satz sagt genau das"); + /* KEINE WEGBESCHREIBUNG, SONDERN EIN WEG. "Waehle oben jemanden" + waere falsch -- die Kacheln stehen weiter UNTEN. Der Knopf + weiss, wo sie sind, auch nach dem naechsten Umbau. */ + ok(ohne.hin, "mit einem Knopf, der zur Personenwahl fuehrt"); + + /* JETZT WIRKLICH WAEHLEN -- ueber die Kachel, so wie ein Mensch. */ + await seite.evaluate(() => { + [...document.querySelectorAll("#personen .e-person")] + .find((k) => (k.dataset.name || k.textContent).includes("Marina"))?.click(); + }); + await seite.waitForTimeout(2400); + const nachWahl = await seite.evaluate(() => { + const karten = [...document.querySelectorAll(".vorlagenbrett__karte")]; + const frisch = karten.find((k) => k.dataset.schon !== "ja"); + const schon = karten.find((k) => k.dataset.schon === "ja"); return { - reihen: reihen.length, - namen: [...(letzte?.querySelectorAll(".stufenleiter__name") || [])] - .map((z) => z.textContent.trim()), - zahlenOk: [...(letzte?.querySelectorAll(".stufenleiter__zahl") || [])] - .every((z) => zahl.test(z.textContent.trim())), - zahlen: [...(letzte?.querySelectorAll(".stufenleiter__zahl") || [])] - .map((z) => z.textContent.trim()), - /* Der Satz steht seit dem 16.09.2026 in `stufenleiter__hilfe` - -- derselbe Baustein wie beim anderen Brett. Vorher war es - `vorlagenbrett__stand`, und das ist die Zeile fuer Zahlen. */ - satz: [...document.querySelectorAll(".stufenleiter__hilfe")] - .map((z) => z.textContent.trim()) - .find((t) => t.includes("Formular oben") || t.includes("gehen an")) || "", + satz: [...document.querySelectorAll("#vorlagenbrett .stufenleiter__hilfe")] + .map((z) => z.textContent.trim()).find((t) => t.includes("geht an")) || "", + /* ZWEI VERSCHIEDENE KARTEN, ZWEI VERSCHIEDENE KNOEPFE -- und + die erste im Feld ist nicht zwangslaeufig eine frische. + Die erste Fassung nahm schlicht "die erste Karte" und + meldete einen Fehler, weil dort "Nochmal" stand: genau das + richtige Wort fuer eine, die schon liegt. */ + frisch: frisch?.querySelector(".knopf")?.textContent.trim() || null, + schon: schon?.querySelector(".knopf")?.textContent.trim() || null, + liste: document.getElementById("e-aufgaben-titel")?.textContent || "", }; }); - /* ZWEI FEINE REIHEN: die Stufen und die Personen. Eine einzelne - Zahl haette nicht gesagt, welche der beiden fehlt. */ - /* DREI REIHEN: Kategorie, Stufe, Person. Die ersten beiden sehen - jetzt aus wie drueben beim anderen Brett. */ - ok(wahl.reihen === 3, "drei Auswahlreihen (Kategorie, Stufe, Person): " + wahl.reihen); - ok(wahl.namen.includes("Marina"), - "der Modi steht zur Auswahl (" + wahl.namen.join(", ") + ")"); - ok(!wahl.namen.includes("Tili") && !wahl.namen.includes("Schulle"), - "ein Creator und eine Managerin nicht — sie arbeiten im anderen Haus"); - ok(wahl.zahlenOk && wahl.zahlen.length === wahl.namen.length, - "neben jedem Namen steht, wie viel offen ist (" + wahl.zahlen.join(", ") + ")"); - ok(wahl.satz.includes("Formular oben"), - "und solange niemand gewählt ist, sagt die Seite es"); + ok(nachWahl.satz.includes("Marina"), "nach dem Klick steht es da: " + nachWahl.satz); + /* DER KNOPF NENNT DEN NAMEN. "Uebernehmen" allein laesst offen, + wer sie bekommt -- und die Antwort steht weit oben. */ + ok(nachWahl.frisch === "An Marina", + 'eine frische Karte traegt ihren Namen ("' + nachWahl.frisch + '")'); + /* Und eine, die sie schon hat, sagt das statt des Namens -- sonst + saehen beide gleich aus und man holte sie zweimal. */ + ok(nachWahl.schon === null || nachWahl.schon === "Nochmal", + 'eine schon uebernommene sagt "Nochmal" (' + + (nachWahl.schon === null ? "keine im Feld" : nachWahl.schon) + ")"); + ok(/Marina/.test(nachWahl.liste), + 'die Aufgabenliste darunter gehoert ihr ("' + nachWahl.liste + '")'); - /* JETZT WIRKLICH DRUECKEN -- und nachsehen, ob die Aufgabe bei - DIESEM Menschen landet. Ein Knopf, den niemand betaetigt hat, - ist kein geprueter Knopf. */ - await seite.evaluate(() => { - const reihen = [...document.querySelectorAll(".stufenleiter")]; - const letzte = document.querySelector('[aria-label="Person wählen"]'); - [...letzte.querySelectorAll(".stufenleiter__knopf")] - .find((k) => k.textContent.includes("Marina"))?.click(); - }); - await seite.waitForTimeout(600); - const nachWahl = await seite.evaluate(() => - [...document.querySelectorAll(".stufenleiter__hilfe")] - .map((z) => z.textContent.trim()).find((t) => t.includes("gehen an")) || ""); - ok(nachWahl.includes("Marina"), "nach dem Klick steht es da: " + nachWahl); + /* GEZAEHLT WIRD IN IHRER LISTE, NICHT AUF DEM BRETT. - /* GEZAEHLT WERDEN AUFGABEN, NICHT KARTEN. - Seit dem 23.09.2026 fasst das Brett gleiche Aufgaben zu EINER - Karte zusammen ("0 von 8"). Eine neue Aufgabe erhoeht die - Kartenzahl also nur dann, wenn es noch keine gleichnamige gibt -- - sonst wandert sie in eine bestehende Karte hinein. Genau das - war die Absicht des Umbaus; eine Pruefung, die trotzdem Karten - zaehlt, misst ab da die alte Anordnung und nicht die Sache. - Jede Karte weist ihre Aufgaben aus: data-id (eine) oder - data-ids (mehrere). */ - const zaehleAufgaben = () => [...document.querySelectorAll(".karte")] - .reduce((n, k) => n + (k.dataset.ids || k.dataset.id || "") - .split(",").filter(Boolean).length, 0); - const vorher = await seite.evaluate(zaehleAufgaben); + Auf "Aufgaben" stand unter dem Katalog das Brett, und die + Pruefung zaehlte dort die Karten. Hier steht darunter die + Aufgabenliste der gewaehlten Person -- und die ist die bessere + Antwort auf die Frage, die der Abschnitt stellt: Landet die + Aufgabe bei DIESEM Menschen? Auf dem Brett musste man das aus + dem Namen auf der Karte herauslesen; hier ist es die Liste + selbst, die ihren Namen traegt. */ + const zaehleListe = () => + document.querySelectorAll("#e-aufgaben-liste .e-aufgabe").length; + const vorher = await seite.evaluate(zaehleListe); + const welche = await seite.evaluate(() => + document.querySelector(".vorlagenbrett__karte .vorlagenbrett__titel") + ?.textContent.trim() || ""); await seite.evaluate(() => { [...document.querySelectorAll(".vorlagenbrett__karte")][0] ?.querySelector("button")?.click(); }); - await seite.waitForTimeout(1800); - const danach = await seite.evaluate(() => ({ - aufgaben: [...document.querySelectorAll(".karte")] - .reduce((n, k) => n + (k.dataset.ids || k.dataset.id || "") - .split(",").filter(Boolean).length, 0), - karten: document.querySelectorAll(".karte").length, - fuerMarina: [...document.querySelectorAll(".karte")] - .filter((k) => k.textContent.includes("Marina")).length, - })); - ok(danach.aufgaben === vorher + 1, - "die Aufgabe liegt auf dem Brett (" + vorher + " -> " + danach.aufgaben - + " Aufgaben in " + danach.karten + " Karten)"); - ok(danach.fuerMarina > 0, "und zwar bei Marina (" + danach.fuerMarina + ")"); + await seite.waitForTimeout(2600); + const danach = await seite.evaluate((titel) => ({ + liste: document.querySelectorAll("#e-aufgaben-liste .e-aufgabe").length, + steht: [...document.querySelectorAll("#e-aufgaben-liste .e-aufgabe")] + .some((a) => a.textContent.includes(titel)), + ueberschrift: document.getElementById("e-aufgaben-titel")?.textContent || "", + markiert: [...document.querySelectorAll(".vorlagenbrett__karte")] + .filter((k) => k.dataset.schon === "ja").length, + }), welche); + ok(danach.liste === vorher + 1, + "die Aufgabe liegt in ihrer Liste (" + vorher + " -> " + danach.liste + ")"); + ok(danach.steht, 'und zwar genau die uebernommene ("' + welche + '")'); + ok(/Marina/.test(danach.ueberschrift), + "die Liste ist ihre (" + danach.ueberschrift.trim() + ")"); + /* UND DER KATALOG WEISS ES. Ohne diese Zeile koennte man dieselbe + Vorlage gleich noch einmal holen, ohne Hinweis -- genau das war + der Grund fuer die Markierung. */ + ok(danach.markiert >= 1, + "der Katalog markiert sie als uebernommen (" + danach.markiert + ")"); /* ==== DIE BEWERBUNG AM BILDSCHIRM ENTSCHEIDEN (23.09.2026) ====== @@ -838,7 +868,11 @@ console.log(""); ok(bw?.auffaellig === true, "und die Karte faellt auf"); /* ---- Jetzt wirklich druecken, samt Notiz ---------------------- */ - const vorherK = await seite.evaluate(() => document.querySelectorAll(".karte").length); + /* Auch hier: die Liste der gewaehlten Person, nicht das Brett. + Marina ist oben gewaehlt, und ihre Bewerbung wird gleich + angenommen -- die Aufgabe muss also bei ihr auftauchen. */ + const vorherK = await seite.evaluate(() => + document.querySelectorAll("#e-aufgaben-liste .e-aufgabe").length); await seite.evaluate(() => { [...document.querySelectorAll(".vorlagenbrett__bewerbung button")] .find((b) => b.textContent.trim() === "Annehmen")?.click(); @@ -860,7 +894,7 @@ console.log(""); await seite.waitForTimeout(2200); const nachher = await seite.evaluate(() => ({ - karten: document.querySelectorAll(".karte").length, + karten: document.querySelectorAll("#e-aufgaben-liste .e-aufgabe").length, nochBeworben: document.querySelectorAll(".vorlagenbrett__bewerbung").length, })); ok(nachher.karten === vorherK + 1, diff --git a/server/pruef-modi-kategorien.mjs b/server/pruef-modi-kategorien.mjs index f2f029bd..51bc514a 100644 --- a/server/pruef-modi-kategorien.mjs +++ b/server/pruef-modi-kategorien.mjs @@ -142,12 +142,36 @@ console.log("\n=== 2. Der Modi setzt eine Kategorie ===\n"); const jsonKopf = { "Content-Type": "application/json" }; let aufgabeId = null; { - const a = await hole(modiAn.keks, "/workspace/api/aufgaben", { + /* IM TEAM DOGI LEGT DIE LEITUNG AN (nachgezogen 23.09.2026). + + Hier stand `modiAn` -- und bis zum 22.09.2026 war das richtig. + An dem Tag bekam der Server die Regel "ein Modi gibt sich keine + Aufgaben" (Filipe: "sie sich nicht selber aufgaben geben"), und + diese Pruefung wurde nicht nachgezogen. Seither meldete sie + "angelegt (403)" -- ein Fehler, der keiner war, und der bei jedem + Lauf mitlief, bis ihn niemand mehr las. + + Die Kategorie wird dabei MITGESCHICKT: Genau darum geht es in + diesem Abschnitt, und ob sie den Weg ueber das Anlegen durch + jemand anderen ueberlebt, war vorher nie geprueft. */ + const a = await hole(dogiAn.keks, "/workspace/api/aufgaben", { method: "POST", headers: jsonKopf, - body: JSON.stringify({ titel: "Clip vom Freitags-Live schneiden", kategorie: "clipping" }), + body: JSON.stringify({ titel: "Clip vom Freitags-Live schneiden", + kategorie: "clipping", verantwortlich_id: idModi }), }); aufgabeId = a.daten?.id ?? null; - ok(a.status === 201 && Number.isInteger(aufgabeId), `angelegt (${a.status})`); + ok(a.status === 201 && Number.isInteger(aufgabeId), + `die Leitung legt sie fuer ihn an (${a.status})`); + + /* UND DIE GEGENPROBE, die es vorher gar nicht gab: Er selbst kann + es nicht. Ohne sie bewiese die Zeile darueber nur, dass das + Anlegen ueberhaupt geht -- nicht, dass die Schranke steht. */ + const b = await hole(modiAn.keks, "/workspace/api/aufgaben", { + method: "POST", headers: jsonKopf, + body: JSON.stringify({ titel: "Etwas fuer mich selbst", kategorie: "clipping" }), + }); + ok(b.status === 403 && b.daten?.fehler === "nur_leitung_legt_an", + `er selbst darf es nicht (${b.status} ${b.daten?.fehler || ""})`); } if (aufgabeId) { const a = await hole(modiAn.keks, "/workspace/api/aufgaben"); @@ -178,12 +202,19 @@ if (aufgabeId) { /* ---------- 3. Ein Tippfehler ist ein Fehler ------------------------- */ console.log("\n=== 3. Was bei einem falschen Wert passiert ===\n"); { - const a = await hole(modiAn.keks, "/workspace/api/aufgaben", { + /* AUCH HIER DIE LEITUNG (nachgezogen 23.09.2026, gleicher Grund). + + Ein Modi kaeme gar nicht bis zur Feldpruefung -- die Schranke + steht davor, und die Antwort waere 403 statt 400. Gemessen wuerde + dann nicht mehr, was dieser Abschnitt wissen will: dass ein + falscher Kategoriewert BENANNT wird statt still zu verschwinden. */ + const a = await hole(dogiAn.keks, "/workspace/api/aufgaben", { method: "POST", headers: jsonKopf, - body: JSON.stringify({ titel: "Irgendwas", kategorie: "gibtesnicht" }), + body: JSON.stringify({ titel: "Irgendwas", kategorie: "gibtesnicht", + verantwortlich_id: idModi }), }); ok(a.status === 400 && /Kategorie/i.test(a.daten?.fehler || ""), - `dem Modi wird es gesagt (${a.status} ${a.daten?.fehler || ""})`); + `der falsche Wert wird benannt (${a.status} ${a.daten?.fehler || ""})`); } /* ---------- 4. Und wenn ein Manager sie schickt? --------------------- */ @@ -249,21 +280,52 @@ console.log("\n=== 5. Das Feld im echten Formular ===\n"); ok(z.sichtbar, "der Modi sieht das Feld"); ok(z.auswahl.length >= 7, `mit ${z.auswahl.length} Eintraegen (${z.auswahl.slice(1, 4).join(", ")} …)`); - /* DAS VORLAGENBRETT (berichtigt 10.09.2026). + /* DAS VORLAGENBRETT (berichtigt 10.09., umgezogen 23.09.2026). - Hier stand "und kein Creator-Vorlagenbrett" -- richtig, solange - es fuer einen Modi ueberhaupt keinen Katalog gab. Seit der - Katalog aus Teil 2 da ist, steht das Brett wieder da, nur mit - SEINEM Inhalt. Geprueft wird deshalb ab jetzt beides: dass es - da ist, und dass der fremde nicht durchschlaegt. Was der - Katalog selbst enthaelt, prueft pruef-modi-katalog. */ + Am 10.09. stand hier "und kein Creator-Vorlagenbrett" -- + richtig, solange es fuer einen Modi ueberhaupt keinen Katalog + gab. Seit dem Katalog aus Teil 2 stand das Brett wieder da, + nur mit SEINEM Inhalt. + + Seit dem 23.09.2026 steht es nicht mehr HIER. + + Filipe: "das soll auch bitte nicht in der kategorie aufgaben + sondern eure aufgaben sein bitte." + + GEPRUEFT WIRD DER UMZUG IN BEIDE RICHTUNGEN. Nur "hier ist er + weg" waere die halbe Auskunft: Weg ist er auch dann, wenn ihn + ein Fehler ueberall verschwinden laesst. Erst mit der zweiten + Haelfte -- "und drueben ist er da" -- sagt die Pruefung etwas + ueber den Umzug statt ueber einen Ausfall. */ const vorlagen = await seite.evaluate(() => { const v = document.getElementById("vorlagenbrett"); - return { da: !!v && !v.hidden, text: document.body.innerText }; + return { + leer: !v || v.hidden, + datei: !!window.Vorlagenbrett, + text: document.body.innerText, + }; }); - ok(vorlagen.da, "das Vorlagenbrett steht da -- mit seinem eigenen Katalog"); + ok(vorlagen.datei, "die Katalogdatei ist auch hier geladen"); + ok(vorlagen.leer, "aber der Katalog steht nicht mehr auf dieser Seite"); ok(!/vier Bereichen und vier Stufen/.test(vorlagen.text), "und kein Wort vom Creator-Katalog"); + /* DIE ANDERE HAELFTE: drueben steht er. */ + { + const s2 = await kontext.newPage(); + await s2.goto(BASIS + "/workspace/entwicklung.html", { waitUntil: "networkidle" }); + await s2.waitForTimeout(1600); + const drueben = await s2.evaluate(() => { + const v = document.getElementById("vorlagenbrett"); + return { + da: !!v && !v.hidden, + kopf: document.querySelector(".vorlagenbrett__unter")?.textContent || "", + }; + }); + ok(drueben.da, "dafuer steht er auf „Eure Aufgaben“"); + ok(/Kategorien/.test(drueben.kopf), + `mit seinem eigenen Kopf ("${drueben.kopf.slice(0, 55)}…")`); + await s2.close(); + } ok(konsole.length === 0, konsole.length ? `Fehler in der Konsole: ${konsole[0]}` : "keine Fehler in der Konsole"); await seite.screenshot({ path: "pruef-modi-kategorie.png", fullPage: false }); @@ -342,9 +404,30 @@ console.log("\n=== 5. Das Feld im echten Formular ===\n"); /* GEGENPROBE ZUM BROWSERTEIL: Findet die Suche die Namen ueberhaupt, wenn sie dastehen? Ohne diesen Schritt bewiese der Haken oben - nichts -- er waere auch gruen, wenn innerText leer waere. */ + nichts -- er waere auch gruen, wenn innerText leer waere. + + GESUCHT WIRD SEIT DEM 23.09.2026 AUF "EURE AUFGABEN". + + Bis dahin stand hier "aufgaben.html" -- und die Namen standen + dort, weil das Vorlagenbrett sie als Knopfreihe zeigte. Mit dem + Umzug des Katalogs sind sie mitgewandert. Die Gegenprobe hing + also an etwas, das mit ihrer eigentlichen Frage nichts zu tun + hatte; sie wurde rot, ohne dass die Verborgenheit beim Manager + auch nur beruehrt worden waere. + + Das ist die Sorte Abhaengigkeit, die man erst bemerkt, wenn sie + bricht: Die Zeile oben prueft eine Seite, die Gegenprobe dazu + eine andere -- und dass beide dieselbe waren, war Zufall. */ { - const { kontext, seite } = await formular("creator", "CODE-MODI-0001"); + const kontext = await browser.newContext(); + const seite = await kontext.newPage(); + await seite.goto(BASIS + "/workspace/", { waitUntil: "domcontentloaded" }); + await seite.click('.rolle[data-rolle="creator"]'); + await seite.fill("#code", "CODE-MODI-0001"); + await seite.click("button[type=submit]"); + await seite.waitForURL("**/start.html", { timeout: 15000 }); + await seite.goto(BASIS + "/workspace/entwicklung.html", { waitUntil: "networkidle" }); + await seite.waitForTimeout(1600); const text = await seite.evaluate(() => document.body.innerText); ok(kategorien.length > 0 && kategorien.some((k) => text.includes(k.name)), "beim Modi findet dieselbe Suche die Namen sehr wohl"); diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index 3e309773..cb011bff 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`. --> - - + + - +