Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/scripts/classify-wework-desktop-e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ set -euo pipefail
core_segments=(
workspace-tabs
priority-filter
automation-lifecycle
model-routing
core-task-flow
window-lifecycle
goal-lifecycle
Expand Down Expand Up @@ -100,6 +102,13 @@ classify_wework_path() {
select_target "core:priority-filter"
return
;;
wework/src/pages/AutomationsPage* | \
wework/src/features/automations/* | \
wework/src/features/todo/ProjectAutomationView.tsx | \
wework/src/types/automation.ts)
select_target "core:automation-lifecycle"
return
;;
# The main sidebar also owns project creation, chats, and attachments.
wework/src/components/layout/DesktopSidebar.tsx)
select_target "core:priority-filter"
Expand Down Expand Up @@ -181,6 +190,7 @@ classify_wework_path() {
wework/src/features/local-runtime/* | \
wework/src/stream/*)
select_target "core:core-task-flow"
select_target "core:model-routing"
return
;;

Expand Down
18 changes: 17 additions & 1 deletion .github/scripts/test-classify-ci-changes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ wework_desktop_other_e2e_matrix={"include":[]}' \

full_desktop_expected='wework_desktop_e2e=true
wework_desktop_core_e2e=true
wework_desktop_core_e2e_matrix={"include":[{"id":"core-workspace-tabs","name":"Core / workspace-tabs","command":"e2e:desktop","segment":"workspace-tabs"},{"id":"core-priority-filter","name":"Core / priority-filter","command":"e2e:desktop","segment":"priority-filter"},{"id":"core-core-task-flow","name":"Core / core-task-flow","command":"e2e:desktop","segment":"core-task-flow"},{"id":"core-window-lifecycle","name":"Core / window-lifecycle","command":"e2e:desktop","segment":"window-lifecycle"},{"id":"core-goal-lifecycle","name":"Core / goal-lifecycle","command":"e2e:desktop","segment":"goal-lifecycle"},{"id":"core-supervisor-lifecycle","name":"Core / supervisor-lifecycle","command":"e2e:desktop","segment":"supervisor-lifecycle"},{"id":"core-resilience","name":"Core / resilience","command":"e2e:desktop","segment":"resilience"},{"id":"core-conversation-state","name":"Core / conversation-state","command":"e2e:desktop","segment":"conversation-state"},{"id":"core-workspace-attachments","name":"Core / workspace-attachments","command":"e2e:desktop","segment":"workspace-attachments"},{"id":"core-rendering-extensions","name":"Core / rendering-extensions","command":"e2e:desktop","segment":"rendering-extensions"},{"id":"core-embedded-browser","name":"Core / embedded-browser","command":"e2e:desktop","segment":"embedded-browser"}]}
wework_desktop_core_e2e_matrix={"include":[{"id":"core-workspace-tabs","name":"Core / workspace-tabs","command":"e2e:desktop","segment":"workspace-tabs"},{"id":"core-priority-filter","name":"Core / priority-filter","command":"e2e:desktop","segment":"priority-filter"},{"id":"core-automation-lifecycle","name":"Core / automation-lifecycle","command":"e2e:desktop","segment":"automation-lifecycle"},{"id":"core-model-routing","name":"Core / model-routing","command":"e2e:desktop","segment":"model-routing"},{"id":"core-core-task-flow","name":"Core / core-task-flow","command":"e2e:desktop","segment":"core-task-flow"},{"id":"core-window-lifecycle","name":"Core / window-lifecycle","command":"e2e:desktop","segment":"window-lifecycle"},{"id":"core-goal-lifecycle","name":"Core / goal-lifecycle","command":"e2e:desktop","segment":"goal-lifecycle"},{"id":"core-supervisor-lifecycle","name":"Core / supervisor-lifecycle","command":"e2e:desktop","segment":"supervisor-lifecycle"},{"id":"core-resilience","name":"Core / resilience","command":"e2e:desktop","segment":"resilience"},{"id":"core-conversation-state","name":"Core / conversation-state","command":"e2e:desktop","segment":"conversation-state"},{"id":"core-workspace-attachments","name":"Core / workspace-attachments","command":"e2e:desktop","segment":"workspace-attachments"},{"id":"core-rendering-extensions","name":"Core / rendering-extensions","command":"e2e:desktop","segment":"rendering-extensions"},{"id":"core-embedded-browser","name":"Core / embedded-browser","command":"e2e:desktop","segment":"embedded-browser"}]}
wework_desktop_other_e2e=true
wework_desktop_other_e2e_matrix={"include":[{"id":"plugins","name":"Plugins","command":"e2e:desktop:plugins","segment":""},{"id":"cloud","name":"Cloud","command":"e2e:desktop:cloud","segment":""}]}'

Expand Down Expand Up @@ -292,6 +292,22 @@ wework_desktop_other_e2e=true
wework_desktop_other_e2e_matrix={"include":[{"id":"plugins-skill-mention-rendering","name":"Plugins / skill-mention-rendering","command":"e2e:desktop:plugins","segment":"skill-mention-rendering"}]}' \
"wework/src/components/chat/composer/ComposerMentionMenu.tsx"

assert_desktop_case "model settings select task launch and model routing coverage" \
'wework_desktop_e2e=true
wework_desktop_core_e2e=true
wework_desktop_core_e2e_matrix={"include":[{"id":"core-model-routing","name":"Core / model-routing","command":"e2e:desktop","segment":"model-routing"},{"id":"core-core-task-flow","name":"Core / core-task-flow","command":"e2e:desktop","segment":"core-task-flow"}]}
wework_desktop_other_e2e=false
wework_desktop_other_e2e_matrix={"include":[]}' \
"wework/src/features/model-settings/localModelSettings.ts"

assert_desktop_case "automation files select only automation lifecycle coverage" \
'wework_desktop_e2e=true
wework_desktop_core_e2e=true
wework_desktop_core_e2e_matrix={"include":[{"id":"core-automation-lifecycle","name":"Core / automation-lifecycle","command":"e2e:desktop","segment":"automation-lifecycle"}]}
wework_desktop_other_e2e=false
wework_desktop_other_e2e_matrix={"include":[]}' \
"wework/src/features/automations/AutomationDetailWorkspace.tsx"

assert_desktop_case "browser E2E changes avoid desktop jobs" \
'wework_desktop_e2e=false
wework_desktop_core_e2e=false
Expand Down
23 changes: 18 additions & 5 deletions docs/en/wework/developer-guide/wework-e2e-automation.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,12 @@ Matrix submissions use a 10-second timeout. If the composer already displays a s
The main desktop flow's short-conversation layout regression stores `short-conversation-00-ready.png`, `short-conversation-01-prompt-filled.png`, `short-conversation-02-completed-top-aligned.png`, and `short-conversation-layout-metrics.json`. The final screenshot and metrics are captured after switching away and reopening the conversation. The gate requires the first message to remain within `160px` of the message viewport's top edge. For focused local diagnosis, run `node wework/e2e/desktop/task-flow.e2e.mjs --short-conversation-only`; the same check remains part of the regular `e2e:desktop` flow rather than a separate CI entrypoint.

The main desktop runner also supports execution through ordered checkpoints.
The checkpoints are `workspace-tabs`, `core-task-flow`, `window-lifecycle`,
`goal-lifecycle`, `resilience`, `conversation-state`, `workspace-attachments`,
`rendering-extensions`, and `embedded-browser`. `--segment <checkpoint>` performs common startup
and project initialization, then runs only the selected checkpoint.
The checkpoints are `workspace-tabs`, `priority-filter`, `telemetry-consent`,
`automation-lifecycle`, `model-routing`, `core-task-flow`, `window-lifecycle`,
`goal-lifecycle`, `supervisor-lifecycle`, `resilience`, `conversation-state`,
`workspace-attachments`, `rendering-extensions`, `browser-multi-tabs`, and
`embedded-browser`. `--segment <checkpoint>` performs common startup and project
initialization, then runs only the selected checkpoint.
`--from-segment <checkpoint>` starts there and continues through every later
checkpoint. When upstream checkpoints are skipped, each checkpoint establishes
its own minimal fixtures instead of depending on tasks or UI state created only
Expand All @@ -139,11 +141,22 @@ feature coverage is registered. Segment commands are also useful for focused
local iteration:

```bash
pnpm --filter wework e2e:desktop -- --segment automation-lifecycle
pnpm --filter wework e2e:desktop -- --segment model-routing
pnpm --filter wework e2e:desktop -- --segment window-lifecycle
pnpm --filter wework e2e:desktop -- --from-segment window-lifecycle
pnpm --filter wework e2e:desktop -- --segment workspace-attachments
```

`automation-lifecycle` independently covers automation creation, immediate
execution, pinning and continuing an existing task, and scheduled continuation.
`model-routing` independently covers all six local protocol-switch directions,
cross-provider retry, the vision sidecar, and the local model protocol matrix.
Both checkpoints establish their own minimal fixtures, so they no longer extend
the critical path for task creation, follow-up, and background plans in
`core-task-flow`. An unsegmented complete desktop run still executes these
scenarios, so splitting them does not reduce coverage.

The plugin desktop suite reuses the same segment options while keeping its
separate Codex Home initialization environment. Its ordered segments are
`plugin-lifecycle`, `skill-mention-rendering`, and
Expand Down Expand Up @@ -179,7 +192,7 @@ Optional `WEWORK_E2E_EXECUTOR_BIN` and `WEWORK_E2E_APP_BIN` reuse already-built

On macOS, desktop E2E launches a temporary `.app` bundle in the background through `open -g`. The test-only `WEWORK_E2E_BACKGROUND_WINDOW=1` setting keeps the Tauri main window hidden, prohibits application activation, and hides its Dock icon. Background throttling is disabled for the hidden WebView, so DOM control, timers, and snapshots continue to work. After the controller connects, the runner also asserts that the test application is not the current foreground process, preventing focus-stealing regressions. Only the desktop E2E runner injects this variable; normal development and production launches are unchanged.

The cloud-project scenario starts a real Backend, Redis, and a real Executor registered as a remote device. It exercises real authentication, device RPC, task persistence, and project deletion while covering project creation, task execution, conversation restoration, follow-up, and project removal. The scenario also verifies all three model protocols through the Backend proxy for cloud Model CRDs, plus local-executor use of Codex and cloud models under the same connected account. Only provider model endpoints are simulated; Backend HTTP and WebSocket APIs must not be mocked. Project cleanup must wait until the task is no longer running; rendered assistant text does not mean the final task state has been persisted. Python 3.11, `uv`, and `redis-server` are required to run this scenario.
The cloud-project scenario starts a real Backend, Redis, and a real Executor registered as a remote device. It exercises real authentication, device RPC, task persistence, and project deletion while covering project creation, task execution, conversation restoration, follow-up, and project removal. The scenario also verifies all three model protocols through the Backend proxy for cloud Model CRDs, plus local-executor use of Codex and cloud models under the same connected account. Only provider model endpoints are simulated; Backend HTTP and WebSocket APIs must not be mocked. To shorten cold startup, the Executor build runs in parallel with Backend, Redis, and database preparation, while remote Executor registration runs in parallel with the Tauri application build. Application startup still waits for both prerequisite groups to finish. Project cleanup must wait until the task is no longer running; rendered assistant text does not mean the final task state has been persisted. Python 3.11, `uv`, and `redis-server` are required to run this scenario.

Before validating local-executor models for a connected account, the cloud scenario selects its isolated directory through the current Projects → Local project entrypoint and confirms the name in the local-project creation dialog. Desktop E2E coverage must follow this primary product flow instead of relying on the removed existing-project test entrypoint.

Expand Down
18 changes: 14 additions & 4 deletions docs/zh/wework/developer-guide/wework-e2e-automation.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,11 @@ node e2e/utils/mock-connector-upstream-server.mjs
主桌面流程的短对话布局回归会保存 `short-conversation-00-ready.png`、`short-conversation-01-prompt-filled.png`、`short-conversation-02-completed-top-aligned.png` 和 `short-conversation-layout-metrics.json`。最后一个截图和 metrics 均在切走并重新打开对话后生成;门禁要求首条消息距离消息视口顶部不超过 `160px`。本地排查该回归时可直接运行 `node wework/e2e/desktop/task-flow.e2e.mjs --short-conversation-only`,但该检查同时属于常规 `e2e:desktop` 主流程,不是独立 CI 入口。

主桌面 runner 也支持按有序 checkpoint 分段执行。当前 checkpoint 依次为
`workspace-tabs`、`core-task-flow`、`window-lifecycle`、`goal-lifecycle`、
`resilience`、`conversation-state`、`workspace-attachments`、
`rendering-extensions` 和 `embedded-browser`。
`workspace-tabs`、`priority-filter`、`telemetry-consent`、
`automation-lifecycle`、`model-routing`、`core-task-flow`、
`window-lifecycle`、`goal-lifecycle`、`supervisor-lifecycle`、`resilience`、
`conversation-state`、`workspace-attachments`、`rendering-extensions`、
`browser-multi-tabs` 和 `embedded-browser`。
`--segment <checkpoint>` 在公共启动和项目初始化后只运行指定 checkpoint;
`--from-segment <checkpoint>` 从指定 checkpoint 开始并继续执行所有后续
checkpoint。跳过上游时,每个 checkpoint 会自行建立最小前置 fixture,不依赖只有
Expand All @@ -134,11 +136,19 @@ merge queue 会验证最终进入 `main` 的合并提交,因此合入后不再
必须同步登记对应 segment。分段命令也可用于本地快速迭代:

```bash
pnpm --filter wework e2e:desktop -- --segment automation-lifecycle
pnpm --filter wework e2e:desktop -- --segment model-routing
pnpm --filter wework e2e:desktop -- --segment window-lifecycle
pnpm --filter wework e2e:desktop -- --from-segment window-lifecycle
pnpm --filter wework e2e:desktop -- --segment workspace-attachments
```

`automation-lifecycle` 独立覆盖自动化创建、立即执行、固定既有任务以及定时继续
会话;`model-routing` 独立覆盖六种本地协议切换方向、跨 provider 重试、视觉
sidecar 和本地模型协议矩阵。两者都建立自身最小 fixture,因此不会拉长
`core-task-flow` 的任务创建、追问和后台计划关键路径。无参数运行完整桌面流程时,
这些场景仍会执行,不会因为分段而减少覆盖。

插件桌面套件也复用同一套分段参数,但保持独立的 Codex Home 初始化环境。插件
segment 依次为 `plugin-lifecycle`、`skill-mention-rendering` 和
`sites-plugin-auto-install`。每个 segment 都会建立自身所需的最小插件 fixture,
Expand Down Expand Up @@ -170,7 +180,7 @@ CODEX_BIN=/absolute/path/to/codex pnpm --filter wework e2e:desktop

在 macOS 上,桌面 E2E 会通过临时 `.app` bundle 和 `open -g` 在后台启动。测试专用的 `WEWORK_E2E_BACKGROUND_WINDOW=1` 会让 Tauri 保持主窗口隐藏、禁止应用激活并隐藏 Dock 图标;隐藏 WebView 会关闭后台节流,因此 DOM 控制、计时器和截图仍正常工作。runner 在连接控制器后还会断言测试应用不是当前前台进程,防止窗口抢焦点行为回归。该环境变量只由桌面 E2E runner 注入,不改变正常开发或生产启动行为。

云端项目场景会启动真实 Backend、Redis 和一个注册为远端设备的真实 Executor,通过真实鉴权、设备 RPC、任务持久化和项目删除接口完成创建项目、执行任务、恢复会话、连续追问与删除项目验证。场景同时验证云端 Model CRD 经 backend 代理转发三种模型协议,以及同一云端账号下的 Codex/云端模型在本机 executor 中执行。测试只模拟 provider 模型端点;不得模拟 Backend HTTP 或 WebSocket 接口。清理项目之前必须等待任务的运行状态结束;助手文本已经渲染并不代表任务状态已经完成持久化。运行该场景需要 Python 3.11、`uv` 和 `redis-server`。
云端项目场景会启动真实 Backend、Redis 和一个注册为远端设备的真实 Executor,通过真实鉴权、设备 RPC、任务持久化和项目删除接口完成创建项目、执行任务、恢复会话、连续追问与删除项目验证。场景同时验证云端 Model CRD 经 backend 代理转发三种模型协议,以及同一云端账号下的 Codex/云端模型在本机 executor 中执行。测试只模拟 provider 模型端点;不得模拟 Backend HTTP 或 WebSocket 接口。为缩短冷启动时间,Executor 构建与 Backend/Redis/数据库准备并行,远端 Executor 注册与 Tauri 应用构建并行;应用启动前仍必须同时等待两组前置任务完成。清理项目之前必须等待任务的运行状态结束;助手文本已经渲染并不代表任务状态已经完成持久化。运行该场景需要 Python 3.11、`uv` 和 `redis-server`。

云端场景在验证连接账号下的本机执行模型之前,会通过当前“项目 → 本地项目”入口选择隔离目录,并在本地项目创建对话框中确认名称。桌面 E2E 应复用这个产品主流程,不得继续依赖已经移除的“已有项目”测试入口。

Expand Down
2 changes: 2 additions & 0 deletions wework/e2e/desktop/checkpoints.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export const DESKTOP_CHECKPOINTS = [
'workspace-tabs',
'priority-filter',
'telemetry-consent',
'automation-lifecycle',
'model-routing',
'core-task-flow',
'window-lifecycle',
'goal-lifecycle',
Expand Down
Loading
Loading