Organize/.cdp/verify-theme.cjs

62 lines
4.6 KiB
JavaScript

/* Verify: open the Theme menu and click "Ocean", confirm the app switches theme. */
const { spawn } = require("node:child_process");
const fs = require("node:fs");
const http = require("node:http");
const EDGE = "C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe";
const PORT = 9351;
const BASE = "http://localhost:3210";
function httpJson(p) {
return new Promise((resolve, reject) => {
const req = http.request({ host: "127.0.0.1", port: PORT, path: p }, (res) => {
let d = ""; res.on("data", (c) => (d += c));
res.on("end", () => { try { resolve(JSON.parse(d)); } catch { resolve(d); } });
});
req.on("error", reject); req.end();
});
}
(async () => {
const profile = fs.mkdtempSync("/tmp/edge-prof-");
const proc = spawn(EDGE, ["--headless=new", `--remote-debugging-port=${PORT}`, `--user-data-dir=${profile}`, "--no-first-run", "--no-default-browser-check", "--disable-gpu", "about:blank"], { stdio: "ignore" });
let targets = null;
for (let i = 0; i < 40; i++) { await new Promise((r) => setTimeout(r, 500)); try { targets = await httpJson("/json/list"); break; } catch {} }
if (!targets) { console.error("no targets"); process.exit(1); }
const page = targets.find((t) => t.type === "page");
const ws = new WebSocket(page.webSocketDebuggerUrl);
await new Promise((r) => (ws.onopen = r));
let id = 0;
const send = (method, params = {}) => new Promise((resolve) => {
const myId = ++id; ws.send(JSON.stringify({ id: myId, method, params }));
const t = setInterval(() => { const m = ws._buf.find((b) => b.id === myId); if (m) { clearInterval(t); resolve(m.result ?? m.error); } }, 10);
});
ws._buf = [];
let crashed = false;
ws.onmessage = (m) => { const msg = JSON.parse(m.data); if (msg.id) { ws._buf.push(msg); return; }
if (msg.method === "Runtime.consoleAPICalled" && msg.params.type === "error") { const t2 = msg.params.args.map(a=>a.value??a.description??a.type).join(" "); if (/Base UI|MenuGroup|hydrat/i.test(t2)) crashed = true; }
if (msg.method === "Runtime.exceptionThrown") crashed = true; };
await send("Runtime.enable"); await send("Page.enable"); await send("Network.enable");
const cookieFile = process.argv[2];
const cookieValue = cookieFile ? (fs.readFileSync(cookieFile, "utf8").split("\n").map((l) => l.trim()).find((l) => l.includes("authjs.session-token")) || "").split("\t").pop() : null;
if (cookieValue) await send("Network.setCookies", { cookies: [{ name: "authjs.session-token", value: cookieValue, domain: "localhost", path: "/", httpOnly: true, sameSite: "Lax" }] });
await send("Emulation.setDeviceMetricsOverride", { width: 1440, height: 900, deviceScaleFactor: 1, mobile: false });
await send("Page.navigate", { url: BASE + "/" });
await new Promise((r) => setTimeout(r, 4500));
// Open the theme menu
const btn = (await send("Runtime.evaluate", { expression: `(() => { const b = Array.from(document.querySelectorAll("button")).find(x => (x.textContent||"").trim() === "Theme" || (x.getAttribute("aria-label")||"") === "Theme"); const r = b.getBoundingClientRect(); return JSON.stringify({ x: r.x + r.width / 2, y: r.y + r.height / 2 }); })()`, returnByValue: true })).result?.value;
const bp = JSON.parse(btn);
await send("Input.dispatchMouseEvent", { type: "mousePressed", x: bp.x, y: bp.y, button: "left", clickCount: 1 });
await send("Input.dispatchMouseEvent", { type: "mouseReleased", x: bp.x, y: bp.y, button: "left", clickCount: 1 });
await new Promise((r) => setTimeout(r, 700));
// Click "Ocean"
const oc = (await send("Runtime.evaluate", { expression: `(() => { const b = Array.from(document.querySelectorAll("button")).find(x => (x.textContent||"").trim() === "Ocean"); if(!b) return null; const r = b.getBoundingClientRect(); return JSON.stringify({ x: r.x + 12, y: r.y + r.height / 2 }); })()`, returnByValue: true })).result?.value;
if (oc) {
const op = JSON.parse(oc);
await send("Input.dispatchMouseEvent", { type: "mousePressed", x: op.x, y: op.y, button: "left", clickCount: 1 });
await send("Input.dispatchMouseEvent", { type: "mouseReleased", x: op.x, y: op.y, button: "left", clickCount: 1 });
}
await new Promise((r) => setTimeout(r, 1500));
const after = (await send("Runtime.evaluate", { expression: `JSON.stringify({ html: document.documentElement.className.split(" ").filter(Boolean).slice(-1), ls: localStorage.getItem("theme") })`, returnByValue: true })).result?.value;
console.log("OCEAN BTN FOUND:", !!oc, "| AFTER CLICK:", after, "| CRASHED (Base UI/hydration/exception):", crashed);
proc.kill(); process.exit(0);
})().catch((e) => { console.error("FAIL", e.message); process.exit(1); });