Reaction: Material statt Kaesten, Spende in der Mitte, Gaeste werden gefragt
Nach Filipes sieben Bildschirmfotos vom 07.10.2026. Sieben Wuensche,
und einer davon ("alles viel hochwertiger") betrifft alle anderen --
deshalb steht das Fundament zuerst und nicht am Ende.
DAS AUSSEHEN HAT EINEN WERKSTOFF. Jede Flaeche sagt jetzt, ob sie oben
oder unten liegt: Was obenauf liegt, bekommt oben eine helle Kante und
darunter einen Schatten; was vertieft ist, genau umgekehrt. Dazwischen
gibt es nichts mehr. Vorher stand im ganzen Pult zweimal eine
Lichtkante und sonst nirgends -- genau deshalb sahen diese zwei Stellen
gut aus und alles daneben danebengestellt. Dazu: jeder Knopf senkt sich
beim Druecken, jedes Feld ist ein Loch und kein Kasten, und wer mit der
Tastatur bedient, wird zum ersten Mal ueberhaupt gesehen.
UND EIN FUND DABEI: Die drei Kacheln des Regieplatzes hiessen in der
HTML anders als im Stilblatt (kachel--laeuft gegen regiekachel--laeuft,
regieregiekachel__kopf gegen regiekachel__kopf). Die Aufteilung "links
was laeuft, rechts was man eintraegt" hat damit NIE gegriffen -- und
mess-reaktion hat den falschen Zustand gemessen und gruen gemeldet,
weil sie gegen die HTML geschrieben war.
DIE SPENDENKARTE STEHT IN DER MITTE und kommt aus der Tiefe nach vorn.
Das dreht eine Entscheidung vom 28.09. um ("sie deckt nie die Mitte
zu"); die Begruendung war richtig und ist es noch -- sie schuetzt das
Video, und Filipe sieht die Spende als das, was in dem Augenblick
zaehlt. Der alte Platz bleibt einen Knopf weit weg und gilt fuer Saal
UND OBS-Tafel. Die Karte besteht dafuer aus zwei Teilen: `opacity`
unter 1 zwingt ein Element auf `transform-style: flat`, eine Karte mit
Deckkraft-Uebergang waere also genau waehrend des Hereinkommens flach
und spraenge am Ende in einem Bild in die Tiefe.
FERTIGE SAETZE GEHEN SOFORT INS BILD. Der Browser schickt nur die
Kennung, den Text holt der Server -- deshalb darf hier die Bestaetigung
entfallen und beim freien Text nicht. Zwei Schranken bleiben: sofort
nur mit einem der vier Knopfbetraege (Filipes Ansage galt dem SATZ,
nicht der Summe), und GEZEIGT IST NICHT GEBUCHT -- die Dogen gibt es
erst nach dem Tipp der Leitung, sonst stellt sich jeder seine eigene
Waehrung aus.
WER GESPENDET HAT, GLAENZT IM CHAT -- je nach Hoehe. Die Dauer haengt
an der Stufe und nicht an einer zweiten Leiter; 0 heisst "gar nicht".
GAESTE WERDEN GEFRAGT, NICHT GEHOLT. Vorher sprang die Kamera eines
Zuschauers an, sobald jemand "Dazuholen" drueckte -- er hat es daran
gemerkt, dass er sich selbst sah. Jetzt gibt es einen Vorraum: eigenes
Bild, Pegel, Kamera und Mikro vorwaehlen, Geraete waehlen, dann "Dabei
sein" oder "Lieber nicht". Zwei Minuten Frist, und sie verfaellt
SICHTBAR -- eine verschwundene Zeile sieht fuer den Host aus wie ein
Verklicker.
DAS MISCHPULT. Hier stand fest `echoCancellation: true,
noiseSuppression: true`. Fuer ein Laptopmikrofon richtig, ueber einem
RODECaster falsch: Das Pult hat Gate, Kompressor und EQ schon gemacht,
und die Echo-Unterdrueckung legt sich danach als zweite Automatik
darueber und duckt, sobald von der Gegenseite Ton kommt -- genau das
Pumpen vom 14.09. Jetzt: Geraetewahl fuer Mikro, Kamera und Ausgabe
(auch fuer Gaeste), ein Pultmodus, und darunter GEMESSEN, was wirklich
ankommt. Mit und ohne Pult.
DAS ZWEITE VIDEO war nie gesperrt -- es war nur unsichtbar. Jetzt
stehen Vorschaubild, Titel und die gemerkte Stelle da, "Jetzt wechseln"
auch von hier aus, und aus der Warteschlange legt ein Knopf eines
bereit.
UND EIN FUND AUF DEM BILDSCHIRMFOTO: Das Band "Ton einschalten" lag auf
derselben Hoehe wie die Senderleiste. Bei vier Knoepfen reichte die
Leiste bis zur Mitte -- vom Mikro-Knopf war nur noch ein "t" zu sehen.
Das war schon vorher so; der fuenfte Knopf hat es sichtbar gemacht.
Dieselbe Lehre wie am 06.09.: keine neue Zahl suchen, sondern die
Begegnung unmoeglich machen. mess-reaktion misst das jetzt als
UEBERLAPPUNG.
GEPRUEFT: pruef-reaktion 423 -> 478 (0 Fehler), pruef-spenden 172 -> 215
(0), pruef-reaktion-schmal 44 (0), pruef-tippziele 13 (0), dazu gruen:
pruef-css-klassen, pruef-alle-wege, pruef-haus-trennung, pruef-agentur,
pruef-deutsche-texte. Keine Zahl ist gefallen.
Drei Doppelungen hat erst das Pruefen gefunden: `satz_unbekannt` gab es
zweimal (Muenzsatz), `saetze` im Spendenstand war belegt (ebenfalls
Muenzsaetze), und `glanz_minuten` waere in einem frischen Haus immer 0
geblieben -- die Spalte kam per ALTER TABLE, und das laeuft VOR dem
Anlegen der Werksstufen.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+350
-6
@@ -401,22 +401,126 @@ melde("=== 6. Ein bis zwei Gaeste ===");
|
||||
const { GAESTE_MAX } = await import("./reaktion-tabellen.js");
|
||||
ok(GAESTE_MAX === 2, `zwei Plaetze, wie in der Notiz (${GAESTE_MAX})`);
|
||||
|
||||
/* =====================================================================
|
||||
SEIT DEM 08.10.2026 WIRD GEFRAGT, NICHT GEHOLT
|
||||
|
||||
Filipe: „wenn gäste eingeladen werden, sollen die akzeptieren
|
||||
oder ablehnen können. bevor die auch angezeigt werden sollen die
|
||||
auswählen können ob die kamera an ist oder aus ist."
|
||||
|
||||
Diese Pruefung hiess vorher „Gast 1 dazu" und erwartete nach
|
||||
EINEM Aufruf einen Gast auf der Buehne. Genau das darf jetzt
|
||||
nicht mehr passieren -- und sie ist rot geworden, als es nicht
|
||||
mehr passierte. Das ist der Fall, fuer den sie da ist.
|
||||
===================================================================== */
|
||||
const a = await roh(CREW, "/workspace/api/reaktion/gast", {
|
||||
method: "POST", keks: k.admin, rumpf: { person_id: IDS.gast },
|
||||
});
|
||||
ok(a.code === 201 && a.j?.gaeste?.length === 1, `Gast 1 dazu (${a.code})`);
|
||||
ok(a.j.gaeste[0].platz === 1, `er bekommt Platz 1 (${a.j?.gaeste?.[0]?.platz})`);
|
||||
ok(a.code === 201, `Gast 1 eingeladen (${a.code})`);
|
||||
ok((a.j?.gaeste || []).length === 0,
|
||||
`und steht NICHT auf der Buehne, bevor er ja gesagt hat (${a.j?.gaeste?.length})`);
|
||||
ok((a.j?.einladungen || []).some((e) => e.id === IDS.gast && e.stand === "gefragt"),
|
||||
"der Host sieht die offene Frage");
|
||||
ok((a.j?.einladungen || [])[0]?.noch_sek > 0,
|
||||
`und wie lange sie noch gilt (${a.j?.einladungen?.[0]?.noch_sek}s)`);
|
||||
|
||||
/* ==== DER GEFRAGTE WEISS ES -- UND SONST NIEMAND ============== */
|
||||
const gLage = await roh(CREW, "/workspace/api/reaktion", { keks: k.gast });
|
||||
ok(gLage.j?.ich?.einladung?.noch_sek > 0,
|
||||
"der Gefragte bekommt seine Einladung mit Restzeit");
|
||||
ok(gLage.j?.ich?.gast === false,
|
||||
"er ist dabei aber noch KEIN Gast");
|
||||
ok(gLage.j?.einladungen === undefined,
|
||||
"und er sieht die Liste der Einladungen nicht (die gehoert dem Host)");
|
||||
const fremd = await roh(CREW, "/workspace/api/reaktion", { keks: k.gast2 });
|
||||
ok(!fremd.j?.ich?.einladung,
|
||||
"wer nicht gefragt wurde, hat auch keine Einladung");
|
||||
ok(fremd.j?.einladungen === undefined,
|
||||
"und erfaehrt nicht, dass jemand anderes gefragt wurde");
|
||||
|
||||
/* ==== EIN GEFRAGTER SENDET NOCH NICHT =========================
|
||||
Die wichtigste Pruefung dieses Abschnitts: Solange er nicht
|
||||
zugesagt hat, darf er nichts schalten -- sonst waere „gefragt"
|
||||
nur ein anderes Wort fuer „dabei". */
|
||||
const fruehSenden = await roh(CREW, "/workspace/api/reaktion/senden", {
|
||||
method: "POST", keks: k.gast, rumpf: { kamera_aus: true },
|
||||
});
|
||||
ok(fruehSenden.code === 403,
|
||||
`wer nur gefragt ist, sendet nicht (${fruehSenden.code})`);
|
||||
|
||||
/* ==== DIE ZUSAGE BRINGT IHN AUF DIE BUEHNE -- MIT KAMERA AUS == */
|
||||
const ja = await roh(CREW, "/workspace/api/reaktion/gast/antwort", {
|
||||
method: "POST", keks: k.gast, rumpf: { ja: true, kamera: false, mikro: true },
|
||||
});
|
||||
ok(ja.code === 200 && (ja.j?.gaeste || []).length === 1,
|
||||
`nach der Zusage steht er auf der Buehne (${ja.code})`);
|
||||
ok(ja.j?.gaeste?.[0]?.platz === 1, `er bekommt Platz 1 (${ja.j?.gaeste?.[0]?.platz})`);
|
||||
/* DIE VORWAHL MUSS AM SERVER ANGEKOMMEN SEIN. Haette sie nur im
|
||||
Browser gegolten, saehe der ganze Saal ein schwarzes Fenster
|
||||
ohne Beschriftung -- und der Gast glaubte, seine Kamera sei
|
||||
aus, waehrend sein Bild hinausgeht. */
|
||||
ok(ja.j?.gaeste?.[0]?.kamera_aus === true,
|
||||
"seine Kamera ist aus, weil er sie abgewaehlt hat");
|
||||
ok(ja.j?.gaeste?.[0]?.mikro_aus === false,
|
||||
"sein Mikro ist an, weil er es angelassen hat");
|
||||
ok((ja.j?.einladungen || []).length === 0,
|
||||
"die beantwortete Einladung ist aus der Liste");
|
||||
|
||||
/* ==== ABLEHNEN BLEIBT SICHTBAR ===============================
|
||||
Verschwaende die Zeile, saehe der Host nur, dass nichts
|
||||
passiert -- und lade dieselbe Person ein zweites Mal ein. */
|
||||
const b0 = await roh(CREW, "/workspace/api/reaktion/gast", {
|
||||
method: "POST", keks: k.admin, rumpf: { person_id: IDS.gast2 },
|
||||
});
|
||||
ok(b0.code === 201, `Gast 2 eingeladen (${b0.code})`);
|
||||
const nein = await roh(CREW, "/workspace/api/reaktion/gast/antwort", {
|
||||
method: "POST", keks: k.gast2, rumpf: { ja: false },
|
||||
});
|
||||
ok(nein.code === 200, `ablehnen geht (${nein.code})`);
|
||||
const nachNein = await roh(CREW, "/workspace/api/reaktion", { keks: k.admin });
|
||||
ok((nachNein.j?.gaeste || []).length === 1,
|
||||
"wer ablehnt, steht nicht auf der Buehne");
|
||||
ok((nachNein.j?.einladungen || []).some(
|
||||
(e) => e.id === IDS.gast2 && e.stand === "abgelehnt"),
|
||||
"aber der Host sieht, DASS abgelehnt wurde");
|
||||
|
||||
/* ==== OHNE EINLADUNG KEINE ANTWORT =========================== */
|
||||
const ohne = await roh(CREW, "/workspace/api/reaktion/gast/antwort", {
|
||||
method: "POST", keks: k.modi, rumpf: { ja: true },
|
||||
});
|
||||
ok(ohne.code === 404 && ohne.j?.fehler === "keine_einladung",
|
||||
`wer nicht gefragt wurde, kann nicht zusagen (${ohne.code}/${ohne.j?.fehler})`);
|
||||
|
||||
/* Die abgelehnte Zeile wegraeumen -- sonst zaehlt sie beim
|
||||
naechsten Abschnitt mit. (Sie zaehlt NICHT fuer die Grenze,
|
||||
siehe gleich; weg muss sie trotzdem, weil der Host sie sonst
|
||||
bis zum Sendungsende vor sich hat.) */
|
||||
await roh(CREW, `/workspace/api/reaktion/gast/${IDS.gast2}`, {
|
||||
method: "DELETE", keks: k.admin,
|
||||
});
|
||||
|
||||
/* ==== EINE OFFENE FRAGE HAELT IHREN PLATZ ====================
|
||||
Sonst laedt der Host zwei Leute ein, beide sagen ja, und der
|
||||
zweite steht vor einer vollen Buehne -- nachdem er zugesagt
|
||||
hat. */
|
||||
const b = await roh(CREW, "/workspace/api/reaktion/gast", {
|
||||
method: "POST", keks: k.admin, rumpf: { person_id: IDS.gast2 },
|
||||
});
|
||||
ok(b.code === 201 && b.j?.gaeste?.length === 2, `Gast 2 dazu (${b.code})`);
|
||||
ok(b.code === 201, `Gast 2 wieder eingeladen (${b.code})`);
|
||||
|
||||
const c = await roh(CREW, "/workspace/api/reaktion/gast", {
|
||||
method: "POST", keks: k.admin, rumpf: { person_id: IDS.modi },
|
||||
});
|
||||
ok(c.code === 409 && c.j?.fehler === "gaeste_voll",
|
||||
`ein dritter passt nicht (${c.code}/${c.j?.fehler})`);
|
||||
`ein dritter passt nicht -- die offene Frage zaehlt mit (${c.code}/${c.j?.fehler})`);
|
||||
|
||||
/* Und jetzt sagt der Zweite auch zu, damit der Rest des
|
||||
Abschnitts zwei Gaeste auf der Buehne vorfindet. */
|
||||
const ja2 = await roh(CREW, "/workspace/api/reaktion/gast/antwort", {
|
||||
method: "POST", keks: k.gast2, rumpf: { ja: true, kamera: true, mikro: true },
|
||||
});
|
||||
ok(ja2.code === 200 && (ja2.j?.gaeste || []).length === 2,
|
||||
`Gast 2 ist dabei (${ja2.code})`);
|
||||
|
||||
/* EIN GAST BEKOMMT DIE NAMENSLISTE -- und das ist kein Zufall:
|
||||
Ohne sie wuesste er nicht, wen er anrufen soll, und sein Bild
|
||||
@@ -454,13 +558,81 @@ melde("=== 6. Ein bis zwei Gaeste ===");
|
||||
|
||||
/* Der frei gewordene Platz wird WIEDERVERWENDET. Mit „Anzahl + 1"
|
||||
bliebe Platz 1 fuer immer leer, sobald einmal jemand gegangen
|
||||
ist -- und das faellt erst im dritten Stream auf. */
|
||||
const neu = await roh(CREW, "/workspace/api/reaktion/gast", {
|
||||
ist -- und das faellt erst im dritten Stream auf.
|
||||
|
||||
Der Platz wird seit dem 08.10.2026 bei der ZUSAGE vergeben und
|
||||
nicht bei der Einladung: Wer ablehnt, soll Platz 1 nicht zwei
|
||||
Minuten lang blockiert haben. */
|
||||
await roh(CREW, "/workspace/api/reaktion/gast", {
|
||||
method: "POST", keks: k.admin, rumpf: { person_id: IDS.modi },
|
||||
});
|
||||
const neu = await roh(CREW, "/workspace/api/reaktion/gast/antwort", {
|
||||
method: "POST", keks: k.modi, rumpf: { ja: true, kamera: true, mikro: true },
|
||||
});
|
||||
const plaetze = (neu.j?.gaeste || []).map((x) => x.platz).sort();
|
||||
ok(plaetze.join(",") === "1,2",
|
||||
`der frei gewordene Platz wird wiederverwendet (${plaetze.join(",")})`);
|
||||
|
||||
/* ==== DIE FRIST IST ECHT UND NICHT NUR EIN TEXT ===============
|
||||
(08.10.2026)
|
||||
|
||||
Eine Einladung, die nach zwei Minuten „abgelaufen" HEISST, aber
|
||||
weiterhin angenommen werden kann, ist keine Frist. Geprueft wird
|
||||
das nicht mit Warten -- zwei Minuten Wartezeit in einem Pruflauf
|
||||
waere genau die Sorte Pruefung, die beim naechsten Mal jemand
|
||||
ueberspringt. Stattdessen wird der Zeitpunkt der Frage in der
|
||||
Datenbank zurueckdatiert; das ist dieselbe Lage, nur sofort.
|
||||
|
||||
GEGENPROBE INBEGRIFFEN: Vorher wird bestaetigt, dass dieselbe
|
||||
Einladung OHNE Rueckdatierung angenommen worden waere. Ohne sie
|
||||
bewiese ein 409 nur, dass irgendetwas nicht ging. */
|
||||
{
|
||||
const { db } = await import("./workspace.js");
|
||||
/* Erst Platz schaffen -- die Buehne ist gerade voll. */
|
||||
await roh(CREW, `/workspace/api/reaktion/gast/${IDS.modi}`, {
|
||||
method: "DELETE", keks: k.admin,
|
||||
});
|
||||
const e1 = await roh(CREW, "/workspace/api/reaktion/gast", {
|
||||
method: "POST", keks: k.admin, rumpf: { person_id: IDS.modi },
|
||||
});
|
||||
ok(e1.code === 201, `fuer die Fristprobe noch einmal gefragt (${e1.code})`);
|
||||
const vorDatum = await roh(CREW, "/workspace/api/reaktion", { keks: k.modi });
|
||||
ok(vorDatum.j?.ich?.einladung?.noch_sek > 0,
|
||||
"Gegenprobe: unmittelbar nach der Frage gilt sie noch");
|
||||
|
||||
/* Zehn Minuten zurueck -- weit jenseits der zwei Minuten. */
|
||||
const alt = new Date(Date.now() - 600_000).toISOString();
|
||||
db().prepare("UPDATE reaktion_gaeste SET gefragt_am = ? WHERE person_id = ?")
|
||||
.run(alt, IDS.modi);
|
||||
|
||||
const zuSpaet = await roh(CREW, "/workspace/api/reaktion/gast/antwort", {
|
||||
method: "POST", keks: k.modi, rumpf: { ja: true },
|
||||
});
|
||||
ok(zuSpaet.code === 409 && zuSpaet.j?.fehler === "einladung_abgelaufen",
|
||||
`eine abgelaufene Einladung wird abgelehnt (${zuSpaet.code}/${zuSpaet.j?.fehler})`);
|
||||
|
||||
const danach = await roh(CREW, "/workspace/api/reaktion", { keks: k.modi });
|
||||
ok(!danach.j?.ich?.einladung,
|
||||
"und der Gefragte sieht sie nicht mehr");
|
||||
const beimHost = await roh(CREW, "/workspace/api/reaktion", { keks: k.admin });
|
||||
ok((beimHost.j?.einladungen || []).some(
|
||||
(x) => x.id === IDS.modi && x.stand === "keine_antwort"),
|
||||
"der Host sieht „keine Antwort“ statt einer verschwundenen Zeile");
|
||||
|
||||
await roh(CREW, `/workspace/api/reaktion/gast/${IDS.modi}`, {
|
||||
method: "DELETE", keks: k.admin,
|
||||
});
|
||||
/* Und wieder herstellen, was der naechste Abschnitt erwartet:
|
||||
zwei Gaeste auf der Buehne. */
|
||||
await roh(CREW, "/workspace/api/reaktion/gast", {
|
||||
method: "POST", keks: k.admin, rumpf: { person_id: IDS.modi },
|
||||
});
|
||||
const zurueck = await roh(CREW, "/workspace/api/reaktion/gast/antwort", {
|
||||
method: "POST", keks: k.modi, rumpf: { ja: true, kamera: true, mikro: true },
|
||||
});
|
||||
ok((zurueck.j?.gaeste || []).length === 2,
|
||||
`nach der Fristprobe stehen wieder zwei auf der Buehne (${zurueck.j?.gaeste?.length})`);
|
||||
}
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
@@ -1142,9 +1314,18 @@ melde("=== 13. Kamera und Chat ===");
|
||||
for (const rolle of ["admin", "hand", "linke", "modi", "gast", "gast2"]) {
|
||||
await roh(CREW, "/workspace/api/reaktion/dabei", { method: "POST", keks: k[rolle] });
|
||||
}
|
||||
/* ZWEI SCHRITTE SEIT DEM 08.10.2026: fragen und zusagen. Hier
|
||||
steht beides hintereinander, weil dieser Abschnitt die Kamera
|
||||
prueft und nicht die Einladung -- die hat ihren eigenen
|
||||
Abschnitt. Der zweite Aufruf gehoert trotzdem dazu: Ohne ihn
|
||||
stuende hier ein Gefragter und kein Gast, und alles darunter
|
||||
maesse etwas anderes. */
|
||||
await roh(CREW, "/workspace/api/reaktion/gast", {
|
||||
method: "POST", keks: k.admin, rumpf: { person_id: IDS.gast2 },
|
||||
});
|
||||
await roh(CREW, "/workspace/api/reaktion/gast/antwort", {
|
||||
method: "POST", keks: k.gast2, rumpf: { ja: true, kamera: true, mikro: true },
|
||||
});
|
||||
|
||||
const stand0 = await roh(CREW, "/workspace/api/reaktion", { keks: k.admin });
|
||||
ok(stand0.j?.stand === "live" && stand0.j?.gaeste?.length === 1,
|
||||
@@ -2856,6 +3037,169 @@ melde("=== Beendet heisst leer ===");
|
||||
`wer nur vorbereitet hat, behaelt seine Eingaben ("${probe.j?.titel}")`);
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
DER SPENDER-GLANZ IM CHAT (08.10.2026)
|
||||
|
||||
Filipe: „die leute die spenden gemacht haben sollen auch für eine
|
||||
kurze zeit speziell aussehen je nach höhe von spende."
|
||||
|
||||
Geprueft wird hier der WEG: Traegt ein Chatbeitrag die Auskunft,
|
||||
und steht sie nur bei dem, der wirklich gegeben hat? Dass die
|
||||
Stufen stimmen und dass der Glanz ablaeuft, prueft `pruef-spenden`
|
||||
-- dort steht die Leiter.
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 18. Spender im Chat ===");
|
||||
{
|
||||
const { db } = await import("./workspace.js");
|
||||
|
||||
await roh(CREW, "/workspace/api/reaktion", {
|
||||
method: "PUT", keks: k.admin,
|
||||
rumpf: { titel: "Glanzprobe", video: "dQw4w9WgXcQ" },
|
||||
});
|
||||
await roh(CREW, "/workspace/api/reaktion/stand",
|
||||
{ method: "POST", keks: k.admin, rumpf: { stand: "live" } });
|
||||
for (const rolle of ["admin", "gast", "gast2"]) {
|
||||
await roh(CREW, "/workspace/api/reaktion/dabei", { method: "POST", keks: k[rolle] });
|
||||
}
|
||||
|
||||
await roh(CREW, "/workspace/api/reaktion/chat",
|
||||
{ method: "POST", keks: k.gast2, rumpf: { text: "Ich gebe gleich was" } });
|
||||
await roh(CREW, "/workspace/api/reaktion/chat",
|
||||
{ method: "POST", keks: k.admin, rumpf: { text: "Ich nicht" } });
|
||||
|
||||
/* ==== DIE GEGENPROBE AN DERSELBEN PERSON ======================
|
||||
|
||||
Beim ersten Lauf stand hier „kein Beitrag traegt einen Glanz" --
|
||||
und das war rot, zu Recht: Abschnitt 16 legt fuer `gast` eine
|
||||
Spende an, und die glaenzte noch. Die Pruefung hat damit
|
||||
bewiesen, dass der Glanz funktioniert, und sich selbst dabei
|
||||
fuer kaputt erklaert.
|
||||
|
||||
Richtig ist eine Gegenprobe, die NUR von dem abhaengt, was
|
||||
dieser Abschnitt selbst tut: dieselbe Person vorher und
|
||||
nachher. */
|
||||
const ohne = await roh(CREW, "/workspace/api/reaktion/chat", { keks: k.gast2 });
|
||||
const vorSpende = (ohne.j?.beitraege || []).find((b) => b.person_id === IDS.gast2);
|
||||
ok(!!vorSpende && vorSpende.spende === null,
|
||||
"Gegenprobe: vor der Spende traegt sein Beitrag keinen Glanz");
|
||||
|
||||
/* Eine gezeigte Spende -- direkt in die Tabelle, weil hier der
|
||||
CHAT geprueft wird und nicht der Weg einer Spende. Die
|
||||
Stufenleiter steht ab Werk auf 5/15/45 Minuten. */
|
||||
db().prepare(`INSERT INTO spenden
|
||||
(betrag_cent, waehrung, name, gruss, person_id, herkunft, stand, erstellt, gezeigt_am)
|
||||
VALUES (?,?,?,?,?,'hand','bestaetigt',?,?)`)
|
||||
.run(2000, "EUR", "Lenny", "", IDS.gast2,
|
||||
new Date().toISOString(), new Date().toISOString());
|
||||
|
||||
const mit = await roh(CREW, "/workspace/api/reaktion/chat", { keks: k.gast2 });
|
||||
const meiner = (mit.j?.beitraege || []).find((b) => b.person_id === IDS.gast2);
|
||||
const fremder = (mit.j?.beitraege || []).find((b) => b.person_id === IDS.admin);
|
||||
ok(!!meiner?.spende, "wer gespendet hat, traegt den Glanz am Beitrag");
|
||||
ok(/^#[0-9a-fA-F]{6}$/.test(meiner?.spende?.farbe || ""),
|
||||
`mit einer echten Farbe (${meiner?.spende?.farbe})`);
|
||||
ok(!!meiner?.spende?.stufe, `und dem Namen der Stufe (${meiner?.spende?.stufe})`);
|
||||
ok(meiner?.spende?.noch_ms > 0,
|
||||
`und einer Restdauer statt einer Uhrzeit (${meiner?.spende?.noch_ms} ms)`);
|
||||
ok(fremder?.spende === null, "und der Nachbar daneben nicht");
|
||||
|
||||
/* ==== AUCH DER EINZELNE NEUE BEITRAG TRAEGT IHN ==============
|
||||
Es gibt zwei Ausgaenge (Liste beim Laden, Meldung beim
|
||||
Schreiben). Wer nur einen pflegt, hat den Glanz nach dem
|
||||
Neuladen -- und beim Schreiben nicht. */
|
||||
const neu = await roh(CREW, "/workspace/api/reaktion/chat",
|
||||
{ method: "POST", keks: k.gast2, rumpf: { text: "Noch einer" } });
|
||||
ok(!!neu.j?.spende,
|
||||
"auch ein frisch geschriebener Beitrag bringt ihn gleich mit");
|
||||
|
||||
/* ==== UND DER ROLLENSCHLUESSEL BLEIBT DRAUSSEN ===============
|
||||
Dieselbe Regel wie am 07.10.2026: Was der Saal nicht wissen
|
||||
muss, verlaesst das Haus nicht. Der Glanz traegt Farbe, Namen
|
||||
und Dauer -- keine Personennummer eines anderen, keinen Betrag
|
||||
in Geld. */
|
||||
ok(meiner?.spende?.dogen_mille > 0 && meiner?.spende?.cent === undefined,
|
||||
"er traegt Dogen und kein Geld");
|
||||
|
||||
await roh(CREW, "/workspace/api/reaktion/stand",
|
||||
{ method: "POST", keks: k.admin, rumpf: { stand: "zu" } });
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
19. WAS AUF DER SEITE STEHEN MUSS (08.10.2026)
|
||||
|
||||
Drei Umbauten dieses Tages haben Bedienteile, die es vorher nicht
|
||||
gab. Eine Funktion, die nur im Programm existiert und nicht im
|
||||
Dokument, ist nicht da -- und gemerkt wird es in einer Sendung.
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 19. Vorraum, Geraete, zweites Video ===");
|
||||
{
|
||||
const html = readFileSync(join(HIER, "..", "workspace", "reaktion.html"), "utf8");
|
||||
const js = readFileSync(join(HIER, "..", "workspace", "assets", "js", "reaktion.js"), "utf8");
|
||||
const css = readFileSync(
|
||||
join(HIER, "..", "workspace", "assets", "css", "reaktion.css"), "utf8");
|
||||
|
||||
/* ---- Der Vorraum ---- */
|
||||
ok(/id="vorraum"/.test(html), "der Vorraum steht in der Seite");
|
||||
/* ZWEI AUSGAENGE. Ein Vorhang, aus dem nur ein Weg herausfuehrt,
|
||||
ist keine Frage -- und er stuende ausgerechnet vor jemandem, der
|
||||
gerade nur zuschauen wollte. */
|
||||
ok(/id="vorraum-ja"/.test(html) && /id="vorraum-nein"/.test(html),
|
||||
"und er hat zwei Ausgaenge: dabei sein und lieber nicht");
|
||||
ok(/id="vorraum-kamera"/.test(html) && /id="vorraum-mikro"/.test(html),
|
||||
"Kamera und Mikro lassen sich VORHER waehlen");
|
||||
ok(/id="vorraum-video"/.test(html) && /muted/.test(html),
|
||||
"das eigene Bild laeuft darin -- stumm");
|
||||
ok(/vorraumZu\(true\)/.test(js),
|
||||
"und beim Ablehnen wird die Kamera wirklich angehalten");
|
||||
ok(/\.vorraum\b/.test(css), "der Vorraum hat eine Regel im Stilblatt");
|
||||
|
||||
/* ---- Die Geraete ---- */
|
||||
ok(/id="geraete"/.test(html), "das Register „Ton“ hat einen Geraeteteil");
|
||||
ok(/id="s-geraete"/.test(html) && /id="geraetefenster"/.test(html),
|
||||
"und auch Gaeste kommen daran -- ueber die Senderleiste");
|
||||
/* ==== DER EIGENTLICHE FUND VOM 08.10.2026 ====================
|
||||
Hier stand fest `echoCancellation: true, noiseSuppression: true`.
|
||||
Ueber einem Mischpult ist das falsch: Der Duo hat Gate,
|
||||
Kompressor und EQ schon gemacht, und die Echo-Unterdrueckung
|
||||
des Browsers duckt danach, sobald von der Gegenseite Ton kommt.
|
||||
Wenn diese Zeile je wieder fest dasteht, ist der Pultmodus
|
||||
wirkungslos -- und es faellt erst im Stream auf. */
|
||||
ok(!/audio:\s*\{\s*echoCancellation:\s*true/.test(js),
|
||||
"Gegenprobe: die Echo-Unterdrueckung steht nicht mehr fest auf an");
|
||||
ok(/echoCancellation:\s*!pult/.test(js),
|
||||
"sie haengt am Pultmodus");
|
||||
ok(/autoGainControl:\s*!pult/.test(js),
|
||||
"und die Lautstaerke-Automatik ebenso");
|
||||
ok(/OverconstrainedError/.test(js),
|
||||
"ein Geraet, das nicht mehr da ist, hat einen eigenen Ausgang");
|
||||
ok(/setSinkId/.test(js), "und der Ausgabeweg laesst sich waehlen");
|
||||
|
||||
/* ---- Das zweite Video ---- */
|
||||
ok(/id="zweitbild"/.test(html) && /id="zweit-titel"/.test(html),
|
||||
"das zweite Video wird gezeigt und nicht nur eingetippt");
|
||||
ok(/id="zweit-stelle"/.test(html),
|
||||
"samt der gemerkten Stelle -- sie ist der Grund fuer den zweiten Platz");
|
||||
ok(/id="p-zweit-tausch"/.test(html),
|
||||
"und es laesst sich von hier aus umschalten");
|
||||
ok(/dataset\.was = 'bereit'/.test(js),
|
||||
"aus der Warteschlange laesst sich eines bereitlegen");
|
||||
|
||||
/* ---- Die fertigen Saetze und der Platz ---- */
|
||||
ok(/id="geben-satzreihe"/.test(html),
|
||||
"die fertigen Saetze stehen im Spendenkasten");
|
||||
ok(/id="saetze"/.test(html) && /id="saetze-sichern"/.test(html),
|
||||
"und die Leitung pflegt sie im Register „Gestaltung“");
|
||||
ok(/id="platzwahl"/.test(html), "der Platz der Karte laesst sich stellen");
|
||||
|
||||
/* ---- Der Werkstoff ---- */
|
||||
ok(/--w-hoch:/.test(css) && /--w-tief:/.test(css),
|
||||
"die Seite hat eine Hoehensprache (was obenauf liegt, was vertieft ist)");
|
||||
ok(/:focus-visible/.test(css),
|
||||
"und wer mit der Tastatur bedient, wird gesehen");
|
||||
}
|
||||
|
||||
melde("");
|
||||
melde(`${geprueft} Pruefungen, ${fehler} Fehler`);
|
||||
melde(fehler === 0 ? "ALLES IN ORDNUNG" : "NICHT IN ORDNUNG");
|
||||
|
||||
Reference in New Issue
Block a user