import { spawn } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import { createRequire } from 'node:module'; import { fileURLToPath } from 'node:url'; /** * 在真实 Electron 里跑 IPC 探针:拉起应用(窗口加载 scripts/codex-ipc-probe.html), * 探针页逐条调用 controller/codexCtl/* 并写出结果,这里汇总打印。 * * 用法:npm run smoke:ipc */ const require = createRequire(import.meta.url); const here = path.dirname(fileURLToPath(import.meta.url)); const appDir = path.join(here, '..'); const probeFile = path.join(here, 'codex-ipc-probe.html'); const outFile = path.join(here, 'codex-ipc-probe.json'); const timeoutMs = Number(process.env.ZSJZ_PROBE_TIMEOUT || 180_000); fs.rmSync(outFile, { force: true }); const electronPath = require('electron'); const child = spawn(electronPath, ['.', '--env=local'], { cwd: appDir, stdio: 'inherit', env: { ...process.env, ZSJZ_CODEX_PROBE: `file:///${probeFile.replace(/\\/g, '/')}`, ZSJZ_CODEX_PROBE_OUT: outFile, }, }); let finished = false; const timer = setTimeout(() => { if (finished) return; finished = true; clearInterval(poll); console.error(`\n[smoke:ipc] 超时(${timeoutMs / 1000}s)未拿到探针结果,结束应用`); child.kill('SIGKILL'); printAndExit(null, '探针超时未完成'); }, timeoutMs); /** * 探针页跑完只写结果文件——渲染进程里的 process.exit() 关不掉主进程, * 所以这里轮询结果文件,拿到后再结束应用。 */ const poll = setInterval(() => { if (finished || !fs.existsSync(outFile)) return; setTimeout(() => { if (finished) return; finished = true; clearInterval(poll); clearTimeout(timer); let report = null; try { report = JSON.parse(fs.readFileSync(outFile, 'utf8')); } catch { // 结果文件损坏,按失败处理 } child.kill('SIGKILL'); printAndExit(report, report ? null : '探针结果文件无法解析'); }, 800); }, 500); child.on('exit', (code) => { if (finished) return; finished = true; clearInterval(poll); clearTimeout(timer); let report = null; try { report = JSON.parse(fs.readFileSync(outFile, 'utf8')); } catch { // 探针没跑完就退出了 } printAndExit(report, report ? null : `探针未写出结果(electron exit=${code})`); }); function printAndExit(report, fatal) { if (!report) { console.error(`[smoke:ipc] ${fatal}`); process.exit(1); } for (const item of report.results) { const detail = JSON.stringify(item.detail ?? ''); console.log(`${item.ok ? 'PASS' : 'FAIL'} ${item.name} ${detail.slice(0, 160)}`); } const failed = report.results.filter((item) => !item.ok); console.log(`\n[smoke:ipc] ${report.results.length - failed.length}/${report.results.length} 通过`); process.exit(failed.length === 0 ? 0 : 1); }