返回 DeepSeek-Reasonix
browser-runtime-smoke.mjs
根目录 / desktop / electron / scripts / browser-runtime-smoke.mjs
1 import { execFileSync } from "node:child_process";
2 import { mkdtempSync, copyFileSync, rmSync, readFileSync, mkdirSync, writeFileSync } from "node:fs";
3 import assert from "node:assert/strict";
4 import { createRequire } from "node:module";
5 import { tmpdir } from "node:os";
6 import { dirname, join, resolve } from "node:path";
7 import { fileURLToPath } from "node:url";
8 import { build } from "esbuild";
9 import { buildBrowserPage } from "./build-browser-page.mjs";
10
11 const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
12 const prepare = process.argv[2] === "--prepare-only" ? process.argv[3] : undefined;
13 if (process.argv.length > 2 && (!prepare || process.argv.length !== 4)) throw new Error("usage: browser-runtime-smoke.mjs [--prepare-only <directory>]");
14 const scratch = mkdtempSync(join(tmpdir(), "reasonix-browser-smoke-"));
15 try {
16 await buildBrowserPage();
17 for (const name of ["browser-page.js", "browser-page.json", "browser-recorder.js", "recorder-preload.cjs"]) copyFileSync(join(root, "dist", name), join(scratch, name));
18 // Top-level await lives inside an async wrapper; the code still uses the
19 // production main-process minification and function-name settings.
20 const source = readFileSync(join(root, "src/main/browser/browserRuntime.native.ts"), "utf8");
21 const imports = source.split("\n").filter(line => line.startsWith("import ")).join("\n");
22 const body = source.split("\n").filter(line => !line.startsWith("import ")).join("\n");
23 await build({ stdin: { contents: `${imports}\n(async () => {${body}})().then(() => app.quit(), error => { console.error(error); app.exit(1); });`, resolveDir: join(root, "src/main/browser"), loader: "ts" }, outfile: join(scratch, "main.cjs"), bundle: true, platform: "node", format: "cjs", minify: true, keepNames: true, external: ["electron"] });
24 const env = { ...process.env };
25 await build({ entryPoints: [join(root, "src/main/browser/guestPreload.ts")], outfile: join(scratch, "guest-preload.cjs"), bundle: true, platform: "node", format: "cjs", external: ["electron"] });
26 if (prepare) {
27 // Stage the identical production-parameter fixture for a native VM. This
28 // mode performs no validation; the native runner must produce result.json.
29 mkdirSync(prepare, { recursive: true });
30 for (const name of ["main.cjs", "guest-preload.cjs", "browser-page.js", "browser-page.json", "browser-recorder.js", "recorder-preload.cjs"]) copyFileSync(join(scratch, name), join(prepare, name));
31 console.log(`Native fixture prepared (not executed): ${prepare}`);
32 } else {
33 delete env.ELECTRON_RUN_AS_NODE;
34 // Electron #51488: AppKit may reopen a crash-state modal despite an isolated
35 // Chromium profile. Apply the official workaround to this process only.
36 const platformArgs = process.platform === "darwin" ? ["-ApplePersistenceIgnoreState", "YES"] : [];
37 const recordingSeconds = Number(process.env.REASONIX_BROWSER_RECORD_SECONDS ?? 1);
38 if (!Number.isInteger(recordingSeconds) || recordingSeconds < 1 || recordingSeconds > 90) throw new Error("recording test duration must be 1–90 seconds");
39 const timeout = Math.max(process.env.REASONIX_BROWSER_CYCLES === "100" ? 180_000 : 60_000, recordingSeconds === 1 ? 0 : (recordingSeconds + 60) * 1000);
40 const evidence = process.env.REASONIX_BROWSER_EVIDENCE_PATH || join(root, "artifacts/browser-runtime", process.platform);
41 mkdirSync(evidence, { recursive: true });
42 // A failed attempt must not leave a previous run's success as its status.
43 writeFileSync(join(evidence, "result.json"), JSON.stringify({ completed: false, status: "running", platform: process.platform }));
44 try {
45 execFileSync(createRequire(import.meta.url)("electron"), [join(scratch, "main.cjs"), ...platformArgs, `--user-data-dir=${join(scratch, "data")}`], { env, stdio: "inherit", timeout, killSignal: "SIGKILL" });
46 assert.equal(JSON.parse(readFileSync(join(scratch, "result.json"), "utf8")).completed, true, "native runner exited before completing all assertions");
47 } catch (error) {
48 writeFileSync(join(evidence, "result.json"), JSON.stringify({ completed: false, status: "failed", platform: process.platform, exitCode: error.status ?? null, signal: error.signal ?? null }));
49 throw error;
50 }
51 for (const name of ["result.json", "browser-evidence.png", "browser-recording.webm", "browser-diagnostics.json"]) copyFileSync(join(scratch, name), join(evidence, name));
52 console.log(`Browser runtime evidence: ${evidence}`);
53 }
54 } finally { rmSync(scratch, { recursive: true, force: true }); }
55
55 lines Plain Text