100 lines
6.4 KiB
JavaScript
100 lines
6.4 KiB
JavaScript
/* Reproduce the user's problem: full console, error overlay, and clicking the Theme toggle. */
|
|
const { spawn } = require("node:child_process");
|
|
const fs = require("node:fs");
|
|
const http = require("node:http");
|
|
const path = require("node:path");
|
|
const EDGE = "C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe";
|
|
const PORT = 9350;
|
|
const BASE = "http://localhost:3210";
|
|
const OUT = path.join(__dirname, "..", "shots");
|
|
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 = [];
|
|
const full = (a) => (a.value ?? a.description ?? a.unserializableValue ?? a.type ?? "").toString();
|
|
ws.onmessage = (m) => {
|
|
const msg = JSON.parse(m.data); if (msg.id) { ws._buf.push(msg); return; }
|
|
if (msg.method === "Runtime.consoleAPICalled") console.log(`[console:${msg.params.type}]`, msg.params.args.map(full).join(" "));
|
|
if (msg.method === "Runtime.exceptionThrown") console.log("[EXCEPTION]", JSON.stringify(msg.params.exceptionDetails.exception?.description ?? msg.params.exceptionDetails.text));
|
|
if (msg.method === "Log.entryAdded" && msg.params.entry.level === "error") console.log("[log:error]", msg.params.entry.text);
|
|
};
|
|
|
|
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));
|
|
|
|
const state = await send("Runtime.evaluate", { expression: `(() => ({
|
|
title: document.title,
|
|
bodyLen: document.body.innerHTML.length,
|
|
hasNextErrorOverlay: !!document.querySelector('nextjs-portal, [data-nextjs-dialog], iframe[title="error"]'),
|
|
themeBtn: !!Array.from(document.querySelectorAll('button')).find(b => /theme/i.test(b.textContent||'') || b.getAttribute('aria-label') && /theme/i.test(b.getAttribute('aria-label'))),
|
|
}))()`, returnByValue: true });
|
|
console.log("STATE", JSON.stringify(state.result.value));
|
|
|
|
// Find the theme trigger in the sidebar and click it.
|
|
const clickRes = await send("Runtime.evaluate", { expression: `(() => {
|
|
const btns = Array.from(document.querySelectorAll('button'));
|
|
const t = btns.find(b => /theme/i.test(b.textContent||'') || (b.getAttribute('aria-label')||'').match(/theme/i));
|
|
if (!t) return 'no-theme-button';
|
|
const r = t.getBoundingClientRect();
|
|
return JSON.stringify({ label: (t.textContent||t.getAttribute('aria-label')).trim(), x: r.x + r.width/2, y: r.y + r.height/2 });
|
|
})()`, returnByValue: true });
|
|
console.log("THEME BTN", clickRes.result?.value);
|
|
|
|
if (clickRes.result?.value && clickRes.result.value !== "no-theme-button") {
|
|
const p = JSON.parse(clickRes.result.value);
|
|
await send("Input.dispatchMouseEvent", { type: "mousePressed", x: p.x, y: p.y, button: "left", clickCount: 1 });
|
|
await send("Input.dispatchMouseEvent", { type: "mouseReleased", x: p.x, y: p.y, button: "left", clickCount: 1 });
|
|
await new Promise((r) => setTimeout(r, 800));
|
|
const menu = await send("Runtime.evaluate", { expression: `(() => {
|
|
const opts = Array.from(document.querySelectorAll('button, [role="radio"], [role="option"]')).map(e => (e.textContent||'').trim()).filter(t => /^(Default|Sunset|Dark|Ocean|System)$/i.test(t));
|
|
return JSON.stringify(opts);
|
|
})()`, returnByValue: true });
|
|
|
|
console.log("MENU OPTIONS", menu.result?.value);
|
|
// click the "Ocean" option and verify the theme actually switches
|
|
const oceanPos = 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 + 10, y: r.y + r.height/2 });
|
|
})()`, returnByValue: true });
|
|
if (oceanPos.result?.value) {
|
|
const pos = JSON.parse(oceanPos.result.value);
|
|
await send("Input.dispatchMouseEvent", { type: "mousePressed", x: pos.x, y: pos.y, button: "left", clickCount: 1 });
|
|
await send("Input.dispatchMouseEvent", { type: "mouseReleased", x: pos.x, y: pos.y, button: "left", clickCount: 1 });
|
|
await new Promise((r) => setTimeout(r, 1200));
|
|
const after = await send("Runtime.evaluate", { expression: `JSON.stringify({
|
|
html: document.documentElement.className.split(" ").filter(Boolean).slice(-1),
|
|
cardBg: (() => { const c = document.querySelector("[aria-label^=\"Drag to move\"]")?.closest("[class*=\\\"rounded-2xl\\\"]"); return c ? c.getAttribute("style")?.match(/background-color: ([^;]+)/)?.[1] : null; })(),
|
|
ls: localStorage.getItem("theme"),
|
|
})`, returnByValue: true });
|
|
console.log("AFTER-CLICK", after.result?.value);
|
|
}
|
|
}
|
|
proc.kill(); process.exit(0);
|
|
})().catch((e) => { console.error("FAIL", e.message); process.exit(1); });
|