> For the complete documentation index, see [llms.txt](https://tinyhumans.gitbook.io/openhuman/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tinyhumans.gitbook.io/openhuman/zh/kai-fa-zhong/e2e-testing.md).

# 端到端测试

使用 WDIO + Appium 进行端到端测试。CI 和本地设置。

## 概览

桌面端 E2E 测试使用 **WebDriverIO（WDIO）** 通过 Appium 驱动 Tauri 应用：

| 平台                          | 驱动              | 端口   | 应用格式          | 选择器       |
| --------------------------- | --------------- | ---- | ------------- | --------- |
| **Linux / Appium Chromium** | Appium Chromium | 4723 | 调试二进制文件       | CSS / DOM |
| **macOS / Appium Chromium** | Appium Chromium | 4723 | `.app` bundle | CSS / DOM |

OpenHuman 的桌面应用运行在 Tauri 原生的 Wry WebView 上（CEF 运行时已在 #5478 中移除）。CI 通过 Xvfb 驱动 Linux 调试二进制文件；macOS / Windows 上附加到 CEF 远程调试端口的 Chromium-driver 后端已不再工作——在原生驱动（Appium Mac2 / WinAppDriver）落地（#5485）之前，这些平台没有桌面端 E2E 覆盖。

***

## 快速开始

### Linux / Appium Chromium

```bash
# 安装 Appium 和 Chromium 驱动（一次性）
npm install -g appium@3
appium driver install --source=npm appium-chromium-driver

# 构建 E2E 应用
pnpm --filter openhuman-app test:e2e:build

# 运行所有流程
pnpm --filter openhuman-app test:e2e:all:flows

# 运行单个 spec
bash app/scripts/e2e-run-spec.sh test/e2e/specs/smoke.spec.ts smoke
```

在无头 Linux 上，测试框架运行于 **Xvfb** 之下，以提供虚拟显示器。

### macOS / Appium Chromium

```bash
# 安装 Appium + Chromium 驱动（一次性，需要 Node 24+）
npm install -g appium@3
appium driver install --source=npm appium-chromium-driver

# 构建 .app bundle
pnpm --filter openhuman-app test:e2e:build

# 运行所有流程
pnpm --filter openhuman-app test:e2e:all:flows
```

### macOS 上的 Docker（本地 Linux 测试框架）

通过 Docker 在 macOS 上运行同样的基于 Linux 的测试框架。

```bash
# 构建并运行所有 E2E 流程
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 smoke
```

需要 Docker Desktop 或 Colima。仓库以绑定挂载方式接入，因此构建结果会在多次运行之间保留。

***

## 架构

### 平台检测

`app/test/e2e/helpers/platform.ts` 导出：

* `isTauriDriver()`，遗留兼容层，现在始终返回 `true` ，用于具备 DOM 能力的 Chromium 会话
* `isMac2()`，遗留兼容层，现在始终返回 `为 false`
* `supportsExecuteScript()`, `true` 因为 Chromium 驱动支持 `browser.execute()` 在所有平台上

### 元素辅助函数

`app/test/e2e/helpers/element-helpers.ts` 提供统一 API：

| 辅助函数                      | Appium Chromium                              |
| ------------------------- | -------------------------------------------- |
| `waitForText(text)`       | 基于 DOM 文本内容的 XPath                           |
| `waitForButton(text)`     | `按钮` / `[role="button"]` XPath               |
| `clickText(text)`         | 标准 `el.click()`                              |
| `clickNativeButton(text)` | 标准 `el.click()` 在按钮上                         |
| `clickToggle()`           | `[role="switch"]` / `input[type="checkbox"]` |
| `waitForWindowVisible()`  | 窗口句柄检查                                       |
| `waitForWebView()`        | `document.readyState` 检查                     |
| `hasAppChrome()`          | 窗口句柄检查                                       |
| `dumpAccessibilityTree()` | HTML 页面源代码                                   |

### 稳定的测试 ID

优先使用稳定的 `data-testid` 用于 E2E spec 点击或轮询的 UI 交互钩子。使用如下分类法 `<surface>-<element>-<id?>`，例如：

* `cron-jobs-panel`, `cron-refresh`
* `cron-job-row-<jobId>`, `cron-job-toggle-<jobId>`, `cron-job-run-<jobId>`, `cron-job-view-runs-<jobId>`, `cron-job-remove-<jobId>`
* `settings-nav-<routeId>`
* `skill-row-<skillId>`, `skill-install-<skillId>`, `skill-uninstall-<skillId>`
* `thread-row-<threadId>`, `new-thread-button`, `send-message-button`
* `onboarding-next-button`

从前端使用 `waitForTestId(testId)` 和 `clickTestId(testId)` 来自 `element-helpers.ts` 当 spec 目标是这些钩子之一时使用。保留文本选择器用于对用户可见文案的断言，不要用于行/操作发现。

### 深度链接辅助函数

`app/test/e2e/helpers/deep-link-helpers.ts` 处理认证深度链接：

* **Appium Chromium**: `browser.execute(window.__simulateDeepLink(url))` 在所有平台上
* **macOS 回退方案**: `macos: deepLink` 扩展命令，然后 `open -a ...`

对于发布候选版本，在触及 CEF 预检、单实例或深度链接启动代码时，还应在 Linux 或 macOS 上手动运行一次二级实例冒烟测试：

1. 正常启动 OpenHuman 并保持其运行。
2. 触发 `openhuman://auth?token=e2e-token&key=auth` 通过操作系统打开器。
3. 确认已在运行的窗口接收到回调，并且没有启动第二个完整的 CEF 实例。
4. 确认二级进程能够干净退出，不会出现 CEF 缓存锁错误。

这可以捕获这样一类回归：二级进程在 Tauri 的深度链接转发路径安装之前，就在 CEF 缓存预检期间退出。

### 编写跨平台 spec

1. **使用辅助函数** 来自 `element-helpers.ts`，绝不要在 spec 中直接使用原始 `XCUIElementType*` 选择器
2. **从前端使用 `clickNativeButton(text)`** 而不是内联按钮点击代码
3. **从前端使用 `hasAppChrome()`** 而不是检查 `XCUIElementTypeMenuBar`
4. **从前端使用 `waitForWebView()`** 而不是检查 `XCUIElementTypeWebView`
5. 对于仅 macOS 的测试，使用 `process.platform` 守卫或单独的 spec 文件
6. 从前端使用 `navigateViaHash(route)` 用于 hash 路由；它会等待 hash、 `document.readyState`以及已挂载的 React 根后再返回。在引导完成后， `walkOnboarding()` 还会等待 `#/home` 以及一个 Home 页标记，然后 spec 才会跳转到其他位置。

***

## 环境变量

| 变量                          | 默认      | 描述                             |
| --------------------------- | ------- | ------------------------------ |
| `APPIUM_PORT`               | `4723`  | Appium 服务器端口                   |
| `E2E_MOCK_PORT`             | `18473` | 模拟后端服务器端口                      |
| `OPENHUMAN_WORKSPACE`       | （临时目录）  | 应用工作区目录                        |
| `OPENHUMAN_SERVICE_MOCK`    | `0`     | 启用服务模拟模式                       |
| `OPENHUMAN_E2E_MODE`        | 未设置     | 启用破坏性测试支持 RPC；E2E 运行器会将其设为 `1` |
| `OPENHUMAN_E2E_AUTH_BYPASS` | 未设置     | 启用 JWT 绕过认证                    |
| `DEBUG_E2E_DEEPLINK`        | （详细）    | 设为 `0` 以静默深度链接日志               |
| `E2E_FORCE_CARGO_CLEAN`     | 未设置     | 在 E2E 构建前强制执行 cargo clean      |

***

## CI 工作流

### Push / PR 检查

默认的拉取请求门禁是 `.github/workflows/ci-lite.yml` （快速通道：质量检查 + 仅针对变更文件的单元测试）。E2E 套件不会在 PR 上运行到 `main` ——完整 E2E 矩阵（Rust 模拟后端、Playwright Web、Linux/macOS/Windows 桌面端）在 `.github/workflows/ci-full.yml` 针对 `release` 分支的 PR 上以及每次 push 到该分支时运行。

macOS 和 Windows 桌面端 E2E 不会在每个 PR 上运行。当需要在晋升前获得跨平台桌面信号时，请使用手动触发的 E2E 工作流（`.github/workflows/e2e.yml`）。

### macOS / Appium Chromium

macOS/Appium Chromium 可用于本地运行，也可通过手动触发的 E2E 工作流使用：

1. 安装 Appium + Chromium 驱动
2. 构建 `.app` bundle
3. 运行所有 E2E 流程

***

## 故障排查

### Linux：“WebView 未就绪”超时

对于默认的 CEF 运行时，这通常意味着某个过时的本地运行器正尝试通过 WebKitWebDriver 驱动一个基于 CEF 的 WebView。当前 CI 在 Linux 上使用 Appium Chromium 驱动；请使用 `app/scripts/e2e-run-session.sh` 或 PR CI 工作流来走受支持的 Linux 路径。

确保 `DISPLAY` 已设置且 Xvfb 正在运行：

```bash
export DISPLAY=:99
Xvfb :99 -screen 0 1280x1024x24 &
```

还要确保 dbus 已启动（webkit2gtk 需要）：

```bash
eval $(dbus-launch --sh-syntax)
```

### Linux：找不到 Appium Chromium 驱动

```bash
npm install -g appium@3
appium driver install --source=npm appium-chromium-driver
```

### macOS：在 `tauri dev`

中深度链接无法工作 `.app` 深度链接需要一个 `pnpm tauri build --debug --bundles app` 。请改用它。

### Docker：首次运行时构建较慢

第一次 Docker 构建会编译 Rust 并安装 E2E 测试框架依赖。后续运行会使用缓存层。Cargo 仓库和 git 源会通过 Docker 卷进行缓存。

## Spec：通知

**文件**: `app/test/e2e/specs/notifications.spec.ts`

通过在线 core sidecar 和 Notifications UI 页面测试通知 RPC 方法：

* `notification_ingest`，通过 core RPC 创建一条新通知
* `notification_list`，验证返回了已摄取的通知
* `notification_mark_read`，将通知标记为已读
* `notification_stats`，检查聚合统计的形状
* UI：Notifications 页面渲染集成通知区块（`[data-testid="integration-notifications-section"]`)
* UI：Notifications 页面显示 System Events 区块（`[data-testid="system-events-section"]`)

**运行**:

```bash
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()` 支持，而当前后端在所有平台上都提供该支持。

***

## Agent 可观察制品流程

对于一次规范、可检查的运行，若要将截图、页面源代码转储和模拟请求日志落盘：

```bash
bash app/scripts/e2e-agent-review.sh
```

制品会落在 `app/test/e2e/artifacts/<timestamp>-agent-review/`。完整详情 + 辅助 API： [`agent-observability.md`](/openhuman/zh/kai-fa-zhong/agent-observability.md)。任何失败的测试都会触发 `wdio.conf.ts`的 `afterTest` 钩子，它会写入 `failure-*.png` + `failure-*.source.xml` 到同一个运行目录中。

***

## Rust 推理提供器 E2E

这些测试（`tests/inference_provider_e2e.rs`）使用 **wiremock** 来模拟 HTTP 上游，因此不需要真实的 LLM API 调用。它们覆盖 OpenAI 兼容聊天、Anthropic 认证风格、按模型抑制 temperature、Ollama 本地提供器，以及 `/v1` HTTP 端点认证层。

```bash
# 本地：
bash scripts/test-rust-inference-e2e.sh

# 通过 Docker（Linux，与 CI 使用相同镜像）：
docker compose -f e2e/docker-compose.yml run --rm inference-e2e
```
