// 用 CDP 截图 / 取状态(真实时间等待,见 cdp.mjs 顶部注释)。 // 用法:node tools/shot.mjs [waitMs] [options] // --wait "" 就绪判据,默认为"骨骼已加载或已报错" // --eval "" 截图前执行的表达式(结果会打印出来) // --settle 就绪后额外等待,确保至少画过一帧(默认 500) // --dom 不截图,打印 document.title 里的测试状态 // --extra "" 追加 Edge 旗标 import { writeFile } from "node:fs/promises"; import { resolve } from "node:path"; import { withPage, sleep } from "./cdp.mjs"; const argv = process.argv.slice(2); const [out, url, w, h, waitMs = "30000"] = argv; if (!out || !url || !w || !h) { console.error('usage: node tools/shot.mjs [waitMs] [--wait js] [--eval js] [--settle ms] [--dom]'); process.exit(2); } const opt = (name, dflt) => { const i = argv.indexOf(`--${name}`); return i >= 0 ? argv[i + 1] : dflt; }; const DEFAULT_WAIT = '(window.__test ? !!(window.__test.successAt || window.__test.error) : document.readyState === "complete")'; const waitFor = opt("wait", DEFAULT_WAIT); const settle = Number(opt("settle", "500")); const domMode = argv.includes("--dom"); const extraArgs = []; if (opt("extra")) extraArgs.push(opt("extra")); const result = await withPage({ width: Number(w), height: Number(h), extraArgs }, async ({ send, evaluate, consoleLines, exceptions }) => { await send("Page.navigate", { url }); const deadline = Date.now() + Number(waitMs); let ready = false; while (Date.now() < deadline) { if (await evaluate(waitFor)) { ready = true; break; } await sleep(150); } const state = await evaluate('(function(){ try { return JSON.parse((document.title||"").replace(/^TESTSTATE /,"")) } catch(e){ return null } })()'); await sleep(settle); if (domMode) { const evalOut = opt("eval") ? await evaluate(opt("eval")) : undefined; return { ready, state, evalOut, consoleLines, exceptions }; } if (opt("eval")) await evaluate(opt("eval")); const shot = await send("Page.captureScreenshot", { format: "png", captureBeyondViewport: false }); await writeFile(resolve(out), Buffer.from(shot.data, "base64")); return { ready, state, consoleLines, exceptions }; }); const tag = result.ready ? "ready" : "TIMEOUT"; if (domMode) { console.log(`state=${tag}`); if (result.evalOut !== undefined) console.log(`eval=${JSON.stringify(result.evalOut)}`); console.log(JSON.stringify(result.state, null, 2)); } else { console.log(`${out} ${tag}`); if (result.state) console.log(` state: ${JSON.stringify(result.state)}`); } const noisy = result.consoleLines.filter((l) => /error|warn|uncaught|fail/i.test(l)); if (noisy.length) console.log(" console: " + noisy.slice(0, 6).join(" | ")); if (result.exceptions.length) console.log(" exceptions: " + result.exceptions.slice(0, 4).join(" | ")); if (!result.ready) process.exit(1);