关于 expect
expect 是一款根据 Git 差异选定网页变更测试范围的技能。它将变更文件映射到受影响的路由,生成供审核的测试计划,并通过 agent-browser 执行。
主要功能
- 01依据 Git 差异限定范围
- 02将变更文件映射到路由
- 03生成 AI 测试计划
- 04使用 agent-browser 执行浏览器测试
- 05重放已保存的测试流程
- 06报告结果与测试证据
适用场景
- 01UI 修改后测试受影响页面
- 02集中检查分支差异涉及的页面
- 03重放已保存的登录流程
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yonatangross/ orchestkit --skill expect安装后在 Claude Code 中输入/expect确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
jev-shadow.defaults.yamlflows/crud.mdlogin.mdnavigation.mdreferences/aria-diffing.mdci-integration.mdconfig-schema.mddiff-scanner.mdexecution.mdfingerprint.mdhuman-review.mdjev-shadow.mdreport.mdresearch.mdroute-map.mdrrweb-recording.mdsaved-flows.mdscope-strategy.mdtest-plan.mdrules/_sections.mdartifact-storage.mddiff-scope-boundaries.mdfingerprint-invalidation.mdno-parallel-browsers.mdtimeout-and-retry.mdscripts/coverage-context.shdiff-scan.shfingerprint.shinit.shjev_shadow.py详细介绍
Expect — 差异感知型 AI 浏览器测试
分析 git 变更,生成有针对性的测试计划,并通过 AI 驱动的浏览器自动化执行。
注意: 如果启用了
disableSkillShellExecution(CC 2.1.91),代理浏览器的安装检查将不会运行。请确认已安装:npx agent-browser --version。
expect # Auto-detect changes, test affected pages
expect -m "test the checkout flow" # Specific instruction
expect --flow login # Replay a saved test flow
expect --target branch # Test all changes on current branch vs main
expect -y # Skip plan review, run immediately
核心原则: 只测试发生变更的内容。由 Git diff 确定范围,不在未受影响的页面上浪费时间。
参数解析
ARGS = "[-m <instruction>] [--target unstaged|branch|commit] [--flow <slug>] [-y]"
# Parse from full argument string
import re
raw = "" # Full argument string from CC
INSTRUCTION = None
TARGET = "unstaged" # Default: test unstaged changes
FLOW = None
SKIP_REVIEW = False
# Extract -m "instruction"
m_match = re.search(r'-m\s+["\']([^"\']+)["\']|-m\s+(\S+)', raw)
if m_match:
INSTRUCTION = m_match.group(1) or m_match.group(2)
# Extract --target
t_match = re.search(r'--target\s+(unstaged|branch|commit)', raw)
if t_match:
TARGET = t_match.group(1)
# Extract --flow
f_match = re.search(r'--flow\s+(\S+)', raw)
if f_match:
FLOW = f_match.group(1)
# Extract -y
if '-y' in raw.split():
SKIP_REVIEW = True
STEP 0:MCP 探测 + 前置条件检查
# memory is alwaysLoad in .mcp.json (CC 2.1.121+, #1541) — probe below kept as fallback for older CC:
ToolSearch(query="select:mcp__memory__search_nodes")
# Verify agent-browser is available (Rust-native, no Playwright)
Bash("command -v agent-browser || npx agent-browser --version")
# If missing: "Install agent-browser: npm i -g agent-browser"
# Load agent-browser's version-matched workflow guide (ships with the CLI).
# NOT `skills get agent-browser` — that resolves but returns only the thin
# top-level router; `core --full` is the actual 2,800+ line command reference.
Bash("agent-browser skills get core --full")
关键:任务管理
# 1. Create main task IMMEDIATELY
TaskCreate(
subject="Expect: test changed code",
description="Diff-aware browser testing pipeline",
activeForm="Running diff-aware browser tests"
)
# 2. Create subtasks for each pipeline phase
TaskCreate(subject="Check fingerprint (skip if unchanged)", activeForm="Checking fingerprint") # id=2
TaskCreate(subject="Scan git diff and classify changes", activeForm="Scanning diff") # id=3
TaskCreate(subject="Map changes to routes/URLs", activeForm="Mapping routes") # id=4
TaskCreate(subject="Generate AI test plan", activeForm="Generating test plan") # id=5
TaskCreate(subject="Execute tests via agent-browser", activeForm="Executing browser tests") # id=6
TaskCreate(subject="Compile test report", activeForm="Compiling report") # id=7
# 3. Set dependencies for sequential phases
TaskUpdate(taskId="3", addBlockedBy=["2"]) # Diff scan needs fingerprint check
TaskUpdate(taskId="4", addBlockedBy=["3"]) # Route map needs diff results
TaskUpdate(taskId="5", addBlockedBy=["4"]) # Test plan needs route map
TaskUpdate(taskId="6", addBlockedBy=["5"]) # Execution needs test plan
TaskUpdate(taskId="7", addBlockedBy=["6"]) # Report needs execution results
# 4. Update status as you progress
TaskUpdate(taskId="2", status="in_progress") # When starting
TaskUpdate(taskId="2", status="completed") # When done — repeat for each subtask
流程概览
Git Diff → Route Map → Fingerprint Check → Test Plan → Execute → Report
| 阶段 | 内容 | 输出 | 参考 |
|---|---|---|---|
| 1. 指纹 | 变更文件的 SHA-256 哈希 | 如果与上次运行相比没有变化,则跳过 | references/fingerprint.md |
| 2. Diff 扫描 | 解析 git diff,对变更分类 | ChangesFor 数据(文件、组件、路由) | references/diff-scanner.md |
| 3. 路由映射 | 将变更文件映射到受影响的页面/URL | 指定范围的页面列表 | references/route-map.md |
| 4. 测试计划 | 根据 diff 和路由映射生成 AI 测试计划 | 包含步骤的 Markdown 测试计划 | references/test-plan.md |
| 5. 执行 | 通过 agent-browser 运行测试计划 | 每个步骤的通过/失败状态、截图 | references/execution.md |
| 6. 报告 | 汇总结果、产物和退出代码 | 结构化报告 + 产物 | references/report.md |
阶段 1:指纹检查
检查当前变更是否已经过测试:
Read(".expect/fingerprints.json") # Previous run hashes
# Compare SHA-256 of changed files against stored fingerprints
# If match: "No changes since last test run. Use --force to re-run."
# If no match or --force: continue to Phase 2
加载:Read("references/fingerprint.md")
阶段 2:Diff 扫描
根据 --target 分析 git 变更:
if TARGET == "unstaged":
diff = Bash("git diff")
files = Bash("git diff --name-only")
elif TARGET == "branch":
diff = Bash("git diff main...HEAD")
files = Bash("git diff main...HEAD --name-only")
elif TARGET == "commit":
diff = Bash("git diff HEAD~1")
files = Bash("git diff HEAD~1 --name-only")
将每个变更文件分为 3 个级别:
- 直接 — 文件本身发生了变更
- 导入 — 导入了变更文件的文件
- 路由 — 渲染变更组件的页面/路由
加载:Read("references/diff-scanner.md")
阶段 3:路由映射
使用 .expect/config.yaml 将变更文件映射到可测试的 URL:
# .expect/config.yaml
base_url: http://localhost:3000
route_map:
"src/components/Header.tsx": ["/", "/about", "/pricing"]
"src/app/auth/**": ["/login", "/signup", "/forgot-password"]
"src/app/dashboard/**": ["/dashboard"]
如果不存在路由映射,则根据 Next.js App Router / Pages Router 约定推断。
加载:Read("references/route-map.md")
阶段 4:生成测试计划
根据当前目标的范围策略,生成限定在 diff 范围内的 AI 测试计划:
scope_strategy = get_scope_strategy(TARGET) # See references/scope-strategy.md
prompt = f"""
{scope_strategy}
Changes: {diff_summary}
Affected pages: {affected_urls}
Instruction: {INSTRUCTION or "Test that the changes work correctly"}
Generate a test plan with:
1. Page-level checks (loads, no console errors, correct content)
2. Interaction tests (forms, buttons, navigation affected by the diff)
3. Visual regression (compare ARIA snapshots if saved)
4. Accessibility (axe-core scan on affected pages)
"""
如果指定了 --flow,则从 .expect/flows/{slug}.yaml 加载已保存的工作流,而不是生成测试计划。
如果未指定 --y,则通过 AskUserQuestion 向用户展示计划,以便在执行前审核。
加载:Read("references/test-plan.md")
阶段 5:执行
agent-browser 快速入门
最低版本为
>= 0.31.1(已记录0.27.1在生产页面上存在问题);当前测试版本为 0.38.1(参见upstream-version-tested)。以下命令在此版本范围内均适用。0.30+ 新增了agent-browser read(提取便于 Agent 阅读的文本),以及--restore/--namespace会话恢复工作流,可在多次 Agent 运行之间稳定、隔离地保持浏览器状态。0.33.0 新增了agent-browser a11y [url],这是一项内置的 axe-core 审计功能(支持 WCAG 标签筛选、选择器范围限定,以及支持 iframe 的文本/JSON 输出),既可作为 CLI 命令使用,也可作为 MCP 工具使用。0.34.0 新增了共享 Chrome 会话的持久会话到标签页绑定;使用--pin-tab可将绑定设为严格模式,这样外部关闭标签页时会返回稳定的tab_gone错误。0.35.0 新增了--ca-cert <path>,用于在 SSL 检查代理后信任本地 CA(仅限 Linux;这是--ignore-https-errors的针对性替代方案)。0.35.1 更改了下方 Diff 行所依赖的行为:diff snapshot现在会为每次 diff 重置 ref 编号,并在页面导航时使 ref 失效,因此导航前捕获的 ref 必须重新读取,不能重复使用。0.36.0 新增了实验性的 WebMCP 工具发现功能(webmcp list/invoke/result/cancel,在open时提供简短的不可信目录摘要)。0.37.0 使record以 30 fps 捕获当前活动页面,并将会话设置继承到新标签页。0.38.0 新增了snapshot --delta(先提供完整基线,之后提供unchanged或精简的结构变更)、screenshot --if-changed --threshold、可在同一文档 DOM 变更后继续使用的持久 ref、--human/--input-mode指针拟真,以及auth login --no-navigate。完整命令列表:references/upstream.md和agent-browser skills get core --full。
| 区域 | 命令 | 说明 |
|---|---|---|
| Snapshot | agent-browser snapshot -i | ARIA 树,带有 @eN ref。-C/--cursor 已在 0.22 中移除 |
| Snapshot delta (0.38+) | agent-browser snapshot --delta | 首次提供完整树,之后提供 unchanged 或精简的结构差异;同一文档变更后 ref 仍有效 |
| 语义定位器 | agent-browser find role button click --name "Continue" | 语法:find <locator> <value> [action];是 @eN ref 的稳定替代方案 |
| 交互 | fill @e1 "..."、click @e2、press Enter、drag @e1 @e2、upload @e1 file.pdf | 均使用 ARIA ref。添加 --human 可采用曲线移动,而不是瞬间跳转 |
| 等待 | wait --load networkidle、wait --text "Success"、wait --fn "window.ready" | 由事件驱动,不使用基于休眠的等待 |
| 网络 | network route "*analytics*" --abort、network route "https://api/*" --body '{...}' | 拦截并模拟响应 |
| 状态 | state save/load auth.json、--session-name <name> | 在多次运行之间保留身份验证状态 |
| Vault | vault store github_pat、vault load github_pat | 加密凭据存储 |
| Diff | diff snapshot、diff screenshot --baseline /tmp/x.png | 对不同运行之间的 ARIA 和像素进行差异比较;在同一次运行中比较同一页面的前后状态时,优先使用 snapshot --delta |
| 捕获 | screenshot --annotate、screenshot --if-changed --threshold <n> (0.38+)、pdf、record start/stop --fps <n> | 对未变化的截图,--if-changed 会跳过重新编码;record 默认为 30 fps(0.37+) |
| 控制面板 | agent-browser dashboard start (0.25+) | 浏览器端运行时检查器,地址为 :4848 |
运行测试计划
expect_task = Agent(
subagent_type="ork:expect-agent",
prompt=f"""Execute this test plan:
{test_plan}
For each step:
1. Navigate to the URL
2. Execute the test action
3. Take a screenshot on failure
4. Report PASS/FAIL with evidence
""",
run_in_background=True,
model="sonnet",
max_turns=50
)
# Stream agent-browser progress line-by-line instead of polling (CC 2.1.98+)
# Each stdout line from agent-browser arrives as a notification — useful for
# catching a failing step early rather than waiting for the full plan.
# Full pattern: Read("${CLAUDE_PLUGIN_ROOT}/skills/chain-patterns/references/monitor-patterns.md")
Monitor(pid=expect_task.agent_id)
# For long test plans (>3 min typical), notify on completion — requires
# Remote Control + "Push when Claude decides" config (CC 2.1.110+).
# Skip silently if the user doesn't have Remote Control enabled.
if test_plan_duration_estimate > 180:
PushNotification(
message=f"ork:expect complete — {passed}/{total} steps passed on {len(affected_urls)} pages",
status="proactive"
)
加载:Read("references/execution.md")
阶段 6:报告
expect Report
═══════════════════════════════════════
Target: unstaged (3 files changed)
Pages tested: 4
Duration: 45s
Results:
✓ /login — form renders, submit works
✓ /signup — validation triggers on empty fields
✗ /dashboard — chart component crashes (TypeError)
✓ /settings — preferences save correctly
3 passed, 1 failed
Artifacts:
.expect/reports/2026-03-26T16-30-00.json
.expect/screenshots/dashboard-error.png
加载:Read("references/report.md")
已保存的流程
存储在 .expect/flows/ 中的可复用测试序列:
# .expect/flows/login.yaml
name: Login Flow
steps:
- navigate: /login
- fill: { selector: "#email", value: "[email protected]" }
- fill: { selector: "#password", value: "password123" }
- click: button[type="submit"]
- assert: { url: "/dashboard" }
- assert: { text: "Welcome back" }
运行方式:expect --flow login
UI 编辑后自动触发(M125 #2)
当开发栈已启动(dev)时,保存任何 .tsx、.jsx、.css 或 .scss 文件(以及 app/**/page.tsx、pages/**/*.tsx 等 Next.js 路由文件)都会发出提示,建议运行 expect <route>。此钩子(posttool/ui-change-detector)默认启用,并且:
- 如果
dev尚未启动,则静默跳过(没有可供连接的 agent-browser 会话); - 对每个路由强制执行 30 秒冷却时间,以防快速保存时产生垃圾信息;
- 将
.claude/state/expect-skip.<sessionId>作为每个会话的选择退出标记(写入任意内容即可); - 将
ORK_EXPECT_AUTO=0作为环境级紧急开关。
路由解析:app/dashboard/page.tsx → /dashboard,pages/settings.tsx → /settings,组件 / 全局样式编辑 → /(以主页作为代理)。app/(marketing)/pricing/page.tsx 等路由组会被剥离,解析为 /pricing。
ARIA 快照录制(M125 #6)
运行通过后,posttool/expect/snapshot-recorder 钩子会将捕获的 ARIA 树保存到 .claude/state/expect-snapshots/<route-slug>/<parent-commit>.json。之后运行 expect <route> --diff 时,会与该路由最近的前一份快照进行比较,无需基准截图即可发现结构性回归(按钮增删、标签变更、层级变化)。
要触发快照录制器,expect 运行输出必须包含 RUN_COMPLETED|passed、ROUTE|<route> 和 ARIA|<json-summary> 标签。由 agent-browser 驱动的流程已会输出这些标签。
Jev 步骤判定器(选择启用,三种模式)
ORK_EXPECT_JEV 用于选择模式。未设置或值为 false 时,功能关闭;关闭时不会产生任何网络请求。shadow(兼容模式下,设置为 truthy 的 ORK_EXPECT_JEV_SHADOW 也会启用)会在 Agent 的选择旁为每个步骤记录一次 Jev 请求:从交互式 ARIA 元素构建的有界合法操作集合中进行 Choice,并附带三个 verify Noul,同时在报告中记录概率和每步的一致性。设置为 1 或 act 时,由 Jev 选择并驱动该步骤;遇到任何错误、超时、空答案或格式错误的答案,或置信度低于 act_confidence_floor 时,会安全回退到 Agent 自己的选择。可调参数位于 .expect/config.yaml 的 jev_shadow: 中。加载:Read("references/jev-shadow.md")
不适用场景
- 单元测试 — 改用
cover - 仅 API 的变更 — 没有需要测试的浏览器 UI
- 生成文件 — 跳过构建产物和锁定文件
- 仅文档变更 — 除非你想验证文档站点的渲染效果
质量标准
满足以下所有条件才算完成:
- 测试计划的范围根据所选
--target的 git diff 确定 — 计划中不会出现未受影响的页面。 - 每个变更文件都至少对应一个经过测试的路由(通过
.expect/config.yaml或推断约定),或被明确排除为仅 API、生成文件或仅文档。 - 首先运行指纹检查;如果与上次运行相比 diff 未变化,则跳过执行,不重复测试。
- 除非传入
-y,否则在运行任何浏览器操作前先展示计划以供审核。 - 每个已执行步骤都会报告 PASS 或 FAIL,并附带证据(失败时提供截图);报告中的通过 / 失败总数与实际运行的步骤一致。
- 只要有任何步骤失败,报告的退出代码就为非零。
预算:总计最多 30 分钟,每个页面最多 5 分钟(较慢的页面会被跳过,详见 references/test-plan.md;默认值及 .expect/config.yaml 覆盖项),最多 50 个执行器轮次(max_turns=50),只运行一个顺序执行的 ork:expect-agent,绝不并行运行浏览器(rules/no-parallel-browsers.md);到达截止时间或触及第一个上限时立即停止并报告部分结果。
相关技能
agent-browser— 浏览器自动化引擎(必需依赖)ork:cover— 测试套件生成(单元 / 集成 / 端到端)ork:verify— 评估现有测试质量testing-e2e— Playwright 模式与最佳实践
参考资料
使用 Read("references/<file>") 按需加载:
| 文件 | 内容 |
|---|---|
fingerprint.md | SHA-256 门控逻辑 |
diff-scanner.md | Git 差异解析 + 3 级分类 |
route-map.md | 文件到 URL 的映射约定 |
test-plan.md | AI 测试计划生成提示词模板 |
execution.md | agent-browser 编排模式 |
report.md | 报告格式 + 产物存储 |
config-schema.md | .expect/config.yaml 完整架构 |
aria-diffing.md | 用于语义差异的 ARIA 快照比较 |
scope-strategy.md | 按目标模式制定的测试深度策略 |
saved-flows.md | Markdown+YAML 流程格式、自适应重放 |
rrweb-recording.md | rrweb DOM 重放集成 |
human-review.md | AskUserQuestion 计划审核关卡 |
ci-integration.md | GitHub Actions 工作流 + 推送前钩子 |
research.md | millionco/expect 架构分析 |
jev-shadow.md | 按步骤启用 Jev 步骤判定器:有界操作 Choice + 验证 Nouls;影子日志,act 驱动并在失败时关闭回退 |
版本: 1.0.0(2026 年 3 月)—— 初始脚手架,M99 里程碑