
OpenHuman 桌面端 E2E 测试实战指南WDIO Appium Chromium 驱动 Tauri 应用的完整方案【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman本篇指南系统讲解 OpenHuman 桌面应用端到端E2E测试的完整技术方案从 WebDriverIOWDIO驱动 Tauri 应用的基础架构、跨平台元素定位策略、稳定data-testid规范、深度链接deep link注入到环境变量矩阵、CI 工作流、Mock 后端隔离与故障排查并延伸至 Agent 可观测的产物捕获层与 Rust 推理 Provider 的 wiremock E2E。读者读完可掌握在 Linux/macOS/Windows 三平台搭建并维护一套可复现、可调试的桌面 E2E 测试体系且能直接对照仓库源码验证每个环节的底层实现。总览E2E 测试的定位与技术选型OpenHuman 的桌面 E2E 测试使用WebDriverIOWDIO作为测试运行器通过 Appium 驱动 Tauri 应用。依据 gitbooks/developing/e2e-testing.md 文档当前矩阵如下平台Driver端口应用格式选择器Linux / Appium ChromiumAppium Chromium4723Debug 二进制CSS / DOMmacOS / Appium ChromiumAppium Chromium4723.appbundleCSS / DOME2E 测试在项目中的定位是最外层、最接近真实用户的验证手段它以完整构建出的桌面应用为载体驱动真实 WebView验证登录、引导onboarding、消息、Cron 任务、技能Skills、设置面板、支付等跨前后端链路的端到端行为。它与 Rust 单元/集成测试、Playwright Web 测试共同构成 OpenHuman 的分层测试体系可参考 gitbooks/developing/testing-strategy.md。关于驱动后端的演进说明文档描述 OpenHuman 桌面应用使用 CEF 运行时tauri-runtime-cefCI 以 Appium Chromium 驱动 Linux debug 二进制macOS/Windows 手动 E2E 使用同一 Chromium 后端。从当前仓库源码看app/test/wdio.conf.ts 与 app/scripts/e2e-run-session.sh 的注释显示应用已随 #5456 迁移至 WryWebKit运行时#5478 移除了基于 CEF CDP 端口的 Appium Chromium 后端CDP 仅存在于 Chromium 引擎下当前 Linux 驱动路径为tauri-driverE2E_USE_TAURI_DRIVER1时启用e2e-run-session.sh。.github/workflows/e2e-reusable.yml 中仍保留 Appium appium-chromium-driver的安装与缓存步骤。下文以文档所述方案为主干同时标注当前源码实现状态以便读者对照实际代码。快速开始本地跑通第一组 E2ELinux / Appium Chromium# 安装 Appium 与 Chromium driver一次性 npm install -g appium3 appium driver install --sourcenpm appium-chromium-driver # 构建 E2E 应用 pnpm --filter openhuman-app test:e2e:build # 运行全部 flows pnpm --filter openhuman-app test:e2e:all:flows # 运行单个 spec bash app/scripts/e2e-run-spec.sh test/e2e/specs/smoke.spec.ts smoke在无头 Linux 环境下harness 通过Xvfb提供虚拟显示见下文故障排查。macOS / Appium Chromium# 安装 Appium Chromium driver一次性需要 Node 24 npm install -g appium3 appium driver install --sourcenpm appium-chromium-driver # 构建 .app bundle pnpm --filter openhuman-app test:e2e:build # 运行全部 flows pnpm --filter openhuman-app test:e2e:all:flows在 macOS 上用 Docker 跑 Linux harness在 macOS 上通过 Docker 运行与 CI 一致的 Linux E2E harness需要 Docker Desktop 或 Colima。仓库以 bind-mount 方式挂载构建产物在多次运行间持久保留。# 构建 运行全部 E2E flows docker compose -f e2e/docker-compose.yml run --rm e2e # 先构建应用如需 docker compose -f e2e/docker-compose.yml run --rm e2e \ pnpm --filter openhuman-app test:e2e:build # 运行单个 spec docker compose -f e2e/docker-compose.yml run --rm e2e \ bash app/scripts/e2e-run-spec.sh test/e2e/specs/smoke.spec.ts smokepackage.json 中的 E2E 脚本矩阵app/package.json 中定义了完整的 E2E 脚本入口可作为日常操作的速查表脚本作用test:e2e:build执行bash ./scripts/e2e-build.sh构建 E2E 应用test:e2e:web构建 Web 版并运行e2e-web-session.shtest:e2e:mega构建后运行mega-flow.spec.ts全流程test:e2e:login/test:e2e:auth登录 / 认证专项脚本test:e2e:skills-registry运行 Skills 注册表 spectest:e2e:cron-jobs运行 Cron 任务 spectest:e2e:all:flows运行e2e-run-all-flows.sh全量 spec 套件test:e2e:session运行e2e-run-session.sh统一会话运行器test:e2e:session:full先构建再运行完整会话test:all覆盖率 Rust E2E 全量回归入口架构统一的后端与分层辅助层平台检测Platform detectionapp/test/e2e/helpers/platform.ts 导出平台检测函数。当前统一到 Chromium driver 后这些函数作为 shim 保留始终走 DOM 能力路径isTauriDriver()遗留 shim现在始终返回true——统一的 Chromium 会话暴露 WebView DOM与旧 tauri-driver 路径一致supportsExecuteScript()始终返回true——Chromium driver 是全功能 W3C WebDriver在所有平台支持browser.execute()isMac2()遗留 shim现在始终返回false——旧的 Appium Mac2可访问性树路径已被统一后端取代。这意味着此前写在if (isTauriDriver())分支里的 DOM 操作现在在每个平台都会执行spec 无需再做平台分支。元素辅助层Element helpersapp/test/e2e/helpers/element-helpers.ts 提供跨平台统一 API封装了两种后端的差异Mac2 走可访问性树 XPathtauri-driver 走 DOM XPathHelperAppium Chromium 下的实现waitForText(text)对 DOM 文本内容做 XPath 匹配waitForButton(text)button/[rolebutton]的 XPathclickText(text)标准el.click()clickNativeButton(text)在 button 上执行标准el.click()clickToggle()[roleswitch]/input[typecheckbox]waitForWindowVisible()窗口句柄检查waitForWebView()document.readyState检查hasAppChrome()窗口句柄检查dumpAccessibilityTree()HTML 页面源码源码中还有几个值得注意的细节waitForTestId(testId)/clickTestId(testId)通过[data-testid...]定位元素当前仅在 DOM 能力后端可用element-helpers.tsclickAtElement()在 DOM 后端优先使用browser.execute(e e.click())注入式点击绕开 WebDriver 点击常见的element not interactable / click intercepted错误并先scrollIntoView避免 webkit2gtk 不自动滚动的问题element-helpers.tstextExists()与visibleTextExists()是非阻塞检查——后者额外要求isDisplayed()为真用于排除折叠的处理转录processing transcript中残留文本的干扰element-helpers.ts。稳定测试 IDStable test IDs对于 E2E spec 需要点击或轮询的 UI 控件优先提供稳定的data-testid命名遵循surface-element-id?分类法。文档给出的实际范例cron-jobs-panel、cron-refreshcron-job-row-jobId、cron-job-toggle-jobId、cron-job-run-jobId、cron-job-view-runs-jobId、cron-job-remove-jobIdsettings-nav-routeIdskill-row-skillId、skill-install-skillId、skill-uninstall-skillIdthread-row-threadId、new-thread-button、send-message-buttononboarding-next-button当 spec 针对这类 hook 时应使用waitForTestId(testId)/clickTestId(testId)文本选择器只用于用户可见文案的断言不做行/动作的发现定位——这样可显著降低 UI 文案变动导致的用例脆弱性。深度链接辅助层Deep link helpersapp/test/e2e/helpers/deep-link-helpers.ts 处理认证类深度链接如openhuman://auth?token...keyauth策略优先级WebView 内window.__simulateDeepLink(url)通过browser.execute()注入当前统一后端在所有平台均可用是最可靠的主路径源码中会先探测__simulateDeepLink是否就绪最长轮询 25 秒macOS 扩展命令macos: launchAppmacos: deepLink保留给需要系统 Launch Services 分发 URL scheme 的 macOS shell-out 场景重试 3 次macOS shell 回退open -a ... url。源码对 Linux 做了明确约束Linux 容器没有注册openhuman://scheme 的.desktop文件xdg-open无法分发 URL因此 WebView simulate 失败时直接抛出异常而不是无意义地尝试 shell 回退deep-link-helpers.ts。此外该文件还实现了JWT bypass 认证buildBypassJwt(userId)构造一个alg: none的三段式 JWTheader.payload.e2e签名段仅保留格式前端解码不校验签名triggerAuthDeepLink(token)支持OPENHUMAN_E2E_AUTH_BYPASS_TOKEN与OPENHUMAN_E2E_AUTH_BYPASS1配合OPENHUMAN_E2E_AUTH_BYPASS_USER_ID两条旁路路径触发认证深链前会先内联关闭 BootCheckGateChoose core mode 弹窗避免认证就绪等待被 gate 卡死。发布候选的手工二次实例冒烟文档明确要求当改动涉及 CEF preflight、单实例single-instance或深链启动代码时在 Linux 或 macOS 上手工做一次二次实例冒烟正常启动 OpenHuman 并保持运行通过系统 opener 触发openhuman://auth?tokene2e-tokenkeyauth确认已在运行的窗口收到回调且不会启动第二个完整 CEF 实例确认二次进程干净退出无 CEF cache-lock 错误。该流程专门捕获二次进程在 Tauri 深链转发路径安装前于 CEF 缓存 preflight 阶段退出这类回归。编写跨平台 spec 的六条规范一律使用 element-helpers.ts 中的 helper严禁在 spec 中使用原始XCUIElementType*选择器点击按钮用clickNativeButton(text)不要内联写点击逻辑判断应用 chrome 用hasAppChrome()不要检查XCUIElementTypeMenuBar等待 WebView 用waitForWebView()不要检查XCUIElementTypeWebViewmacOS 专属测试用process.platform守卫或独立 spec 文件hash 路由使用navigateViaHash(route)——它内部等待 hash 生效、document.readyState就绪以及 React 根挂载后才返回onboarding 之后walkOnboarding()还会等待#/home与 Home 页标记然后 spec 才能继续导航。环境变量矩阵文档给出了 E2E 运行时可调的全部环境变量变量默认值说明APPIUM_PORT4723Appium 服务端口E2E_MOCK_PORT18473Mock 后端服务端口OPENHUMAN_WORKSPACE(临时目录)应用工作区目录OPENHUMAN_SERVICE_MOCK0启用 service mock 模式OPENHUMAN_E2E_MODE未设置启用破坏性测试支持 RPCE2E runner 会置为1OPENHUMAN_E2E_AUTH_BYPASS未设置启用 JWT 旁路认证DEBUG_E2E_DEEPLINK(verbose)设为0静默深链日志E2E_FORCE_CARGO_CLEAN未设置强制在 E2E 构建前执行 cargo clean从 e2e-run-session.sh 源码可以看到 runner 实际还会导出更多环境VITE_BACKEND_URL/BACKEND_URL指向http://127.0.0.1:${E2E_MOCK_PORT}把前端与 core sidecar 都指向 mockOPENHUMAN_TELEGRAM_BOT_API_BASE、OPENHUMAN_COMPOSIO_DIRECT_BASE_V2/V3重定向 Telegram Bot API 与 Composio 直连请求到 mock 端口实现外部依赖全隔离OPENHUMAN_KEYRING_BACKENDfile无头 Linux CI 没有可用 Secret Service/keychain改用文件型 keyring认证状态写入OPENHUMAN_WORKSPACE随工作区一起清理OPENHUMAN_CEF_CACHE_PATH指向独立临时目录mega-flowspec 会调用openhuman.config_reset_local_data删除整个OPENHUMAN_WORKSPACE若 CEF 缓存放在工作区内会被连根拔掉、杀死 WebDriver 会话报 invalid session id因此 runner 把 CEF 缓存放到工作区外的兄弟目录e2e-run-session.sh。Mock 后端的隔离设计app/test/e2e/mock-server.ts 是对共享 mock 后端的薄封装从 scripts/mock-api-core.mjs 再导出startMockServer、stopMockServer、getRequestLog、setMockBehavior(s)等接口并额外提供 Telegram 注入工具injectTelegramUpdate向 mock 队列注入一条 Telegram UpdategetTelegramSentMessages断言 bot 的出站消息mock-server.ts。runner 在启动前会生成一份完整的 E2E config.tomle2e-run-session.sh将全部推理路由到 mock 的 OpenAI-compatible 端点api_url http://127.0.0.1:18473 primary_cloud p_e2e_mock default_model e2e-mock-model chat_provider e2e:e2e-mock-model reasoning_provider e2e:e2e-mock-model agentic_provider e2e:e2e-mock-model coding_provider e2e:e2e-mock-model [[cloud_providers]] id p_e2e_mock slug e2e label E2E Mock endpoint http://127.0.0.1:18473/openai/v1 auth_style none default_model e2e-mock-model注释解释了为何要预填充cloud_providersunify_ai_provider_settings迁移在首次启动时运行若cloud_providers为空会种子化 OpenHuman 入口并设置primary_cloud从而把所有推理路由到OpenHumanBackendProvidersupports_streamingfalse永远返回非流式响应mock 收不到/openai/v1/chat/completions。预填充后迁移跳过种子化provider_for_role()经primary_cloud → slug e2e → auth_stylenone → OpenAiCompatibleProvider解析supports_streamingtruemock 才能收到流式请求。wdio.conf.ts 还实现了每个 spec 文件开头无条件调用/__admin/reset重置 mock 状态mock 后端持有模块级可变状态会话、Cron 任务、webhook 触发器、请求日志、socket 会话历史上一旦某个 spec 失败未执行自己的 reset 就会污染下一个 spec 文件现在通过resetMockBackendOncePerSpecFile按文件路径去重既保证隔离又不打断同文件内it之间有意保留的状态。运行器与 WDIO 配置单会话、顺序执行app/scripts/e2e-run-session.sh 是统一会话运行器负责校验构建产物存在pnpm test:e2e:build先行并检查前端 bundle 中确实包含 mock URL否则明确报错提示重新构建清理平台缓存的应用数据macOS 的~/Library/WebKit|Caches|Application Support/com.openhuman.appLinux 的~/.local/share|.cache|.config/com.openhuman.appWindows 的%APPDATA%/%LOCALAPPDATA%并备份/生成config.toml启动 driver轮询其/status端点就绪以--maxInstances 1运行wdio在 EXIT trap 中依次回收 driver → 应用 → 工作区并对 CEF 子进程树做先快照子 PID、再杀父进程、最后 SIGKILL 残留的处理避免父进程退出后子进程被 reparent 到 init 而无法pkill -P定位e2e-run-session.sh。wdio.conf.ts 的关键配置maxInstances: 1 单会话WDIO 每个 worker 只建一个 session所有 spec 在同一应用进程内顺序执行spec 之间无重启成本测试有意依赖顺序spec N 的状态流入 spec N1每个 spec 自行负责所需重置capabilities[wdio:maxInstances]: 1WDIO 的 per-capability 上限防止 runner 为每个 spec 各调度一个 WebKit 会话导致同时重置应用、级联启动超时before钩子在窗口句柄列表中优先切换到 URL 含tauri.localhost的主应用窗口找不到再回退到第一个非about:窗口afterTest钩子任何失败测试自动调用captureFailureArtifacts见下节输出failure-*.png与failure-*.source.xmlbailE2E_BAIL_ON_FAILURE1时置为 1首个失败 spec 后停止Mocha 超时 120 秒工具密集型 spec 后的重置可能等待 WebKit 与 core 清理保持两分钟套件预算而单个轮询 helper 各自保留短而具诊断性的超时specFileRetries: 0Linux 分片在e2e-run-all-flows.sh重启 driver 后重试失败 spec此处重试只会复用同一个卡死的 driver。CI 工作流Push / PR 检查默认 PR gate 是 .github/workflows/ci-lite.yml快速车道质量检查 变更文件的单元测试E2E 套件不在面向main的 PR 上运行完整 E2E 矩阵Rust mock 后端、Playwright Web、Linux/macOS/Windows 桌面运行在 .github/workflows/ci-full.yml触发条件是目标为release分支的 PR 及每次 pushmacOS 与 Windows 桌面 E2E 不随每个 PR 运行需要跨平台桌面信号时使用手动触发的 E2E workflow.github/workflows/e2e.yml。.github/workflows/e2e.yml 的手动触发输入包括run_linux默认 true、run_macos/run_windows默认 false等待原生驱动支持、fulltrue 跑全量分片套件false 只跑指定 spec、spec_path默认test/e2e/specs/mega-flow.spec.ts与spec_label。它复用 .github/workflows/e2e-reusable.yml其中对 Appium 与 chromium driver 做了缓存~/.appium、/usr/local/lib/node_modules/appiumkey 为appium3-chromium-${{ runner.os }}-v2并对 CEF 运行时下载约 400MB与 Rust 构建做分层缓存。macOS / Appium ChromiummacOS/Appium Chromium 可用于本地运行也可通过手动分发的 E2E workflow 执行步骤固定为安装 Appium Chromium driver → 构建.appbundle → 运行全部 E2E flows。故障排查LinuxWebView not ready 超时默认 CEF 运行时下这通常意味着过期的本地 runner 试图用 WebKitWebDriver 驱动 CEF 支撑的 WebView。当前 CI 在 Linux 使用 Appium Chromium driver请改用 app/scripts/e2e-run-session.sh 或 PR CI workflow 的受支持 Linux 路径。确保DISPLAY已设置且 Xvfb 在运行export DISPLAY:99 Xvfb :99 -screen 0 1280x1024x24 同时确保 dbus 已启动webkit2gtk 依赖eval $(dbus-launch --sh-syntax)Linux找不到 Appium Chromium drivernpm install -g appium3 appium driver install --sourcenpm appium-chromium-drivermacOStauri dev下深链不工作深链要求.appbundle请改用pnpm tauri build --debug --bundles appDocker首次构建很慢首次 Docker 构建需要编译 Rust 并安装 E2E harness 依赖后续运行使用缓存层Cargo registry 与 git sources 通过 Docker volumes 缓存见 e2e/docker-compose.yml 的e2e-cargo-registry、e2e-cargo-git、e2e-rust-target、e2e-tauri-target、e2e-pnpm-store、e2e-node-modules、e2e-cef-cache等命名卷。其中 CEF 缓存卷尤其关键cef-dll-sys将下载写入~/Library/Caches/tauri-cef容器内无命名卷时每个新容器都会重新下载约 400MB 的 CEF 归档且容器无出站 DNS/TLS 访问 CDN 时会失败。shm_size: 2gb是为匹配 GitHub-hosted ubuntu-22.04 runner 给 CEF 的共享内存配额。示例 SpecNotifications文件app/test/e2e/specs/notifications.spec.ts该 spec 通过live core sidecar与 Notifications UI 页面测试通知 RPC 方法notification_ingest通过 core RPC 创建新通知notification_list验证已摄入的通知被返回notification_mark_read将通知标记为已读notification_stats检查聚合统计形状UINotifications 页面渲染集成通知区[data-testidintegration-notifications-section]UINotifications 页面展示系统事件区[data-testidsystem-events-section]。运行bash app/scripts/e2e-run-spec.sh test/e2e/specs/notifications.spec.ts notifications平台说明RPC 类断言notification_ingest、notification_list、notification_mark_read、notification_stats通过统一 Appium Chromium 后端执行UI 断言依赖browser.execute()支持当前后端在所有平台均提供。源码中waitForCoreSidecar先轮询core.ping直至 sidecar 就绪最长 30 秒模块级变量ingestedNotifId跨用例共享服务端生成的 UUID保证 mark_read/list 引用同一通知notifications.spec.ts。Agent 可观测的产物捕获层gitbooks/developing/agent-observability.md 与 app/scripts/e2e-agent-review.sh 提供了一个**面向编码 AgentCodex、Claude Code、Cursor**的可检查运行入口落盘截图、页面源码 dump 与 mock 请求日志bash app/scripts/e2e-agent-review.sh产物落在app/test/e2e/artifacts/timestamp-agent-review/典型布局app/test/e2e/artifacts/ISO-timestamp-agent-review/ 01-welcome.png 01-welcome.source.xml 02-post-welcome.png 02-post-welcome.source.xml 03-post-onboarding.png 03-post-onboarding.source.xml 04-privacy-panel.png 04-privacy-panel.source.xml mock-requests-after-welcome.json mock-requests-after-onboarding.json mock-requests-after-privacy.json failure-test.png # 仅失败时生成 failure-test.source.xml # 仅失败时生成 meta.json # 运行元数据 检查点索引实现要点helpers/artifacts.tscaptureCheckpoint(name)按序号命名捕获使 run 目录按时间顺序可读saveMockRequestLog(name, getRequestLog())落盘 mock 请求日志快照captureFailureArtifacts已接入 wdio.conf.ts 的afterTest钩子任何失败测试自动触发spec不应直接调用环境覆盖E2E_ARTIFACT_DIR强制指定 run 目录、E2E_ARTIFACT_ROOT自动命名 run 目录的父目录默认app/test/e2e/artifacts、E2E_ARTIFACT_LABEL目录名标签默认runwrapper 设为agent-review。新 spec 中按如下方式使用import { captureCheckpoint, saveMockRequestLog } from ../helpers/artifacts; import { getRequestLog } from ../mock-server; await captureCheckpoint(after-connect-click); saveMockRequestLog(after-connect-click, getRequestLog());文档明确列出该层刻意不做的范围视觉基线/组件全状态图像 diff、每次点击都截图太吵、真实集成Gmail/Notion/Telegram仅 mock、新测试框架/reporter——先让这一个闭环跑通再考虑扩展更多 flows。Rust 推理 Provider E2E除桌面 UI E2E 外仓库还提供纯 Rust 层的推理 Provider E2Etests/inference_provider_e2e.rs。它使用wiremockmock HTTP 上游无需真实 LLM API 调用覆盖 OpenAI-compat chat、Anthropic 认证风格、per-model temperature 抑制、Ollama 本地 provider以及/v1HTTP 端点的认证层。# 本地 bash scripts/test-rust-inference-e2e.sh # 通过 DockerLinux与 CI 同镜像 docker compose -f e2e/docker-compose.yml run --rm inference-e2ee2e/docker-compose.yml 中的inference-e2e服务复用同一 CI 镜像以bash -lc ./scripts/test-rust-inference-e2e.sh为入口挂载工作区与 Cargo 缓存卷——保证本地复现与 CI 完全一致。小结OpenHuman 的 E2E 体系是一套以统一 WebDriver 后端为基础、以 Mock 全隔离为保障、以稳定 testid 与辅助层为规范、以产物可观测为调试出口的完整工程实践文档定义的 Appium Chromium 方案端口 4723、CSS/DOM 选择器依然是当前文档中的标准配置而源码已演进为 Wry 运行时 tauri-driver 的 Linux 路径两者可通过 wdio.conf.ts 与 e2e-run-session.sh 对照核实从 element-helpers.ts 的统一定位 API 到 deep-link-helpers.ts 的三级深链回退再到 wdio.conf.ts 的单会话顺序执行与 mock 按文件重置每个环节都能在仓库中找到对应的实现与注释依据想深入源码的读者可沿 app/test/e2e/helpers 目录、app/test/wdio.conf.ts、app/scripts/e2e-run-session.sh、e2e/docker-compose.yml 与 .github/workflows/e2e-reusable.yml 逐步追查将本文每一处结论落到具体代码行上。【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考