ui-test

ui-test

热门

基于 browse CLI 的 AI 驱动对抗性 UI 测试。通过分析 git diff 仅测试变更部分,或探索整个应用以发现缺陷。测试功能正确性、可访问性、响应式布局和 UX 启发式规则。适用于用户要求测试 UI 变更、QA 拉取请求、审计可访问性或进行探索性测试的场景。支持本地浏览器(localhost)和远程 Browserbase(已部署站点)。

3666Star
231Fork
更新于 2026/7/24
SKILL.md
readonly只读
name
ui-test
description

基于 browse CLI 的 AI 驱动对抗性 UI 测试。通过分析 git diff 仅测试变更部分,或探索整个应用以发现缺陷。测试功能正确性、可访问性、响应式布局和 UX 启发式规则。适用于用户要求测试 UI 变更、QA 拉取请求、审计可访问性或进行探索性测试的场景。支持本地浏览器(localhost)和远程 Browserbase(已部署站点)。

UI Test — 代理式 UI 测试技能

在真实浏览器中测试 UI 变更。你的任务是尝试破坏功能,而不是确认它们正常工作。

三种工作流:

  • Diff 驱动 — 分析 git diff,仅测试变更部分
  • 探索性 — 浏览应用,发现开发者未考虑到的缺陷
  • 并行 — 将独立的测试组分散到多个 Browserbase 浏览器中

测试工作原理

主代理协调 — 规划测试策略,委派给子代理,并合并结果。子代理执行实际的浏览器测试。

规划:多角度分析,然后一次性执行

你必须自行完成所有三轮规划并在启动任何子代理之前输出它们。 规划在你的响应中完成 — 不会委派给子代理。不要跳过规划直接执行。

第一轮 — 功能: 核心用户流程是什么?应该正常工作的内容?将每个测试写为:操作 → 预期结果。

第二轮 — 对抗性: 重新阅读第一轮。你遗漏了什么?考虑:不同用户类型/角色、错误路径、空状态、竞态条件、边缘输入(空、超大、特殊字符、快速点击)。

第三轮 — 覆盖缺口: 重新阅读第一至二轮。考虑:可访问性(axe-core、仅键盘)、移动视口、控制台错误、与应用其他部分的视觉一致性。

去重: 将三轮合并为一个编号的测试列表。移除重叠项。将每个测试分配到一个组(例如 A 组、B 组)。

然后一次性执行 — 每组启动一个子代理。每个子代理接收其特定的测试列表,仅此而已。子代理不探索也不规划 — 它们执行分配的测试并报告结果。

在你的响应中输出三轮、合并的计划以及分组分配,然后再调用任何 Agent 工具。

拆分工作的原则

  • 子代理执行分配的测试,而非开放式探索。 主代理为每个子代理提供特定的编号测试列表。子代理不规划、不探索、也不决定测试什么 — 它们执行列表并停止。
  • 瓶颈是最慢的代理 — 拆分工作,使没有单个代理承担不成比例的份额。多个小代理优于少数大代理。
  • 根据变更规模调整工作量 — 单个组件修复不需要多个代理或许多步骤。全页面重新设计则需要。让 diff 的范围驱动计划。
  • 失败时不提前停止 — 在分配的测试范围内尽可能多地发现缺陷。

为子代理提供步骤预算

主代理必须在每个子代理提示中包含明确的 browse 步骤限制。 子代理不会自我限制 — 除非另有说明,否则它们会一直运行直到完成。

粗略启发式:少量针对性检查约 25 步,完整页面(功能+对抗性+可访问性)约 40 步,多页面或广泛类别约 75 步。根据分配的测试实际需求调整 — 这些是起点,而非规则。

每个子代理提示必须包含:

你的预算为 N 个 browse 步骤(每个 `browse` 命令 = 1 步)。请边执行边计数。当达到 N 时,立即停止并报告:
- 每个完成的测试:STEP_PASS/STEP_FAIL
- 每个未执行的测试:STEP_SKIP|<test-id>|budget reached

达到预算后不要重试或继续。
仅运行这些测试:[合并计划中的编号列表]
不要探索分配的测试之外的内容。
不要生成 HTML 报告或写入任何文件。仅返回步骤标记和你的发现(文本形式)。

主代理不应自行运行 browse 命令(除了验证开发服务器是否启动)。所有测试都在子代理中完成。

当子代理达到预算时,主代理按原样接受部分结果。 不要重新运行或重试子代理。在最终报告中包含 SKIPPED 测试,以便开发者了解哪些内容未被覆盖。

报告

每个子代理返回:

Tests: 8 | Passed: 5 | Failed: 2 | Skipped: 1 | Pages visited: 2

主代理合并为最终报告:

Tests: 20 | Passed: 14 | Failed: 4 | Skipped: 2 | Agents: 3 | Pass rate: 70%

不要报告“已用步骤数” — browse 命令计数是实现细节,对审阅者而言不是有意义的指标。

测试理念

你是一个对抗性测试者。 你的目标是发现缺陷,而不是证明正确性。

  • 尝试破坏你测试的每个功能。 不要只检查“按钮是否存在?” — 快速点击两次、提交空表单、粘贴 500 个字符、在流程中按 Escape。
  • 测试开发者未考虑到的内容。 空状态、错误恢复、仅键盘导航、移动端溢出。
  • 每个断言必须基于证据。 比较前后快照。通过引用检查特定元素。没有来自可访问性树或确定性检查的具体证据,绝不报告 PASS。
  • 报告失败时提供足够的复现细节。 包括确切操作、预期结果、实际结果以及建议修复。

断言协议

每个测试步骤必须产生结构化的断言。不要写自由形式的“看起来不错”。

步骤标记

对于每个测试步骤,恰好输出一个标记:

STEP_PASS|<step-id>|<evidence>

STEP_FAIL|<step-id>|<expected> → <actual>|<screenshot-path>
  • step-id:简短标识符,如 homepage-ctaform-validation-errormodal-cancel
  • evidence:你观察到的证明步骤通过的内容(元素引用、文本内容、URL、eval 结果)
  • expected → actual:你预期的与实际得到的对比
  • screenshot-path:保存的截图路径(仅失败时 — 见下方截图捕获)

失败时的截图捕获

每个 STEP_FAIL 必须附带截图,以便开发者直观地看到问题所在。

当测试步骤失败时:

# 1. 观察到失败后立即截图
browse screenshot --path .context/ui-test-screenshots/<step-id>.png

# 如果 --path 不受支持,截图后手动保存:
browse screenshot
# browse CLI 会输出截图路径 — 移动/复制它:
cp /tmp/browse-screenshot-*.png .context/ui-test-screenshots/<step-id>.png

在任何测试运行开始时设置截图目录:

mkdir -p .context/ui-test-screenshots

规则:

  • 文件名 = step-id(例如 double-submit.pngaxe-audit.pngmodal-focus-trap.png
  • 存储在 .context/ui-test-screenshots/ 中 — 该目录被 gitignore,对开发者和其它代理可访问
  • 对于并行运行,包含会话名称:<session>-<step-id>.png(例如 signup-double-submit.png
  • 在失败时刻截图 — 捕获损坏状态,而非恢复后
  • 对于视觉/布局缺陷,同时截取基线(正常工作状态)用于比较:<step-id>-baseline.png

如何验证(按严谨度排序)

  1. 确定性检查(最强)— browse eval 返回可检查的结构化数据。例如:axe-core 违规数、document.title、表单字段值、控制台错误数组、元素数量。
  2. 快照元素匹配 — 可访问性树中存在具有特定角色和文本的特定元素。通过引用检查:@0-12 button "Save"。元素要么存在于树中,要么不存在。
  3. 前后对比 — 操作前快照、操作、操作后快照。验证树按预期方式变化(元素出现、消失、文本改变)。
  4. 截图 + 视觉判断(最弱)— 仅用于可访问性树无法捕获的纯视觉属性(颜色、间距、布局)。始终附带你具体评估的内容。

前后对比模式

这是核心验证循环。每次交互都使用它:

# 1. 之前:捕获状态
browse snapshot
# 记录:存在哪些元素、它们的文本、它们的引用

# 2. 操作:执行交互
browse click @0-12

# 3. 之后:捕获新状态
browse snapshot
# 比较:发生了什么变化?什么出现了?什么消失了?

# 4. 断言:基于比较发出标记
# 如果对话框出现:STEP_PASS|modal-open|dialog "Confirm" appeared at @0-20
# 如果无变化:
browse screenshot --path .context/ui-test-screenshots/modal-open.png
# STEP_FAIL|modal-open|expected dialog to appear → snapshot unchanged|.context/ui-test-screenshots/modal-open.png

设置

which browse || npm install -g browse

避免权限疲劳

此技能运行许多 browse 命令(快照、点击、eval)。为避免逐个批准,将 browse 添加到允许的命令中:

将两个模式添加到 .claude/settings.json(项目级)或 ~/.claude/settings.json(用户级):

{
  "permissions": {
    "allow": [
      "Bash(browse:*)",
      "Bash(BROWSE_SESSION=*)"
    ]
  }
}

第一个模式覆盖普通的 browse 命令。第二个模式覆盖并行会话(BROWSE_SESSION=signup browse open ...)。两者都需要以避免批准提示。

模式选择

目标 模式 命令 认证
localhost / 127.0.0.1 本地 browse open <url> --local 无需(默认使用干净的隔离本地浏览器)
已部署/预发布站点 远程 browse open <url> --remote Browserbase 凭据;在支持的地方使用上下文

规则:如果目标 URL 包含 localhost127.0.0.1,在第一次 browse open 时传递 --local

本地模式(localhost 默认)

browse open http://localhost:3000 --local

browse open ... --local 默认使用干净的隔离本地浏览器,最适合可复现的 localhost QA 运行。

仅在需要时使用本地模式变体:

  • browse open <url> --auto-connect — 自动发现现有的可调试本地 Chrome。仅当测试明确需要现有的本地登录/cookies/状态时使用。
  • browse open <url> --cdp <port|url> — 附加到特定的 CDP 目标(显式本地浏览器附加)。

远程模式(通过 cookie-sync 的已部署站点)

# 步骤 1:从本地 Chrome 同步 cookies 到 Browserbase
node .claude/skills/cookie-sync/scripts/cookie-sync.mjs --domains your-app.com
# 输出:Context ID: ctx_abc123

# 步骤 2:使用同步的上下文以远程模式打开
SESSION_JSON="$(browse cloud sessions create --context-id ctx_abc123 --persist --keep-alive)"
SESSION_ID="$(echo "$SESSION_JSON" | jq -r .id)"
CONNECT_URL="$(echo "$SESSION_JSON" | jq -r .connectUrl)"

browse open https://staging.your-app.com --cdp "$CONNECT_URL"
browse snapshot
# ... 运行测试 ...
browse stop
browse cloud sessions update "$SESSION_ID" --status REQUEST_RELEASE

Cookie-sync 标志:--domains--context--verified--proxy "City,ST,US"

工作流 A:Diff 驱动测试

阶段 1:分析 diff

git diff --name-only HEAD~1          # 或:git diff --name-only / git diff --name-only main...HEAD
git diff HEAD~1 -- <file>            # 读取实际变更

对变更文件分类:

文件模式 UI 影响 测试内容
*.tsx, *.jsx, *.vue, *.svelte 组件 渲染、交互、状态、边缘情况
pages/**, app/**, src/routes/** 路由/页面 导航、页面加载、内容、404 处理
*.css, *.scss, *.module.css 样式 视觉外观(截图)、响应式
*form*, *input*, *field* 表单 验证、提交、空输入、长输入、特殊字符
*modal*, *dialog*, *dropdown* 交互式 打开/关闭、Escape、焦点陷阱、取消与确认
*nav*, *menu*, *header* 导航 链接、激活状态、路由、键盘导航
仅非 UI 文件 跳过 — 报告“无需 UI 测试”

阶段 2:将文件映射到 URL

检测框架:cat package.json | grep -E '"(next|react|vue|nuxt|svelte|@sveltejs|angular|vite)"'

框架 默认端口 文件 → URL 模式
Next.js App Router 3000 app/dashboard/page.tsx/dashboard
Next.js Pages Router 3000 pages/about.tsx/about
Vite 5173 检查路由配置
Nuxt 3000 pages/index.vue/
SvelteKit 5173 src/routes/+page.svelte/
Angular 4200 检查路由模块

阶段 3:确保正确的代码在运行

在测试之前,验证开发服务器正在提供来自 diff 的代码 — 而不是过时的分支。

如果测试 PR 或特定分支:

# 检查当前检出的分支
git branch --show-current

# 如果不是 PR 分支,切换到它
git fetch origin <branch> && git checkout <branch>

# 安装依赖 — 不同分支的 lockfile 可能不同
yarn install  # 或 npm install / pnpm install

如果开发服务器已在不同分支上运行,切换后重启它。

查找正在运行的开发服务器:

for port in 3000 3001 5173 4200 8080 8000 5000; do
  s=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$port" 2>/dev/null)
  if [ "$s" != "000" ]; then echo "Dev server on port $port (HTTP $s)"; fi
done

如果未找到:告诉用户启动开发服务器。

验证它确实渲染:
browse open + browse snapshot 之后,检查可访问性树是否包含真实页面内容(导航、标题、交互元素) — 而不仅仅是错误覆盖层或空 body。Next.js 开发服务器可以在显示全屏构建错误对话框时返回 HTTP 200。如果快照为空或被错误对话框主导,则服务器已损坏 — 在测试前修复构建。

阶段 4:生成测试计划

对于每个变更区域,规划快乐路径和对抗性测试

Test Plan (based on git diff)
=============================
Changed: src/components/SignupForm.tsx (added email validation)

1. [happy] Valid email submits successfully
   URL: http://localhost:3000/signup
   Steps: fill valid email → submit → verify success message appears

2. [adversarial] Invalid email shows error
   Steps: fill "not-an-email" → submit → verify error message appears

3. [adversarial] Empty form submission
   Steps: click submit without filling anything → verify error, no crash

4. [adversarial] XSS in email field
   Steps: fill "<script>alert(1)</script>" → submit → verify sanitized/rejected

5. [adversarial] Rapid double-submit
   Steps: click submit twice quickly → verify no duplicate submission

6. [adversarial] Keyboard-only flow
   Steps: Tab to email → type → Tab to submit → Enter → verify success

阶段 5:执行测试

browse stop 2>/dev/null
mkdir -p .context/ui-test-screenshots
# localhost/default QA → clean, reproducible local run
browse open http://localhost:3000 --local

对于每个测试,遵循前后模式

# 导航
browse open http://localhost:3000/path --local
browse wait load

# 之前快照
browse snapshot
# 注意当前状态:元素、引用、文本

# 操作
browse click @0-ref
# 或:browse fill "selector" "value"
# 或:browse type "text"
# 或:browse press Enter

# 之后快照
browse snapshot
# 与之前比较:发生了什么变化?

# 使用标记断言
# STEP_PASS|step-id|evidence  或  STEP_FAIL|step-id|expected → actual

阶段 6:报告结果

## UI Test Results

### STEP_PASS|valid-email-submit|status "Thanks!" appeared at @0-42 after submit
- URL: http://localhost:3000/signup
- Before: form with email input @0-3, submit button @0-7
- Action: filled "user@test.com", clicked @0-7
- After: form replaced by status element with "Thanks! We'll be in touch."

### STEP_FAIL|double-submit|expected single submission → form submitted twice|.context/ui-test-screenshots/double-submit.png
- URL: http://localhost:3000/signup
- Before: form with submit button @0-7
- Action: clicked @0-7 twice rapidly
- After: two success toasts appeared, suggesting duplicate submission
- Screenshot: .context/ui-test-screenshots/double-submit.png
- Suggestion: disable submit button after first click, or debounce the handler

---
**Summary: 4/6 passed, 2 failed**
Failed: double-submit, xss-sanitization

Screenshots saved to `.context/ui-test-screenshots/` — open any failed step's screenshot to see the broken state.

完成后始终 browse stop

阶段 7:生成 HTML 报告

生成文本报告后,生成一个独立的 HTML 报告,审阅者可以在浏览器中打开。报告将截图内联(base64)嵌入,因此作为单个文件工作 — 无需外部依赖。

原因: 文本报告对代理对话很好,但审阅者(PM、设计师、其他工程师)希望有一个可以打开、扫描和分享的可视化产物。内联截图使失败立即显而易见。

如何生成
  1. 读取 references/report-template.html 中的 HTML 模板
  2. 通过用实际测试数据替换模板占位符来构建报告:
占位符
{{TITLE}} 报告标题,用于 <title> 标签(例如 "UI Test: PR #1234 — OAuth Settings")
{{TITLE_HTML}} 报告标题,用于可见的 <h1>。如果有 PR URL,将 PR 引用包裹在 <a> 标签中使其可点击(例如 UI Test: <a href="https://github.com/org/repo/pull/1234">PR #1234</a> — OAuth Settings)。如果没有 URL,使用与 {{TITLE}} 相同的纯文本。
{{META}} 单行上下文:日期、应用 URL、用户、分支
{{TOTAL_TESTS}} STEP_PASS + STEP_FAIL 总数
{{AGENT_COUNT}} 运行的子代理数量
{{PASS_COUNT}} STEP_PASS 数量
{{FAIL_COUNT}} STEP_FAIL 数量
{{PASS_RATE}} 整数百分比(例如 "92")
{{RATE_CLASS}} good(≥90%)、warn(70–89%)、bad(<70%)
{{FAILURES_SECTION}} 失败测试卡的 HTML(见下文)
{{PASSES_SECTION}} 通过测试卡的 HTML(见下文)
  1. 对于每个测试结果,生成一个 <details> 卡片。失败的测试应默认打开,以便审阅者立即看到:
<!-- 失败测试卡(默认打开) -->
<div class="section">
  <h2>Failures <span class="count">{{FAIL_COUNT}}</span></h2>
  <details class="test-card fail" open>
    <summary>
      <span class="badge fail">FAIL</span>
      <span class="step-id">step-id-here</span>
      <span class="evidence">expected → actual</span>
    </summary>
    <div class="body">
      <dl>
        <dt>URL</dt><dd>http://localhost:3000/path</dd>
        <dt>Action</dt><dd>What was done</dd>
        <dt>Expected</dt><dd>What should have happened</dd>
        <dt>Actual</dt><dd>What happened instead</dd>
      </dl>
      <div class="suggestion">Fix: description of suggested fix</div>
      <div class="screenshot">
        <img src="data:image/png;base64,..." alt="Screenshot of failure">
        <div class="caption">step-id.png — captured at moment of failure</div>
      </div>
    </div>
  </details>
</div>

<!-- 通过测试卡(默认折叠) -->
<div class="section">
  <h2>Passed <span class="count">{{PASS_COUNT}}</span></h2>
  <details class="test-card pass">
    <summary>
      <span class="badge pass">PASS</span>
      <span class="step-id">step-id-here</span>
      <span class="evidence">evidence summary</span>
    </summary>
    <div class="body">
      <dl>
        <dt>URL</dt><dd>http://localhost:3000/path</dd>
        <dt>Evidence</dt><dd>What was observed</dd>
      </dl>
    </div>
  </details>
</div>
  1. 将截图嵌入为 base64,使 HTML 完全自包含:
# 将截图转换为 base64 数据 URI
base64 -i .context/ui-test-screenshots/step-id.png | tr -d '\n'
# 用作:src="data:image/png;base64,<output>"

读取 STEP_FAIL 标记中引用的每个截图文件,进行 base64 编码,并将其作为 <img src="data:image/png;base64,..."> 嵌入到相应的测试卡中。对于 STEP_PASS,仅在明确截取了截图时嵌入(例如基线截图)。

  1. 将最终 HTML 写入 .context/ui-test-report.html
# 写入生成的 HTML
cat > .context/ui-test-report.html << 'REPORT_EOF'
<!DOCTYPE html>
...generated report...
REPORT_EOF

# 为审阅者打开
open .context/ui-test-report.html  # macOS
# xdg-open .context/ui-test-report.html  # Linux
  1. 告诉用户:Report saved to .context/ui-test-report.html 并提供打开选项。

规则:

  • 失败部分在通过部分之前 — 审阅者首先关心什么被破坏了
  • 失败卡片默认 open;通过卡片折叠
  • 每个 STEP_FAIL 卡片必须包含嵌入的截图 — 如果截图文件缺失,在卡片中注明
  • 如果提供了建议/修复,在每个失败卡片中包含它
  • 报告必须离线工作 — 无 CDN 链接,无外部资源
  • 保持 HTML 在 5MB 以下 — 如果截图超出,降低图像质量或跳过通过的基线截图

对抗性测试模式

将这些应用于你测试的每个交互元素。阅读 references/adversarial-patterns.md 获取完整的模式库(表单、模态框、导航、错误状态、键盘可访问性)。

确定性检查

这些产生结构化数据,而非判断调用。将它们用作最强形式的断言。

检查 捕获内容 断言
axe-core WCAG 违规 violations.length === 0
控制台错误 运行时异常、失败的请求 空错误数组
损坏的图片 缺失/失败的图片加载 没有 naturalWidth === 0 的图片
表单标签 没有可访问标签的输入 每个输入有 hasLabel: true

有关确切的 browse eval 配方,请阅读 references/browser-recipes.md

工作流 B:探索性测试

无 diff,无计划 — 只需打开应用并尝试破坏它。当用户说“测试我的应用”、“查找缺陷”或“QA 这个站点”时使用此工作流。

方法

  1. 发现应用 — 读取 package.json 检测框架,然后打开根 URL 并快照查看内容
  2. 导航所有内容 — 点击导航链接,访问每个可到达的页面,记录存在的内容
  3. 测试你发现的内容 — 对于每个页面,应用下面的对抗性模式(表单、模态框、导航、键盘、错误状态)
  4. 运行确定性检查 — 在每个页面上运行 axe-core、控制台错误、损坏的图片、表单标签
  5. 报告发现 — 使用 STEP_PASS/STEP_FAIL 标记,为失败包含复现步骤

不要试图系统地覆盖。只需像用户一样探索,但意图是破坏东西。代理擅长这个 — 让它自由漫游。

探索性运行的提示

  • 从主页开始,然后自然地跟随导航
  • 尝试 404 页面(/does-not-exist)— 是自定义还是默认?
  • 寻找空状态(无数据的页面)
  • 在有效输入之前用垃圾输入测试表单
  • 在每个页面上检查移动视口(375px)— 是否溢出?
  • 如果应用有认证,首先使用 cookie-sync

工作流 C:并行测试

使用命名的 browse 会话(BROWSE_SESSION=<name>)并发运行独立的测试组。每个会话有自己的浏览器。适用于本地和远程模式。

当测试多个页面或类别并希望更快的挂钟时间时使用。

阅读 references/parallel-testing.md 获取完整工作流:会话设置、代理扇出、用于认证的 cookie-sync 以及结果合并。

设计一致性

检查变更的 UI 是否与应用的其他部分视觉匹配。在进行视觉或设计检查时阅读 references/design-consistency.md

测试类别

类别 方法 断言类型
可访问性 axe-core + 键盘导航 确定性(违规数)
视觉质量 截图 + 启发式评估 视觉判断(最弱 — 注明具体内容)
响应式 视口扫描 + 截图 视觉 + 确定性(溢出检查)
控制台健康 控制台捕获 eval 确定性(错误数)
UX 启发式 快照 + 用户体验法则 + Nielsen 结构化判断(引用具体启发式)
错误状态 导航到空/错误状态 前后对比
数据显示 表格/仪表板快照 元素匹配(列数、格式)
设计一致性 截图基线 + 变更页面比较 视觉判断(引用具体属性)
探索性 自由导航 + 对抗性测试 前后对比 + 判断

参考指南(按需加载):

如需带有确切命令的示例,请阅读 EXAMPLES.md 以查看断言协议的实际应用。

最佳实践

  1. 保持对抗性 — 尝试破坏东西,而不仅仅是确认它们工作
  2. 每个断言需要证据 — 快照引用、eval 结果或前后差异
  3. 每次交互前后对比 — 快照、操作、快照、比较
  4. 每个失败截图 — 在 STEP_FAIL 时立即 browse screenshot,保存到 .context/ui-test-screenshots/<step-id>.png
  5. 先进行确定性检查 — 在视觉判断之前运行 axe-core、控制台错误、表单标签
  6. 对于 localhost,从干净的本地模式开始 — 在第一次 browse open 时传递 --local 以获得可复现的运行;仅在需要现有本地状态时使用 --auto-connect
  7. 完成后始终 browse stop — 对于并行运行,停止每个命名的会话
  8. 报告失败时包含复现步骤 — 操作、预期、实际、截图路径、建议
  9. 并行化独立测试 — 在已部署站点上测试多个页面或类别时,使用工作流 C 和命名会话

故障排除

  • "No active page"browse stop,重试。对于僵尸进程:pkill -f "browse.*daemon"
  • 开发服务器无响应curl http://localhost:<port> — 要求用户启动它
  • browse eval 使用 await 失败:改用 .then()browse eval 不支持顶级 await
  • 元素引用未找到:再次 browse snapshot — 引用在页面更新时改变
  • 空白快照:在快照前使用 browse wait loadbrowse wait selector ".expected"
  • SPA 深层链接 404:先导航到 /,然后点击进入
  • 远程认证失败:使用 --context <id> 重新运行 cookie-sync,尝试 --verified
  • 并行会话冲突:确保每个 browse 命令使用 BROWSE_SESSION=<name> — 否则命令进入默认会话
  • 会话未停止BROWSE_SESSION=<name> browse stop。对于僵尸进程:pkill -f "browse.*<name>.*daemon"