Die Rabattcodes haengen am Konto, nicht mehr am Abo
Filipe: "die partner codes sollen auch schon fuer die leute sichtbar sein die angemeldet sind." Umgestellt, und auf Nachfrage dauerhaft: Rabattcodes sind ab jetzt ein Konto-Vorteil, kein Abo-Vorteil. WARUM DAS MEHR IST ALS EINE BEQUEMLICHKEIT Der alte Riegel verlangte einen Abo-Status. Nachgesehen, statt vermutet: Die Bezahlung auf abonnieren.html steht auf "Coming soon", bis Dogis PayPal-Business-Zugang da ist (der Kommentar dort nennt es beim Namen). Registrieren geht, bezahlen nicht. Der erste echte Partnercode lag damit seit gestern hinter einer Tuer, die sich gar nicht oeffnen laesst -- er waere fuer NIEMANDEN sichtbar gewesen ausser fuer Dogi und VanVan ueber die Rollenvorschau. Entschieden wird jetzt an "supporterToken" (beim Login gesetzt, beim Logout entfernt, supporter.js). Der Abo-Stand wird als Sicherheitsnetz weiter mitgelesen: Niemand soll Zugang verlieren, den er gestern hatte. BEIDE STELLEN, NICHT EINE Die Bedingung steht doppelt im Haus -- an der Kachel auf links.html und an der Seite selbst. Nur eine davon umzustellen erzeugt einen Fehler, den keine der beiden fuer sich zeigt: Man kaeme mit Konto auf die Seite und saehe dort die Sperre. Beide sind umgestellt, tragen den Hinweis aufeinander, und die Pruefung vergleicht sie in jedem Anmeldezustand gegeneinander. TEXTE, DIE SONST GELOGEN HAETTEN "Nur fuer Supporter" auf einer Seite, die ein kostenloses Konto oeffnet, schickt Leute zum Bezahlen fuer etwas, das sie umsonst bekommen. Kopf, Vorspann, Kachelband, Beschreibung und Sperrtext sagen jetzt "Konto", in allen fuenf Sprachen. Die Sperre bietet auf Filipes Wunsch beide Wege an: den kostenlosen zuerst, das Abo daneben -- mit einer Zeile darunter, dass es erst startet, wenn es offiziell live geht. Ohne die waere der zweite Knopf eine Falle. EIN FEHLER, DER SEIT DEM 03.08.2026 DRINSTAND Die Pruefung meldete auf der FREIGESCHALTETEN Kachel weiter "Nur mit Konto" statt "Freigeschaltet". Ursache: Das Skript setzte den Text (`badge.textContent = ...`), aber applyTranslations() schreibt aus dem data-i18n-Attribut zurueck -- und es laeuft danach noch einmal, weil dogiSiteTexteLaden() die Texte aus der Verwaltung holt und dann neu uebersetzt. NACHGEMESSEN STATT HERGELEITET, und die erste Erklaerung war zu schnell: Der Text war schon nach 50 ms falsch, also nicht "irgendwann spaeter ueberschrieben". Der Grund ist, dass TEAM_API_BASIS auf den ECHTEN Worker zeigt -- der Abruf gelingt selbst aus einer lokalen Testseite. Kontroll- versuch mit blockiertem Abruf: derselbe alte Code, und das Band bleibt korrekt. Ursache weg, Fehler weg. Behoben, indem der SCHLUESSEL getauscht wird statt des Textes. Damit schreibt jeder weitere Uebersetzungslauf von selbst das Richtige hin -- auch bei Sprachwechsel, wo die alte Fassung ebenfalls zurueckfiel. Aufgefallen ist es nie, weil bis gestern niemand in den freigeschalteten Zustand kommen konnte. NEBENBEFUND, NICHT ANGEFASST: index.html hat dieselbe Bauart beim Live-Status (#live-text mit data-i18n, Text per Skript gesetzt). Gemessen ist es dort ein Wettlauf zweier Abrufe -- in meinem Lauf gewann der Status um Haaresbreite, und ein Sprachwechsel repariert es dort ohnehin (dogi-sprache-geaendert). Kleiner, aber echt. Auf Ansage. pruef-rabattcodes EXIT=0 (63 Pruefungen, vorher 42). Neu darunter: drei Anmeldezustaende statt zweier -- ausgeloggt, angemeldet ohne Abo, angemeldet mit Abo --, jeder auf BEIDEN Seiten, dazu der Klick auf die Kachel (fuehrt sie wirklich weiter?), die Beschriftung des Bands und als Gegenprobe ein erzwungener Uebersetzungslauf, der den alten Fehler zuverlaessig ausloest. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+143
-34
@@ -1,12 +1,25 @@
|
||||
/* Prüft die Rabattcode-Seite mit dem ersten echten Partnercode (DOGI10).
|
||||
/* Prüft die Rabattcodes mit dem ersten echten Partnercode (DOGI10) —
|
||||
auf BEIDEN Seiten, auf denen sie vorkommen.
|
||||
|
||||
WARUM ZWEI SEITEN IN EINER PRÜFUNG
|
||||
|
||||
Die Bedingung "wer darf die Codes sehen" steht doppelt im Haus: in
|
||||
links.html an der Kachel und in rabattcodes.html an der Seite selbst.
|
||||
Ändert jemand nur eine davon, entsteht ein Fehler, den keine der
|
||||
beiden für sich zeigt — man kommt mit Konto zwar auf die Seite und
|
||||
sieht dort die Sperre, oder die Kachel blockiert jemanden, der die
|
||||
Codes sehen dürfte. Geprüft wird deshalb beides gegeneinander.
|
||||
|
||||
WAS HIER SCHIEFGEHEN KANN, UND WARUM MAN ES NICHT SIEHT
|
||||
|
||||
Die Seite hat zwei Zustände, und man bekommt immer nur einen davon zu
|
||||
sehen: Ohne Supporter-Abo die Sperre, mit Abo die Codekarte. Wer im
|
||||
Browser nachschaut, prüft also die Hälfte. Beide Richtungen stehen
|
||||
deshalb hier drin — auch die Gegenprobe, dass die Karte OHNE Abo
|
||||
wirklich verschwunden ist und nicht bloss weiter unten steht.
|
||||
Man bekommt immer nur einen Zustand zu sehen. Wer im Browser
|
||||
nachschaut, prüft also ein Drittel. Alle drei stehen hier drin:
|
||||
ausgeloggt, angemeldet ohne Abo, angemeldet mit Abo. Der mittlere ist
|
||||
der, um den es seit dem 08.09.2026 geht (Filipe: "die partner codes
|
||||
sollen auch schon für die leute sichtbar sein die angemeldet sind") —
|
||||
ohne ihn prüfte man weiter nur die zwei, die schon vorher gingen.
|
||||
Dazu jeweils der KLICK: Eine Kachel, die richtig aussieht und
|
||||
trotzdem nicht weiterführt, fällt sonst niemandem auf.
|
||||
|
||||
Dazu vier Dinge, die im Bild richtig aussehen und trotzdem falsch
|
||||
sein können:
|
||||
@@ -62,7 +75,7 @@ const srv = createServer((q, r) => {
|
||||
r.writeHead(404).end("");
|
||||
});
|
||||
await new Promise((r) => srv.listen(4331, r));
|
||||
const URL = "http://127.0.0.1:4331/rabattcodes.html";
|
||||
const BASIS = "http://127.0.0.1:4331";
|
||||
|
||||
/* Erwartungswerte. Sie stammen aus dem Shop, nicht aus dem Codenamen:
|
||||
/home/vandiy/vans-diy-bastelbedarf/src/content/partnercodes.json
|
||||
@@ -83,10 +96,28 @@ function pruefe(name, ok, zusatz) {
|
||||
|
||||
const browser = await chromium.launch();
|
||||
|
||||
/* Eine Seite im gewünschten Zustand öffnen. `abo` schaltet die Sperre,
|
||||
`zeit` stellt die Uhr (nur Date, die Zeitgeber laufen weiter — sonst
|
||||
stünde main.js still). */
|
||||
async function seiteOeffnen({ abo = true, zeit = null, sprache = "de", viewport, reducedMotion } = {}) {
|
||||
/* DIE DREI ANMELDEZUSTAENDE, die im Browser wirklich vorkommen.
|
||||
|
||||
supporter.js legt beim Login "supporterToken" an und stellt den
|
||||
Abo-Stand als "supporterSubStatus" daneben -- "none", wenn ein Konto
|
||||
besteht, aber nichts bezahlt ist (supporter.html, /supporter/me).
|
||||
Beim Logout verschwinden beide zusammen (clearToken).
|
||||
|
||||
Der MITTLERE Zustand ist der, um den es seit dem 08.09.2026 geht: Wer
|
||||
nur ein Konto hat, muss die Codes sehen. Ohne ihn pruefte man weiter
|
||||
nur die zwei Zustaende, die auch vorher schon funktioniert haben. */
|
||||
const ZUSTAENDE = [
|
||||
{ schluessel: "aus", name: "ausgeloggt", token: null, status: null, darfSehen: false },
|
||||
{ schluessel: "konto", name: "angemeldet, kein Abo", token: "pruef-tok", status: "none", darfSehen: true },
|
||||
{ schluessel: "abo", name: "angemeldet mit Abo", token: "pruef-tok", status: "active", darfSehen: true },
|
||||
];
|
||||
const zustandVon = (k) => ZUSTAENDE.find((z) => z.schluessel === k);
|
||||
|
||||
/* Eine Seite im gewünschten Zustand öffnen. `zeit` stellt die Uhr (nur
|
||||
Date, die Zeitgeber laufen weiter — sonst stünde main.js still). */
|
||||
async function seiteOeffnen({ zustand = "konto", seite = "/rabattcodes.html",
|
||||
zeit = null, sprache = "de", viewport, reducedMotion } = {}) {
|
||||
const z = zustandVon(zustand);
|
||||
const ctx = await browser.newContext({
|
||||
viewport: viewport || { width: 1280, height: 1000 },
|
||||
permissions: ["clipboard-read", "clipboard-write"],
|
||||
@@ -94,34 +125,112 @@ async function seiteOeffnen({ abo = true, zeit = null, sprache = "de", viewport,
|
||||
});
|
||||
const p = await ctx.newPage();
|
||||
if (zeit) await p.clock.setFixedTime(zeit);
|
||||
await p.addInitScript(([a, s]) => {
|
||||
if (a) localStorage.setItem("supporterSubStatus", "active");
|
||||
await p.addInitScript(([token, status, spr]) => {
|
||||
if (token) localStorage.setItem("supporterToken", token);
|
||||
else localStorage.removeItem("supporterToken");
|
||||
if (status) localStorage.setItem("supporterSubStatus", status);
|
||||
else localStorage.removeItem("supporterSubStatus");
|
||||
localStorage.setItem("dogi-sprache", s);
|
||||
}, [abo, sprache]);
|
||||
await p.goto(URL, { waitUntil: "domcontentloaded" });
|
||||
localStorage.setItem("dogi-sprache", spr);
|
||||
}, [z.token, z.status, sprache]);
|
||||
await p.goto(BASIS + seite, { waitUntil: "domcontentloaded" });
|
||||
await p.waitForTimeout(700);
|
||||
return { ctx, p };
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
1 — Die Sperre, in BEIDE Richtungen
|
||||
1 — Wer die Codes sieht: drei Zustaende, ZWEI Seiten
|
||||
|
||||
Die Bedingung steht doppelt im Haus (links.html und rabattcodes.html)
|
||||
und muss an beiden Stellen gleich lauten. Laeuft nur eine davon auf,
|
||||
entsteht ein Fehler, den keine der beiden Seiten fuer sich zeigt: Man
|
||||
kommt mit Konto zwar auf die Seite, sieht dort aber die Sperre -- oder
|
||||
die Kachel blockiert jemanden, der die Codes sehen duerfte. Deshalb
|
||||
wird hier nicht nur jede Seite geprueft, sondern auch, dass beide
|
||||
dasselbe sagen.
|
||||
=================================================================== */
|
||||
console.log("\n=== Supporter-Sperre ===");
|
||||
for (const [abo, sichtbar] of [[true, "Karte"], [false, "Sperre"]]) {
|
||||
const { ctx, p } = await seiteOeffnen({ abo });
|
||||
const z = await p.evaluate(() => ({
|
||||
console.log("\n=== Wer die Codes sieht (drei Anmeldezustände, beide Seiten) ===");
|
||||
for (const z of ZUSTAENDE) {
|
||||
const soll = z.darfSehen;
|
||||
|
||||
const a = await seiteOeffnen({ zustand: z.schluessel });
|
||||
const r = await a.p.evaluate(() => ({
|
||||
karte: !document.getElementById("rc-inhalt").hidden,
|
||||
sperre: !document.getElementById("rc-gesperrt").hidden,
|
||||
codeSichtbar: !!document.getElementById("rc-code")?.checkVisibility?.(),
|
||||
}));
|
||||
const lage = abo ? "Mit Abo" : "Ohne Abo";
|
||||
pruefe(`${lage}: ${sichtbar} steht da`,
|
||||
z.karte === abo && z.sperre === !abo, `Karte=${z.karte} Sperre=${z.sperre}`);
|
||||
/* Gegenprobe: "hidden" ist ein Attribut, kein Beweis. Ohne Abo darf
|
||||
der Code auch tatsächlich nicht auf dem Schirm sein. */
|
||||
pruefe(`${lage}: Code ist ${abo ? "sichtbar" : "unsichtbar"}`,
|
||||
z.codeSichtbar === abo, `sichtbar=${z.codeSichtbar}`);
|
||||
pruefe(`[${z.name}] rabattcodes.html zeigt ${soll ? "die Codes" : "die Sperre"}`,
|
||||
r.karte === soll && r.sperre === !soll, `Karte=${r.karte} Sperre=${r.sperre}`);
|
||||
/* Gegenprobe: "hidden" ist ein Attribut, kein Beweis. */
|
||||
pruefe(`[${z.name}] rabattcodes.html: Code ist ${soll ? "sichtbar" : "unsichtbar"}`,
|
||||
r.codeSichtbar === soll, `sichtbar=${r.codeSichtbar}`);
|
||||
await a.ctx.close();
|
||||
|
||||
const b = await seiteOeffnen({ zustand: z.schluessel, seite: "/links.html" });
|
||||
const l = await b.p.evaluate(() => ({
|
||||
gesperrt: document.getElementById("card-rabattcodes").classList.contains("gesperrt"),
|
||||
band: document.getElementById("rabatt-badge")?.textContent.trim(),
|
||||
}));
|
||||
pruefe(`[${z.name}] links.html: Kachel ist ${soll ? "offen" : "gesperrt"}`,
|
||||
l.gesperrt === !soll, `gesperrt=${l.gesperrt}, Band „${l.band}“`);
|
||||
/* Das BAND muss zur Kachel passen. Ein Schloss auf einer offenen Kachel
|
||||
sagt dem Angemeldeten das Gegenteil von dem, was gilt -- und genau das
|
||||
stand dort: Der Text wurde per Skript gesetzt, aber applyTranslations()
|
||||
schrieb ihn aus dem data-i18n-Attribut wieder zurueck. */
|
||||
pruefe(`[${z.name}] links.html: Band sagt „${soll ? "✓ Freigeschaltet" : "🔒 Nur mit Konto"}“`,
|
||||
l.band === (soll ? "✓ Freigeschaltet" : "🔒 Nur mit Konto"), l.band);
|
||||
/* GEGENPROBE GEGEN GENAU DEN RUECKFALL: eine erneute Uebersetzung.
|
||||
Der Sprachwechsel loest applyTranslations() aus, ohne Netz und ohne
|
||||
Warten -- mit der alten Fassung stand danach wieder das Schloss da.
|
||||
Eine Pruefung, die den Fehler nicht ausloesen kann, haette ihn auch
|
||||
nicht gefunden. */
|
||||
const nachWechsel = await b.p.evaluate(() => {
|
||||
document.querySelector(".sprach-schalter [data-sprache='en']").click();
|
||||
return document.getElementById("rabatt-badge").textContent.trim();
|
||||
});
|
||||
pruefe(`[${z.name}] links.html: Band überlebt eine erneute Übersetzung`,
|
||||
nachWechsel === (soll ? "✓ Unlocked" : "🔒 Account required"), nachWechsel);
|
||||
pruefe(`[${z.name}] beide Seiten sind sich einig`,
|
||||
l.gesperrt === !r.karte, `links gesperrt=${l.gesperrt}, rabattcodes offen=${r.karte}`);
|
||||
|
||||
/* Und was passiert beim KLICK? Das ist die Frage, um die es geht --
|
||||
eine Kachel, die richtig aussieht und trotzdem nicht weiterfuehrt,
|
||||
haetten die Zeilen oben nicht bemerkt. */
|
||||
await b.p.click("#card-rabattcodes");
|
||||
await b.p.waitForTimeout(700);
|
||||
const nach = b.p.url();
|
||||
if (soll) {
|
||||
pruefe(`[${z.name}] Klick auf die Kachel führt zu den Codes`,
|
||||
nach.endsWith("/rabattcodes.html"), nach);
|
||||
} else {
|
||||
const hinweisDa = await b.p.evaluate(() =>
|
||||
!document.getElementById("rabatt-gesperrt-hinweis").hidden);
|
||||
pruefe(`[${z.name}] Klick führt nicht weiter, sondern zeigt den Hinweis`,
|
||||
nach.endsWith("/links.html") && hinweisDa, `${nach}, Hinweis=${hinweisDa}`);
|
||||
}
|
||||
await b.ctx.close();
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
1b — Die Sperre bietet BEIDE Wege an (Wunsch 08.09.2026)
|
||||
|
||||
Der alte Knopf schickte Ausgeloggte allein aufs Abo. Das ist noch gar
|
||||
nicht bezahlbar ("Coming soon" auf abonnieren.html), und fuer die
|
||||
Codes reicht ein kostenloses Konto -- der Weg dorthin muss deshalb
|
||||
der erste sein.
|
||||
=================================================================== */
|
||||
console.log("\n=== Zwei Wege für Ausgeloggte ===");
|
||||
for (const [seite, wahl] of [["/rabattcodes.html", "#rc-gesperrt .btn"],
|
||||
["/links.html", "#rabatt-gesperrt-hinweis .btn"]]) {
|
||||
const { ctx, p } = await seiteOeffnen({ zustand: "aus", seite });
|
||||
if (seite.endsWith("links.html")) await p.click("#card-rabattcodes");
|
||||
await p.waitForTimeout(400);
|
||||
const knoepfe = await p.$$eval(wahl, (els) =>
|
||||
els.map((e) => ({ text: e.textContent.trim(), href: e.getAttribute("href") })));
|
||||
const zumKonto = knoepfe.filter((k) => k.href === "abonnieren.html");
|
||||
pruefe(`${seite}: Weg zum Konto UND zum Abo vorhanden`, zumKonto.length === 2,
|
||||
knoepfe.map((k) => `${k.text} -> ${k.href}`).join(" | "));
|
||||
pruefe(`${seite}: der erste Weg ist der kostenlose`,
|
||||
/registrier|anmeld/i.test(zumKonto[0] ? zumKonto[0].text : ""), zumKonto[0] && zumKonto[0].text);
|
||||
await ctx.close();
|
||||
}
|
||||
|
||||
@@ -130,7 +239,7 @@ for (const [abo, sichtbar] of [[true, "Karte"], [false, "Sperre"]]) {
|
||||
=================================================================== */
|
||||
console.log("\n=== Partnersiegel ===");
|
||||
{
|
||||
const { ctx, p } = await seiteOeffnen({});
|
||||
const { ctx, p } = await seiteOeffnen();
|
||||
await p.waitForFunction(() => {
|
||||
const i = document.querySelector(".rc-medaillon img");
|
||||
return i && i.complete && i.naturalWidth > 0;
|
||||
@@ -176,7 +285,7 @@ console.log("\n=== Partnersiegel ===");
|
||||
=================================================================== */
|
||||
console.log("\n=== Code und Konditionen ===");
|
||||
{
|
||||
const { ctx, p } = await seiteOeffnen({});
|
||||
const { ctx, p } = await seiteOeffnen();
|
||||
const z = await p.evaluate(() => {
|
||||
const knopf = document.getElementById("rc-code");
|
||||
const link = document.querySelector('.rc-partner a.btn-primary');
|
||||
@@ -216,7 +325,7 @@ console.log("\n=== Code und Konditionen ===");
|
||||
=================================================================== */
|
||||
console.log("\n=== Kopieren ===");
|
||||
{
|
||||
const { ctx, p } = await seiteOeffnen({});
|
||||
const { ctx, p } = await seiteOeffnen();
|
||||
/* Vorher: die Erfolgsmeldung darf NICHT stehen. Ohne diese Zeile
|
||||
würde eine dauerhaft sichtbare Meldung als Erfolg durchgehen. */
|
||||
const vorher = await p.evaluate(() => !document.getElementById("rc-echo-ok").hidden);
|
||||
@@ -321,13 +430,13 @@ console.log("\n=== Handy und Bewegungsarmut ===");
|
||||
await ctx.close();
|
||||
}
|
||||
{
|
||||
const { ctx, p } = await seiteOeffnen({});
|
||||
const { ctx, p } = await seiteOeffnen();
|
||||
await p.waitForTimeout(500);
|
||||
await p.locator("#rc-partner").screenshot({ path: "pruef-rabattcodes-karte.png" });
|
||||
await ctx.close();
|
||||
}
|
||||
{
|
||||
const { ctx, p } = await seiteOeffnen({ abo: false });
|
||||
const { ctx, p } = await seiteOeffnen({ zustand: "aus" });
|
||||
await p.screenshot({ path: "pruef-rabattcodes-gesperrt.png", fullPage: true });
|
||||
await ctx.close();
|
||||
}
|
||||
@@ -342,7 +451,7 @@ srv.close();
|
||||
sondern ein stiller Aussetzer (Erkenntnis 28.08.2026). Die Zahl unten
|
||||
wird beim Erweitern mit angehoben.
|
||||
=================================================================== */
|
||||
const MINDESTENS = 34;
|
||||
const MINDESTENS = 60;
|
||||
const fehl = ergebnisse.filter((e) => !e.ok);
|
||||
console.log(`\n${ergebnisse.length} Prüfungen, ${fehl.length} fehlgeschlagen.`);
|
||||
console.log("Bildschirmfotos: pruef-rabattcodes-karte.png, -handy.png, -gesperrt.png");
|
||||
|
||||
Reference in New Issue
Block a user