/* 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); });