关于 drive-desktop-app
用于 Reticle 的技能,可同时观察 Electron、Tauri 的 IPC 与渲染器行为,让桌面应用测试验证后端操作,而非依赖浏览器网络日志。
主要功能
- 01观察渲染器与 IPC 活动
- 02断言 IPC 命令结果
- 03检测 Electron preload 覆盖缺失
- 04支持截图和视觉差异比较
适用场景
- 01验证 Electron 操作触发 IPC 命令
- 02测试 Tauri invoke 流程
- 03检查操作后的桌面界面与控制台
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ reticlehq/ reticle --skill drive-desktop-app安装后在 Claude Code 中输入/drive-desktop-app确认已加载。
详细介绍
驱动桌面应用,并证明发生了什么
桌面应用通过 IPC 而非 HTTP 与后端通信。修改 fetch/XHR 无法观察到这些通信,因此,浏览器形态的工具看不到应用发出的任何后端调用:网络日志为空,操作没有正在进行的请求可供等待,而基于网络的断言则会空洞地通过。这从一开始就是一次假通过。
Reticle 同时观察渲染进程和 IPC 边界,因此桌面应用的判定与 Web 应用的判定含义相同。还没设置?请按照 reticle 技能操作:它会安装 Reticle,在 init 修改应用文件前询问用户,并验证一个流程。
Electron:两行代码,无需改动应用代码
// vite.config.ts — desktop:true lets connect() start in a packaged renderer (NODE_ENV=production).
// A default (production-mode) `vite build` ships no Reticle code; to drive a packaged
// renderer with no dev server, build it with `vite build --mode development`
export default defineConfig({
base: './', // file:// needs relative asset paths
plugins: [react(), reticle({ desktop: true })],
});
// electron/preload.cjs — FIRST line. This is what makes main-process IPC visible.
require('@reticlehq/electron/preload');
它必须放在 preload 中,而且必须是第一行。contextBridge.exposeInMainWorld 会向渲染进程提供一个深度冻结的对象,因此页面中的任何内容都无法再对其进行插桩。preload 是 ipcRenderer.invoke 仍可写入的最后位置,而且这个 shim 必须在 preload 捕获自身引用之前运行。
沙盒化的 preload 无法解析 node_modules,所以裸 require 会失败。可以打包 preload(electron-vite 和 Forge 默认会这样做),或者设置 sandbox: false。
Tauri:必须配置 CSP,且失败时不会有提示
前端与任何 Web 应用相同。容易被忽略的是,Tauri 的默认 CSP 会在桥接 WebSocket 打开前将其阻止,因此应用运行完全正常,只是永远不会连接:
{
"app": {
"security": {
"csp": "default-src 'self' ipc: http://ipc.localhost; connect-src 'self' ipc: http://ipc.localhost ws://localhost:4400 ws://127.0.0.1:4400"
}
}
}
保留 ipc: http://ipc.localhost:Tauri v2 需要它来运行 invoke。这仅用于开发;请从发布配置中移除 ws:// 条目。
观察 IPC 无需修改 Rust 端:invoke('load_todos') 会直接以 ipc://load_todos 的形式到达 Reticle。只有截图和无头模式才需要 reticle-tauri crate,而且它的版本与 npm 包独立管理。
另外:请使用 hash 路由器。 打包后的渲染进程从 file:// 提供服务,基于 history 的路由无法解析。
验证
与 Web 应用使用相同的循环,只需在断言条件中加入 IPC:
reticle_act_and_wait({ sessionId, ref, action: "click", until: { kind: "allOf", predicates: [
{ kind: "net", urlContains: "ipc://todos:archive", status: 200 },
{ kind: "element", query: { testid: "..." } },
{ kind: "console", level: "error", absent: true },
]}})
IPC 没有状态码。 200/500 是根据命令是否成功映射得到的合成状态码,这样相同的断言条件才能继续工作。在 Tauri 上,你会看到 status: 500 和 statusText: "OK" 同时出现。这不是错误:传输正常响应,而 500 是命令本身的判定结果。ok 才是权威结果。
reticle_look { action: "state" } 会像在 Web 上一样读取实时存储。平台的捕获步骤接好后,reticle_screenshot 和 reticle_visual_diff 就可以使用。Electron 无需额外配置;Tauri 需要该 crate。Tauri 的无头模式通过 RETICLE_HEADLESS=1 启用,截图仍然可用,因为捕获过程渲染的是 Webview,而不是屏幕。
缺少观察器时的表现
Electron preload 缺失时会明确报告,而不是静默失败:判定结果会返回 coverage: partial,并指出缺少的那一行,而不是在盲区中给出干净结果。如果看到这种情况,请先添加 preload 行,再相信任何结果。
如果应用运行正常,但看不到 IPC 调用:在 Electron 上,shim 的 require 不是第一行。在 Tauri 上,会观察到来自 @tauri-apps/api/core 的 invoke,但不会观察手写的 postMessage 协议。
诚实性
桌面应用中的 unknown 也不代表通过。不要为了把红色判定改成绿色而放宽 IPC 断言:桌面应用中的假通过,正是这套接线要消除的问题。
完整的桌面应用参考文档:curl https://docs.reticle.sh/desktop.md。其他所有内容:curl https://docs.reticle.sh/llms.txt。