Files
ttstd-3d-director/scripts/test-electron.cjs
tongtongstudio b9fd289caf chore: 迁移到 pnpm 并更新打包与测试文档
- 用 pnpm 替换 npm,锁定 pnpm@10.33.0,删除 package-lock.json 相关说明
- package.json 将渲染层依赖移入 devDependencies,减小 app.asar 体积
- 新增 electron:smoke 冒烟脚本与 icon 生成脚本
- win 平台设置 AppUserModelID,修复任务栏图标归组
- 清理 tsBuildInfoFile 输出路径至 node_modules/.tmp
- 更新测试断言以匹配画幅遮罩、取景器缩放与模型库外部资产行为
- README 与打包指南补充 pnpm、镜像与常见问题说明
2026-09-16 20:03:57 +08:00

106 lines
3.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// scripts/test-electron.cjs
// 冒烟测试:确认构建产物 dist/index.html 能被 Electron 正常加载,且渲染层没有报错。
//
// 用法:pnpm run electron:smoke
// 注意:package.json 中有 "type": "module",因此本文件必须使用 .cjs 后缀,
// 否则 require 会因被当作 ESM 而报错。
//
// 这个脚本也用来验证「dist 是自包含的」:渲染层需要的所有依赖都已被 Vite
// 打包进 dist/assets,因此打包时不需要把 node_modules 塞进 app.asar。
// 有的终端 / CI 会注入 ELECTRON_RUN_AS_NODE=1,那会让 Electron 退化成纯 Node,
// require('electron') 只返回二进制路径,后面会以 "Cannot read properties of
// undefined (reading 'whenReady')" 收场。提前拦下并给出可执行的修复命令。
if (process.env.ELECTRON_RUN_AS_NODE) {
console.error("FAIL 检测到环境变量 ELECTRON_RUN_AS_NODE 已设置。");
console.error(" Electron 会因此退化成纯 Node,require('electron') 只返回二进制路径。");
console.error(" 清掉它再运行:");
console.error(" bash : unset ELECTRON_RUN_AS_NODE");
console.error(" cmd : set ELECTRON_RUN_AS_NODE=");
console.error(" PowerShell: Remove-Item Env:ELECTRON_RUN_AS_NODE");
process.exit(1);
}
const { app, BrowserWindow } = require("electron");
const path = require("path");
console.log("Electron:", process.versions.electron);
console.log("Chrome:", process.versions.chrome);
console.log("Node:", process.versions.node);
const distEntry = path.join(__dirname, "..", "dist", "index.html");
const rendererErrors = [];
const IGNORABLE = [
// 模型库是仓库外的外部资产,缺失时浏览器只会报资源 404,不影响应用本身
/模型库/,
/net::ERR_FILE_NOT_FOUND/,
];
// 加载 + 渲染 + 首帧动画都跑完,再判定结果
const SETTLE_MS = 4000;
app.whenReady().then(async () => {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
show: false,
webPreferences: {
// 与 electron/main.cjs 保持一致,避免冒烟环境比生产环境宽松
nodeIntegration: false,
contextIsolation: true,
},
});
mainWindow.webContents.on("console-message", (_event, level, message) => {
// level: 0=verbose 1=info 2=warning 3=error
if (level >= 3 && !IGNORABLE.some((re) => re.test(message))) {
rendererErrors.push(message);
}
});
mainWindow.webContents.on("did-fail-load", (_event, code, description, url) => {
rendererErrors.push(`did-fail-load ${code} ${description} ${url}`);
});
mainWindow.webContents.on("render-process-gone", (_event, details) => {
rendererErrors.push(`render-process-gone ${details.reason}`);
});
try {
await mainWindow.loadFile(distEntry);
console.log("OK 页面加载成功:", distEntry);
} catch (error) {
console.error("FAIL 页面加载失败:", error.message);
console.error(" 请先执行 pnpm run build 生成 dist/");
app.exit(1);
return;
}
// 给 React 首帧和 R3F 初始化留出时间,暴露运行时错误
await new Promise((resolve) => setTimeout(resolve, SETTLE_MS));
const rootChildCount = await mainWindow.webContents
.executeJavaScript("document.getElementById('root')?.childElementCount ?? -1")
.catch(() => -1);
if (rootChildCount > 0) {
console.log(`OK React 已挂载(#root 子节点 ${rootChildCount} 个)`);
} else {
rendererErrors.push(`#root 未渲染任何子节点(childElementCount=${rootChildCount})`);
}
if (rendererErrors.length === 0) {
console.log("PASS 冒烟测试通过:页面加载、React 挂载、无渲染层错误");
app.exit(0);
} else {
console.error(`FAIL 渲染层出现 ${rendererErrors.length} 个错误:`);
rendererErrors.slice(0, 10).forEach((message) => console.error(" -", message));
app.exit(1);
}
});
app.on("window-all-closed", () => {
app.quit();
});