Files
dogfather-universe/pruef-musik.mjs
T
DogFatherGitandClaude Opus 5 1d99da8ef8 Musik-Knopf repariert: Inhaltsrichtlinie blockierte die Spotify-Player
Gemeldet am 27.08.2026 ("unten rechts laeuft was nicht richtig mit der
Musik"). Ursache lag nicht in der Seite, sondern in der am 26.08.2026
eingefuehrten Inhaltsrichtlinie: "frame-src 'none'" verbietet dem
Dokument jede Einbettung -- und die beiden Player (Hasidog, Van-Van)
sind genau das. Der Knopf reagierte, das Feld ging auf, die Player
blieben leer. Kein Absturz, keine Fehlermeldung auf der Seite; die
Begruendung stand nur in der Browser-Konsole.

- frame-src erlaubt jetzt genau eine Quelle: https://open.spotify.com.
  Kein Sternchen, kein 'unsafe-*'. Was im Spotify-Rahmen passiert,
  regelt Spotifys eigene Richtlinie.
- Neuer Test pruef-musik.mjs (17 Pruefungen). Er startet bewusst den
  ECHTEN server/index.js statt eines Datei-Servers -- ein einfacher
  Datei-Server erzeugt die Kopfzeile gar nicht und haette den Fehler
  nie gesehen. Geprueft wird im echten Browser, ob die Rahmen wirklich
  von open.spotify.com laden (statt einer Fehlerseite), dazu Tippziel,
  Position, Schliessen per Knopf und Escape, Handy-Layout.

Beim Nachsehen aufgefallen und NOCH OFFEN: PayPal-Kasse und
Google-Anmeldung auf abonnieren.html laden ihre Skripte und Rahmen
ebenfalls von fremden Adressen. Beide sind derzeit nicht scharf
(googleClientId/paypalClientId sind null), wuerden aber mit derselben
Richtlinie an derselben Stelle still ausfallen, sobald Dogi seine
Zugangsdaten eintraegt. Wird gesondert mit ihm besprochen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 13:52:36 +02:00

155 lines
7.8 KiB
JavaScript

/* =====================================================================
Musik-Knopf unten rechts — läuft gegen den ECHTEN Server
WARUM NICHT GEGEN EINEN SIMPLEN DATEI-SERVER
Der Fehler, um den es hier geht, stand in keiner HTML-Datei und in
keiner Zeile JavaScript: Die Seite war fehlerfrei, aber der Server
schickte "frame-src 'none'" mit — und der Browser warf die beiden
Spotify-Player still weg (gemeldet 27.08.2026). Ein Test, der die
Dateien nur ausliefert, hätte das nie gesehen, weil er die Kopfzeile
gar nicht erst erzeugt.
Deshalb startet dieser Test server/index.js selbst auf einem eigenen
Port und prüft im echten Browser, ob die Player wirklich ankommen.
Geprüft wird:
1. Der Knopf ist da und groß genug zum Antippen.
2. Klick klappt das Feld auf, beide Player werden nachgeladen.
3. Die Rahmen laden WIRKLICH von open.spotify.com (nicht Fehlerseite).
4. Kein einziger Verstoß gegen die Inhaltsrichtlinie in der Konsole.
5. Die Richtlinie bleibt eng: nur open.spotify.com, kein Sternchen.
6. Schließen über ✕ und über Escape.
7. Am Handy passt das Feld in den Bildschirm.
===================================================================== */
import { chromium } from "playwright";
import { spawn } from "node:child_process";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const HIER = dirname(fileURLToPath(import.meta.url));
const PORT = 4433;
const BASIS = `http://127.0.0.1:${PORT}`;
let ok = 0, fehl = 0;
const funde = [];
const pruefe = (name, gut, info) => {
if (gut) { ok++; console.log(" ok " + name + (info ? " -> " + info : "")); }
else { fehl++; console.log(" FEHL " + name + (info ? " -> " + info : "")); funde.push(name + " — " + info); }
};
const server = spawn(process.execPath, ["index.js"], {
cwd: join(HIER, "server"),
env: { ...process.env, PORT: String(PORT) },
stdio: ["ignore", "pipe", "pipe"],
});
async function warteAufServer() {
for (let i = 0; i < 40; i++) {
try {
const res = await fetch(BASIS + "/index.html");
if (res.ok) return res;
} catch { /* noch nicht oben */ }
await new Promise((r) => setTimeout(r, 250));
}
throw new Error("Server ist nicht hochgekommen");
}
(async () => {
const antwort = await warteAufServer();
const richtlinie = antwort.headers.get("content-security-policy") || "";
console.log("\n== Inhaltsrichtlinie ==");
const frameRegel = (richtlinie.split(";").find((t) => t.trim().startsWith("frame-src")) || "").trim();
pruefe("frame-src erlaubt Spotify", /https:\/\/open\.spotify\.com/.test(frameRegel), frameRegel || "(keine frame-src-Regel)");
pruefe("frame-src bleibt eng (kein *)", !/[\s:]\*/.test(frameRegel) && !/'unsafe/.test(frameRegel), frameRegel);
const browser = await chromium.launch();
/* ---------- Computer ---------- */
console.log("\n== Computer ==");
const seite = await browser.newPage({ viewport: { width: 1366, height: 800 } });
const verstoesse = new Set();
seite.on("console", (m) => {
const text = m.text();
if (/Content Security Policy|violates the following/i.test(text)) verstoesse.add(text.replace(/\s+/g, " ").slice(0, 160));
});
await seite.goto(BASIS + "/index.html", { waitUntil: "domcontentloaded" });
await seite.waitForSelector("#music-toggle", { timeout: 10000 });
const knopf = await seite.evaluate(() => {
const r = document.getElementById("music-toggle").getBoundingClientRect();
return { breite: Math.round(r.width), hoehe: Math.round(r.height), rechts: Math.round(innerWidth - r.right), unten: Math.round(innerHeight - r.bottom) };
});
pruefe("Knopf ist groß genug zum Antippen", knopf.breite >= 44 && knopf.hoehe >= 44, knopf.breite + "x" + knopf.hoehe + "px");
pruefe("Knopf sitzt unten rechts", knopf.rechts >= 0 && knopf.rechts < 80 && knopf.unten >= 0 && knopf.unten < 80, "Abstand rechts " + knopf.rechts + "px, unten " + knopf.unten + "px");
pruefe("Feld ist zu Beginn zu", await seite.evaluate(() => document.getElementById("music-panel").hidden), "");
await seite.click("#music-toggle");
await seite.waitForTimeout(500);
const nachKlick = await seite.evaluate(() => {
const p = document.getElementById("music-panel");
return {
offen: !p.hidden,
quellen: [...p.querySelectorAll("iframe")].map((f) => f.getAttribute("src") || ""),
nochUngeladen: p.querySelectorAll("iframe[data-src]").length,
};
});
pruefe("Feld klappt auf", nachKlick.offen, "");
pruefe("Beide Player werden nachgeladen", nachKlick.quellen.length === 2 && nachKlick.quellen.every((q) => q.startsWith("https://open.spotify.com/embed/artist/")), nachKlick.quellen.length + " Player");
pruefe("Kein Player bleibt ungeladen", nachKlick.nochUngeladen === 0, nachKlick.nochUngeladen + " ohne src");
/* Der eigentliche Punkt: Kommt der Rahmen wirklich an? Wird er von der
Richtlinie geblockt, steht in seiner Adresse chrome-error://. */
await seite.waitForTimeout(6000);
const rahmen = seite.frames().map((f) => f.url());
const spotifyRahmen = rahmen.filter((u) => u.startsWith("https://open.spotify.com/"));
const kaputteRahmen = rahmen.filter((u) => u.startsWith("chrome-error://"));
pruefe("Beide Spotify-Rahmen sind wirklich geladen", spotifyRahmen.length === 2, spotifyRahmen.length + " geladen: " + spotifyRahmen.map((u) => u.slice(38, 60)).join(", "));
pruefe("Kein Rahmen ist eine Fehlerseite", kaputteRahmen.length === 0, kaputteRahmen.length + " Fehlerseiten");
pruefe("Kein Verstoß gegen die Inhaltsrichtlinie", verstoesse.size === 0, [...verstoesse].join(" | ") || "keine");
await seite.screenshot({ path: "bild-musik-computer.png" });
/* ---------- Schließen ---------- */
console.log("\n== Schließen ==");
await seite.click("#music-close");
await seite.waitForTimeout(300);
pruefe("✕ schließt das Feld", await seite.evaluate(() => document.getElementById("music-panel").hidden), "");
await seite.click("#music-toggle");
await seite.waitForTimeout(300);
await seite.keyboard.press("Escape");
await seite.waitForTimeout(300);
pruefe("Escape schließt das Feld", await seite.evaluate(() => document.getElementById("music-panel").hidden), "");
/* ---------- Handy ---------- */
console.log("\n== Handy (390x844) ==");
const handy = await browser.newPage({ viewport: { width: 390, height: 844 } });
await handy.goto(BASIS + "/index.html", { waitUntil: "domcontentloaded" });
await handy.waitForSelector("#music-toggle", { timeout: 10000 });
await handy.click("#music-toggle");
await handy.waitForTimeout(4000);
const h = await handy.evaluate(() => {
const r = document.getElementById("music-panel").getBoundingClientRect();
return { links: Math.round(r.left), rechts: Math.round(innerWidth - r.right), oben: Math.round(r.top), unten: Math.round(r.bottom),
fensterHoehe: innerHeight, ueberlauf: document.documentElement.scrollWidth - document.documentElement.clientWidth };
});
pruefe("Feld passt seitlich in den Bildschirm", h.links >= 0 && h.rechts >= 0, "links " + h.links + "px, rechts " + h.rechts + "px");
pruefe("Feld ragt nicht oben heraus", h.oben >= 0, "oben " + h.oben + "px");
pruefe("Seite läuft nicht seitlich über", h.ueberlauf <= 1, h.ueberlauf + "px");
const handyRahmen = handy.frames().filter((f) => f.url().startsWith("https://open.spotify.com/")).length;
pruefe("Am Handy laden die Player auch", handyRahmen === 2, handyRahmen + " geladen");
await handy.screenshot({ path: "bild-musik-handy.png" });
await browser.close();
server.kill();
console.log("\n===== " + ok + " ok, " + fehl + " Fehler =====");
if (funde.length) { console.log("\nOffene Punkte:"); funde.forEach((f) => console.log(" - " + f)); }
process.exit(fehl ? 1 : 0);
})().catch((e) => {
console.error("Testlauf abgebrochen:", e.message);
server.kill();
process.exit(1);
});