控制UI端到端测试 是做什么的?
通过确定性的网关数据提供控制UI设计反馈和真实浏览器流程。
UI 压力测试
- 实质性UI变更:尽早构建本地HTML压力测试画廊;让用户比较有意义的状态,并就具体示例提供反馈。
- 用于:涉及多种状态的布局、交互和组件。当画廊无法提供有用的比较时,小的文案/图标编辑可以跳过。
- 从受影响的组件和数据契约中推导出示例。涵盖相关的正常、加载、空、错误、不可用、权限、选定、展开状态;以及可能对布局造成压力的长文本/密集内容。互斥状态需分开显示;对于当前实现不支持的拟定状态,需进行标注。
- 一个可在浏览器中打开的HTML概览:包含稳定的示例ID、简短的状态标签以及足以理解每个示例的上下文。优先使用真实组件和确定性模拟fixture。静态/近似渲染需标注;对于无法通过HTML再现的交互,需链接到运行中的UI。生成的画廊:属于任务拥有的工件存储;默认不提交。
- 每个示例:带标签的反馈输入。使用特定于画廊的存储键和稳定的示例ID,在本地持久化(跨刷新保持)。复制反馈操作可将示例ID、状态标签和评论以Markdown/纯文本格式导出,供用户返回对话。
- 在可用的预览浏览器中打开画廊;共享URL/文件路径。在迭代过程中保留相同的画廊、示例ID和现有评论。将用户反馈同时应用于画廊和实现;保持两者可比性。
- 在请求反馈前:检查渲染示例在相关视口尺寸下的显示效果;验证反馈在刷新后能存活,并能导出正确的示例标签。报告不支持的状态或预览限制。
画廊支持设计审查;但不能替代针对性的行为测试或从运行中的UI检查的前后对比证据。在下面新建的抓取目录中保留最终证据,与不断演变的画廊分开。
测试形态
- 完整的GUI流程:
ui/src/**/*.e2e.test.ts。 ui/src/test-helpers/control-ui-e2e.ts:启动Vite控制UI并安装模拟的Gateway WebSocket。- 确定性场景。除非用户明确要求实时证明,否则不包含实时提供商密钥、真实渠道凭据或真实网关。
- 窄化渲染逻辑:优先使用现有的
.browser.test.ts/ 单元测试。E2E通道覆盖:路由、应用启动、网关握手、请求以及可见的UI行为整合。
命令
- 在Codex工作树中定位单个E2E测试:
node scripts/run-vitest.mjs run --config test/vitest/vitest.ui-e2e.config.ts --configLoader runner ui/src/e2e/chat-flow.messaging.e2e.test.ts
- 在普通检出目录中运行整个本地通道:
pnpm test:ui:e2e
- 使用已准备好的依赖安装或已准备的普通检出;切勿在使用其他作业时协调共享安装。
- 遵循
$openclaw-testing:可信的开发证明可在本地运行;远程证明需要浏览器/平台、纯净环境或源码隔离的理由。
视觉证据默认设置
- 外观更改:检查前后视觉证据。
- 其他行为:最清晰适当的边界证明;当断言已能证明更改时,视频+截图集并非必须。
- 确定性Vitest E2E断言;绝不提交生成后的截图/视频。
- 共享套件禁用Chromium部分光栅化和GPU光栅化,以避免观察到依赖于绘制历史的边缘像素。在声明可重现性之前,仍需进行精确的重复比较。
- 瞬态状态例如已保存:在fixture之前安装
page.clock;使用pauseVirtualClock暂停;仅推进进入状态所需的fixture工作。单独使用setFixedTime不会暂停定时器。就绪状态捕获使用原生布局交付,无需推进fixture时钟。 - 静态截图:使用
ui/src/test-helpers/control-ui-e2e-screenshot.ts中的takeControlUiScreenshotFrame,包含明确的语义内容和animations: "disabled"。通过viewport/scrollTo传入视口变化/预期滚动目标;验证保持的居中、视口/滚动裁剪交集、稳定的布局/字体/可见图像。 - 在
elements中传递所有相关的元素定位器;从该单帧中保存返回的页面PNG和裁剪图。裁剪图在测量的PNG像素中包含分数边界;断言其尺寸和未改变的边界。切勿将单独的页面/定位器截图合并作为同帧证据。 - 默认的当前帧模式 + 现有的视口/元素辅助函数保留了录制和采样动画行为。在边界测量和未裁剪捕获期间,静态准备保持激活状态;全页证明必须符合视口帧尺寸。
- 在/于重点E2E测试之后:如果可用,运行模拟控制UI应用,例如
pnpm dev:ui:mock -- --port <port>。 - 使用Playwright驱动Chromium连接到本地模拟URL。使用截图/短视频捕获所需状态。
- 使用
browser.newContext({ recordVideo: { dir, size }, viewport }),page.screenshot({ path });在报告视频路径前关闭上下文。 - 会话主机命令状态证明:仅视口截图,使用 Playwright 1.62.1 + Chrome 151.0.7922.34(Linux 真实网关;macOS arm64 合成复现)验证。此修复未迁移/认证其他录制所有者;请分别验证他们所需的截图内容和最终化视频。请参阅 已验证的捕获路径和上游限制。
- 使用
ui/src/test-helpers/control-ui-e2e-artifacts.ts中的createControlUiE2eArtifactDir(scope, parentDir?)分配保留证据。每次调用原子性地创建新目录并记录实际路径。父目录优先级:显式父目录 → 修剪后的现有OPENCLAW_UI_E2E_ARTIFACT_DIR→ 仓库的.artifacts/control-ui-e2e父目录。现有的自定义输出控制选择父目录;切勿添加/重写环境变量以启用捕获。 - 在测试/场景或
beforeEach期间分配,每次尝试一次;独立脚本每次调用分配一次。将所有者显式传递给共享捕获辅助函数。保留原始门控、功能/阶段名称、视口、等待和录制选项。不同的阶段:不同的文件名。将截图/报告/视频放在一起。 - 保留成功和失败的证据。报告实际分配的路径,包括重定位的文件名覆盖。在审查后手动仅删除确切拥有的目录;在重放前切勿清除共享父目录。可丢弃的构建/媒体fixture和拥有的临时原始视频可以保持清理。新的合成捕获无法恢复被覆盖的证据。
- 超时诊断:在现有诊断父目录下创建新的子目录。Mantis 保留调用拥有目录下的每次捕获尝试;拒绝报告覆盖。真实网关套件、
chat-outbox-*、chat-attachment-read-lifecycle:不同的所有者;在声称在该处保留可重放之前进行协调。 - 录制 = 验证,而不仅仅是演示捕获。如果录制器失败/显示意外行为:停止,修复行为,添加/更新回归测试,重新录制。
- 视觉证据被阻止:精确说明阻止原因;仍然报告文本E2E证据。
模拟模式
启动应用服务器;在 page.goto 之前安装模拟;断言网关流量和可见UI:
const server = await startControlUiE2eServer();
const page = await context.newPage();
const gateway = await installMockGateway(page, {
historyMessages: [{ role: "assistant", content: [{ type: "text", text: "Ready." }] }],
});
await page.goto(`${server.baseUrl}chat`);
await page.locator(".agent-chat__composer-combobox textarea").fill("hello");
await page.getByRole("button", { name: "Send message" }).click();
const request = await gateway.waitForRequest("chat.send");
await gateway.emitChatFinal({ runId: String(request.params.idempotencyKey), text: "Done." });
await page.getByText("Done.").waitFor();
新的流程需要更多网关表面:使用类型化的场景选项/方法响应来扩展 installMockGateway。
运行检查器证据
- 使用
ui/src/test-helpers/control-ui-run-inspector.ts中的withControlUiRunInspector收集。拥有单独的页面;成功/失败时关闭;保留调用方的聊天页面和未发送草稿。 preparePage:模拟网关或战役的现有每标签页认证设置。共享浏览器上下文不会复制另一个标签页的会话存储令牌。- 辅助函数使用渲染组件模型的
RunInspectorSelector+activityRunInspectorSelectorHref,包括所选收据的决策游标。 - 渲染面板的
data-run-id+data-execution-id:返回存在的身份,而不仅仅是请求的URL。所选详情中的data-receipt-selector-id=DecisionReceiptDisplayV1.selectorId。 - 缺少/模糊的身份或缺少所选收据不得计为匹配。
- 决策选择器与可选的终端转录音键
agent.wait.terminalReceipt.assistantTranscriptIdempotencyKey分开。切勿从DOM选择器/历史行制造键,或声称检查器证据修复了其缺失。 - 侧边栏运行状态的当前可访问标签:
Active run,而不是Running。
独立录制
- 带旁白的字幕、缓和的缩放、快进暂停:使用 proof-video 开发技能。独立模板录制原始证据 + 提示辅助文件,使用系统 ffmpeg 渲染带字幕的 MP4。保留原始捕获;附上检查过的精美视频。
- 已运行的模拟控制UI URL:临时Playwright脚本或
playwright test规范;保持录制流程聚焦:- 打开模拟URL;通过稳定的
data-*/ 面向用户角色选择器进行交互;等待断言的状态,而不是固定的休眠。 - 请求驱动流程:断言可见UI + 模拟的网关流量。例如:预期计数/行可见 +
sessions.list被调用并带有预期的search、offset、limit。 - 仅在断言之后进行短暂休眠,以便捕获的视频可读。
- 生成的视频:调用时新建的分配目录。切勿提交或删除较早的捕获。
- 打开模拟URL;通过稳定的