关于 Matware E2e Runner
Matware E2e Runner 是面向开发团队的端到端网页测试工具,测试以 JSON 描述,并可由 AI 创建、执行和调试。它提供并行浏览器池、实时仪表板及 GitHub/GitLab 问题到测试的流程。
主要功能
- 01用 JSON 编写测试流程
- 02通过 MCP 创建并运行测试
- 03并行执行浏览器测试
- 04截图与实时仪表板
- 05从问题生成并验证测试
适用场景
- 01测试登录或结账流程
- 02将 GitHub 或 GitLab 问题转为测试
- 03在 CI 中并行运行测试
MCP 相关
内置 MCP 服务,向 Claude Code 等 MCP 客户端提供 17 个测试创建、运行与调试工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
15收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| all | boolean | 否 | 运行 tests 目录中的所有测试套件 |
| cwd | string | 否 | 项目根目录的绝对路径。Claude Code 应传入其当前工作目录。 |
| file | string | 否 | JSON 测试文件的绝对或相对路径 |
| suite | string | 否 | 要运行的套件名称(例如 "auth"、"01-login")。带或不带数字前缀均可匹配。 |
| baseUrl | string | 否 | 覆盖本次运行的基础 URL |
| retries | number | 否 | 失败测试的重试次数 |
| concurrency | number | 否 | 并行工作进程数(默认值来自配置) |
| failOnNetworkError | boolean | 否 | 网络请求失败时(例如 ERR_CONNECTION_REFUSED)使测试失败。默认值:false。 |
| verificationStrictness | enum | 否 | 视觉验证严格度。strict:不允许有歧义,任何疑问都判定为 FAIL。moderate:合理判断(默认值)。lenient:仅在存在明显矛盾时判定失败。strict | moderate | lenient |
详细介绍
English · Español
@matware/e2e-runner
AI 原生的 E2E 测试运行器,可为你编写、运行和调试测试。
E2E Runner 让你无需编写测试代码即可测试 Web 应用。测试采用纯 JSON 编写——而且你甚至不必亲自编写:只要向 Claude Code 提出要求即可。
🎬 提出要求来编写测试,然后观看它运行
套件运行时的实时仪表板——每个步骤都会实时将截图传送到动态信息流。
借助内置的 MCP 服务,创建测试就像对话一样简单——无需查阅文档,也无需记忆语法:
你: 为登录流程创建一个 E2E 测试并运行它。
Claude Code: 编写测试,在真实浏览器中运行,并向你报告结果—— ✅
login-flow已通过,耗时 2.3s · 截图已保存 · 没有网络错误。
在幕后,Claude 只需编写并运行了以下内容。测试就是 JSON——一份按顺序列出用户操作的清单:
[
{ "name": "login-flow", "actions": [
{ "type": "goto", "value": "/login" },
{ "type": "type", "selector": "#email", "value": "[email protected]" },
{ "type": "type", "selector": "#password", "value": "secret" },
{ "type": "click", "text": "Sign In" },
{ "type": "assert_text", "text": "Welcome back" },
{ "type": "screenshot", "value": "logged-in.png" }
]}
]
无需导入、无需 describe/it,也无需构建步骤。只要看得懂就写得出——或者直接提出要求。
连接到 Claude Code(2 条命令):
claude plugin marketplace add fastslack/mtw-e2e-runner
claude plugin install e2e-runner@matware
现在只需说 "为 X 创建一个测试并运行它"——Claude 就能使用 17 个 MCP 工具、斜杠命令和专用 Agent。
使用其他 Agent(Cursor、Codex、Copilot,以及另外 40 多种)?安装技能:
npx skills add fastslack/mtw-e2e-runner
📖 目录
| 章节 | 内容 | |
|---|---|---|
| 🚀 | 安装与首次测试 | npm 设置 · 使用你自己的 Chrome 运行(无需 Docker)、Obscura 或 Docker 池 |
| ✨ | 功能一览 | 快速了解功能 |
| ✍️ | 编写测试 | 测试格式 · 完整操作目录 · 重试 · 串行 · 模块 · 身份验证 · 钩子 |
| 🤖 | AI 集成 | Claude Code · OpenCode · 17 个 MCP 工具 · 视觉验证 · 从问题创建测试 |
| 📊 | 仪表板与洞察 | 实时仪表板 · 学习系统 · 网络日志 · 截图捕获 |
| 🌐 | 浏览器驱动程序 | browserless · cdp · lightpanda · obscura · steel |
| ⚙️ | CLI、配置与 CI | 命令 · 标志 · e2e.config.js · GitHub Actions · 编程式 API |
🚀 安装 — 小巧轻便
npm install --save-dev @matware/e2e-runner
npx e2e-runner init # scaffolds e2e/ with a sample test + config
然后选择浏览器的运行方式。除非你想使用并行池,否则无需 Docker:
选项 1 · 使用现有的 Chrome — 无需 Docker ⭐
启动任意带有调试端口的 Chromium 浏览器,然后让运行器连接到它:
google-chrome --headless=new --remote-debugging-port=9222 & # or brave / chromium / msedge
CHROME_POOL_URL=http://localhost:9222 POOL_DRIVER=cdp npx e2e-runner run --all
也可以将其写入 e2e.config.js,这样就不必每次重复设置:
export default {
baseUrl: 'http://localhost:3000', // your app — plain localhost, no docker hostname
poolUrls: ['http://localhost:9222'],
poolDriver: 'cdp',
};
除 npm 外无需安装任何东西,并且 baseUrl 只需设置为 localhost(浏览器在你的机器上)。
选项 2 · Obscura — 单个轻量二进制文件,无需 Docker
一个约 30 MB 的二进制文件,内置反检测功能。安装一次,运行它,然后让运行器连接到它:
obscura serve --port 9222 --stealth &
CHROME_POOL_URL=http://localhost:9222 POOL_DRIVER=obscura npx e2e-runner run --all
运行 npx e2e-runner pool start(并在配置中设置 poolDriver: 'obscura')会输出适用于你操作系统的确切安装命令。
选项 3 · Docker 池 — 并行运行,适用于 CI 和大型测试套件
一个共享的队列管理型 Chrome 池,可同时运行多项测试:
npx e2e-runner run --all # the first run auto-starts the Docker pool for you
需要 Docker。设置 baseUrl: 'http://host.docker.internal:3000',这样容器中的 Chrome 才能访问你的应用。
为什么要用 host.docker.internal(仅适用于 Docker 选项)?
使用 Docker 池时,Chrome 在容器内运行,因此其中的 localhost 指的是容器,而不是你的机器。host.docker.internal 可连接到你的主机。在 Linux 上(Docker Engine,而非 Docker Desktop),请添加 --add-host=host.docker.internal:host-gateway,或使用你的局域网 IP。选项 1 和 2 没有这个问题——浏览器在本地运行,因此直接使用 localhost 即可。
编写你的第一个测试
打开 e2e/tests/sample.json — 一个流程是按顺序排列的操作列表:
[
{ "name": "homepage loads", "actions": [
{ "type": "goto", "value": "/" },
{ "type": "assert_text", "text": "Welcome" },
{ "type": "screenshot", "value": "home.png" }
]}
]
使用 npx e2e-runner run --all 运行。结果——通过/失败、耗时、屏幕截图、网络错误——会显示在终端中;如果网页控制面板已打开,也会显示在那里。
添加 OpenCode(可选)
cp node_modules/@matware/e2e-runner/opencode.json ./
mkdir -p .opencode && cp -r node_modules/@matware/e2e-runner/.opencode/* .opencode/
详情请参阅 OPENCODE.md。
更新
每种安装方式都需单独更新 — 更新你使用的方式:
# npm dependency (per project)
npm install --save-dev @matware/e2e-runner@latest
# Claude Code plugin
claude plugin update e2e-runner@matware
# MCP-only install (npx caches the package — pin @latest to force a refresh)
claude mcp add --transport stdio --scope user e2e-runner \
-- npx -y -p @matware/e2e-runner@latest e2e-runner-mcp
说明
有两个注意事项:(1)
npx优先使用项目node_modules中的副本,而不是自己的缓存 — 如果项目固定使用旧版本,MCP 服务和控制面板也会运行旧版本,因此还要更新项目依赖项。(2) 已在运行的进程会将旧代码保留在内存中:更新后,重启控制面板并重新连接 MCP 服务(/mcp→e2e-runner→ 重新连接,或重启会话)。
✨ 你将获得
🧪 零代码测试 — 使用团队中的任何人都能读写的 JSON 文件。无需 JavaScript,无需编译,也不会被某个框架绑定。
🤖 AI 驱动的测试 — Claude Code 可通过 17 个 MCP 工具原生创建、执行和调试测试。只需让它“测试结账流程”,它就会生成 JSON、运行测试并汇报结果。
🐛 从问题到测试的工作流 — 粘贴 GitHub 或 GitLab 问题的 URL。运行器会获取问题内容、生成端到端测试、运行测试,并告诉你:已确认 Bug 或 无法复现。
👁️ 视觉验证 — 用通俗的语言描述页面应呈现的样子。AI 会截取屏幕截图,并根据你的描述判断通过或失败。无需设置像素差异检测。
🧠 学习系统 — 跟踪测试在多次运行中的稳定性。检测不稳定的测试、不稳定的选择器、缓慢的 API 和错误模式,然后提供可执行的见解。
⚡ 并行执行 — 使用共享浏览器池(browserless、raw CDP、Lightpanda、Obscura 或 Steel)同时运行 N 个测试。对于共享状态的测试,也提供串行模式。
🎯 可插拔的浏览器驱动程序 — 为每个测试选择合适的引擎:通过 browserless 使用真实的 Chrome,使用 Lightpanda 或 Obscura 快速轻量地运行,或使用 Steel 管理会话。可按测试设置 driver,也可以用 --driver 覆盖整个运行过程。
📊 实时仪表板 — 实时执行视图、包含通过率图表的运行历史记录、支持基于哈希搜索的屏幕截图图库,以及可展开的网络请求日志。
🔁 智能重试 — 支持测试级和操作级重试,并可配置延迟。系统会自动检测并标记不稳定的测试。
📦 可复用模块 — 将常见流程(登录、导航、设置)提取为参数化模块,并通过 $use 引用。
🏗️ 适用于 CI — 支持 JUnit XML 输出、失败时返回退出码 1,并自动截取错误屏幕截图。附带可直接使用的 GitHub Actions 示例。
🌐 多项目支持 — 一个仪表板汇总所有项目的测试结果。一个 Chrome 池即可供所有项目使用。
🐳 可移植 — Chrome 在 Docker 中运行,测试以 JSON 文件形式存放在你的代码仓库中。可在任何安装了 Node.js 和 Docker 的机器上运行。
✍️ 编写测试
涵盖测试编写的所有内容——文件格式、完整的操作词汇表、重试、状态隔离和复用。展开所需内容:
测试格式与文件布局
e2e/tests/ 中的每个 .json 文件都包含一个测试数组。每个测试都有一个 name 和按顺序执行的 actions:
[
{
"name": "homepage-loads",
"actions": [
{ "type": "goto", "value": "/" },
{ "type": "assert_visible", "selector": "body" },
{ "type": "assert_url", "value": "/" },
{ "type": "screenshot", "value": "homepage.png" }
]
}
]
套件文件可使用数字前缀来排序(01-auth.json、02-dashboard.json)。--suite 标志无论是否带前缀都能匹配,因此 --suite auth 会找到 01-auth.json。
操作目录 — 导航、输入与交互
| 操作 | 字段 | 描述 | |--------|--------|-------------| | `goto` | `value` | 导航至 URL(相对于 `baseUrl` 或绝对地址) | | `click` | `selector` 或 `text` | 通过 CSS 选择器或可见文本内容点击。文本模式还可使用 `scope: "dialog"`、`visible: true`、`last: true` | | `type` / `fill` | `selector`、`value` | 清空字段并输入文本 | | `wait` | `selector`、`text`、`gone` 或 `value`(毫秒) | 等待元素或文本出现,等待 `gone` 指定的元素消失(如加载指示器或对话框),或固定延时。优先使用条件,而非固定的 `value` 等待时间 | | `screenshot` | `value`(文件名) | 截取屏幕截图 | | `select` | `selector`、`value` | 选择下拉选项 | | `clear` | `selector` | 清空输入字段 | | `press` | `value` | 按下键盘按键(`Enter`、`Tab` 等) | | `scroll` | `selector` 或 `value`(像素) | 滚动至元素或按像素滚动 | | `hover` | `selector` | 将鼠标悬停在元素上 | | `evaluate` | `value` | 在浏览器上下文中执行 JavaScript | | `navigate` | `value` | 浏览器导航(`back`、`forward`、`reload`) | | `clear_cookies` | — | 清除当前页面的所有 Cookie | | `wait_network_idle` | 可选的 `value`(空闲毫秒数,默认 500)、`timeout` | 等待网络空闲达到 `value` 毫秒——适用于会触发后台请求的操作之后 | | `set_storage` | `value`(`"key=val"`),可选的 `selector: "session"` | 设置 `localStorage` 键(或使用 `selector: "session"` 设置 `sessionStorage`) | | `gql` | `value`(查询),可选的 `text`(变量 JSON),可选的 `selector`(断言) | 通过页面内的 `fetch` 运行 GraphQL 查询/变更,并从 `localStorage` 读取身份验证令牌。遇到 GraphQL 错误时失败。`selector` 是针对响应 `r` 执行断言的 JS 表达式(例如 `"r.data.users.length > 0"`)。会安装 `window.__e2eGql`,供后续 `evaluate` 步骤使用 |
按文本点击 — 当 click 使用 text 而非 selector 时,它会搜索常见的交互元素和内容元素:
button, a, [role="button"], [role="tab"], [role="menuitem"], [role="option"],
[role="listitem"], div[class*="cursor"], span, li, td, th, label, p, h1-h6
{ "type": "click", "text": "Sign In" }
断言 — 验证文本、元素、URL、数量和网络
| 操作 | 字段 | 描述 | |--------|--------|-------------| | `assert_text` | `text` | 断言页面任意位置存在该文本(子字符串) | | `assert_no_text` | `text` | 断言页面任意位置均未出现该文本——与 `assert_text` 相反 | | `assert_text_in` | `selector`、`text`、可选的 `value: "exact"` | 断言指定容器内存在该文本。默认情况下,`text` 是不区分大小写的正则表达式;`value: "exact"` 会切换为区分大小写的子字符串匹配 | | `assert_element_text` | `selector`、`text`、可选的 `value: "exact"` | 断言元素文本包含预期文本(或与其完全匹配) | | `assert_url` | `value` | 断言当前 URL 路径或完整 URL。路径(`/dashboard`)仅与 pathname 比较 | | `assert_visible` | `selector` | 断言元素存在且可见 | | `assert_not_visible` | `selector` | 断言元素已隐藏或不存在 | | `assert_attribute` | `selector`、`value` | 检查属性:`"type=email"` 用于检查值,`"disabled"` 用于检查属性是否存在 | | `assert_class` | `selector`、`value` | 断言元素具有指定的 CSS 类 | | `assert_input_value` | `selector`、`value` | 断言输入框、选择框或文本区域的 `.value` 包含指定文本 | | `assert_matches` | `selector`、`value`(正则表达式) | 断言元素文本与正则表达式模式匹配 | | `assert_count` | `selector`、`value` | 断言元素数量:精确值(`"5"`)或运算符(`">3"`、`">=1"`、`"<10"`) | | `assert_no_network_errors` | — | 如果任何网络请求失败(例如 `ERR_CONNECTION_REFUSED`),则失败 | | `assert_storage` | `value`(`"key"` 或 `"key=expected"`)、可选的 `selector: "session"` | 断言 `localStorage`/`sessionStorage` 键存在或具有指定值 | | `assert_visual` | `value`(基准图片)、可选的 `selector`、`text`(最大差异,例如 `"0.02"`)、`fullPage`、`maskRegions`、`threshold` | 视觉回归:将屏幕截图与基准图片进行比较。首次运行时保存基准图片;之后如果差异像素超过阈值(默认为 2%),则运行失败并生成差异图片 | | `get_text` | `selector` | 提取元素文本(非断言,不会失败)。结果:`{ value: "..." }` |
框架感知操作 — 无需编写繁琐的 evaluate 样板代码,即可用于 React/MUI
这些操作可处理 React/MUI 应用中的常见模式,而这些模式通常需要编写冗长的 evaluate 样板代码:
| 操作 | 字段 | 描述 |
|---|---|---|
type_react | selector、value、可选的 blur、waitAfter | 使用原生值设置器向 React 受控输入框输入内容。会分发 input 和 change 事件,确保 React 状态正确更新。blur: true 会在失焦时提交;waitAfter: "<ms>" 会在操作后等待(适用于防抖自动补全)。 |
click_regex | text(正则表达式)、可选的 selector、可选的 value: "last" | 点击 textContent 与正则表达式匹配的元素(不区分大小写)。默认点击第一个匹配项。使用 value: "last" 点击最后一个匹配项。 |
click_option | text | 根据文本点击 [role="option"] 元素——常见于自动补全/选择下拉菜单。 |
select_combobox | text、可选的 selector、filter、openWait/filterWait/waitAfter | 打开 MUI Autocomplete/Select,可选择输入 filter,然后点击与 text 匹配的选项。会依次尝试匹配 [role="option"]、.MuiAutocomplete-option、li.MuiMenuItem-root。 |
focus_autocomplete | text(标签文本) | 根据标签文本聚焦自动补全输入框。支持 MUI 和通用的 [role="combobox"]。 |
click_chip | text | 根据文本点击芯片/标签元素。会搜索 [class*="Chip"]、[class*="chip"]、[data-chip]。 |
click_icon | value(图标 ID)、可选的 selector(范围) | 根据 data-testid/data-icon/aria-label/类名片段或 SVG <title> 点击图标——支持 MUI、FontAwesome、Heroicons 等。会点击最近的可点击祖先元素(按钮、链接、标签页)。 |
click_menu_item | text、可选的 selector(范围) | 根据文本点击菜单项,支持 [role="menuitem"]、.dropdown-item、.menu-item、MUI MenuItem。 |
click_in_context | text(容器文本)、selector(子元素) | 在与 text 匹配的最小容器内点击子元素——例如某个特定卡片/行中的删除按钮。 |
// Before: 5 lines of evaluate boilerplate
{ "type": "evaluate", "value": "const input = document.querySelector('#search'); const nativeSet = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; nativeSet.call(input, 'term'); input.dispatchEvent(new Event('input', {bubbles: true})); input.dispatchEvent(new Event('change', {bubbles: true}));" }
// After: 1 action
{ "type": "type_react", "selector": "#search", "value": "term" }
多标签页操作 — 弹出窗口、OAuth 窗口和跨标签页流程
| 操作 | 字段 | 描述 |
|---|---|---|
open_tab | value(URL)、可选的 text(标签) | 打开新标签页并导航到该 URL(相对于 baseUrl 或绝对 URL)。标签默认为 tab-<n> |
switch_tab | value | 根据标签、数字索引或标题/URL 匹配项(正则表达式或子字符串)切换活动标签页。"default" 返回原始标签页 |
wait_for_tab | 可选的 text(标签)、timeout | 等待应用打开新标签页/弹出窗口(window.open、target="_blank"),并将其设为活动标签页 |
assert_tab_count | value | 断言打开的标签页数量:精确值("2")或运算符(">=2") |
close_tab | 可选的 value(标签) | 关闭当前标签页(或指定标签页),并切换回最后一个剩余标签页 |
之后的所有操作都会在活动标签页中运行:
{ "type": "click", "text": "Open report" }
{ "type": "wait_for_tab", "text": "report" }
{ "type": "assert_text", "text": "Quarterly results" }
{ "type": "close_tab" }
重试与不稳定检测
测试级重试 — 失败时重试整个测试。可通过配置全局设置,也可按测试设置:
{ "name": "flaky-test", "retries": 3, "timeout": 15000, "actions": [...] }
重试后通过的测试会在报告和学习系统中标记为不稳定。
操作级重试 — 重试单个操作,而无需重新运行整个测试。适用于对时序敏感的点击和等待:
{ "type": "click", "selector": "#dynamic-btn", "retries": 3 }
{ "type": "wait", "selector": ".lazy-loaded", "retries": 2 }
可通过配置中的 actionRetries、CLI 参数 --action-retries <n> 或环境变量 ACTION_RETRIES 进行全局设置。重试间隔:actionRetryDelay(默认值为 500ms)。
串行测试 — 适用于共享状态的测试
共享状态的测试(例如,两个测试修改同一条记录)在并行运行时可能发生竞争。可将这些测试标记为串行:
{ "name": "create-patient", "serial": true, "actions": [...] }
{ "name": "verify-patient-list", "serial": true, "actions": [...] }
串行测试会在所有并行测试完成后逐个运行——避免相互干扰,同时不降低独立测试的速度。
测试经过身份验证的应用
最简单的方法——像真实用户一样通过 UI 登录:
{
"hooks": {
"beforeEach": [
{ "type": "goto", "value": "/login" },
{ "type": "type", "selector": "#email", "value": "[email protected]" },
{ "type": "type", "selector": "#password", "value": "test-password" },
{ "type": "click", "text": "Sign In" },
{ "type": "wait", "selector": ".dashboard" }
]
},
"tests": [...]
}
对于使用 JWT 的 SPA,可直接注入令牌来跳过登录表单:
{ "type": "set_storage", "value": "accessToken=eyJhbGciOiJIUzI1NiIs..." }
也可以在配置中全局设置:
// e2e.config.js
export default {
authToken: 'eyJhbGciOiJIUzI1NiIs...',
authStorageKey: 'accessToken',
};
每项测试都会在全新的浏览器上下文中运行,因此测试之间的身份验证状态会自动清理。
**更多策略:**基于 Cookie 的身份验证、HTTP 标头注入、绕过 OAuth/SSO、可复用身份验证模块以及基于角色的测试,请参阅 docs/authentication.md
可复用模块 — 使用 $use 提取常见流程
将常见流程提取为带参数的模块:
// e2e/modules/login.json
{
"$module": "login",
"description": "Log in via the UI login form",
"params": {
"email": { "required": true, "description": "User email" },
"password": { "required": true, "description": "User password" }
},
"actions": [
{ "type": "goto", "value": "/login" },
{ "type": "type", "selector": "#email", "value": "{{email}}" },
{ "type": "type", "selector": "#password", "value": "{{password}}" },
{ "type": "click", "text": "Sign In" },
{ "type": "wait", "value": "2000" }
]
}
在测试中使用:
{
"name": "dashboard-loads",
"actions": [
{ "$use": "login", "params": { "email": "[email protected]", "password": "secret" } },
{ "type": "assert_text", "text": "Dashboard" }
]
}
模块支持参数验证(缺少必需参数时会立即失败)、条件块({{#param}}...{{/param}})、嵌套组合和循环检测。
钩子 — beforeAll / beforeEach / afterEach / afterAll
在生命周期的各个阶段运行操作。可在配置中全局定义,也可按测试套件定义:
{
"hooks": {
"beforeAll": [{ "type": "goto", "value": "/setup" }],
"beforeEach": [{ "type": "goto", "value": "/" }],
"afterEach": [{ "type": "screenshot", "value": "after.png" }],
"afterAll": []
},
"tests": [...]
}
重要:
beforeAll会在单独的浏览器页面中运行,该页面会在测试开始前关闭。对于测试所需的状态(Cookie、本地存储、身份验证令牌),请使用beforeEach。
排除模式 — 在 --all 中跳过草稿
在 --all 运行中跳过探索性测试或草稿测试:
// e2e.config.js
export default {
exclude: ['explore-*', 'debug-*', 'draft-*'],
};
单独运行测试套件(--suite)不受排除模式影响。
🤖 AI 集成
核心理念:由你的 Agent 为你编写、运行并验证测试。
Claude Code — 安装插件和仅安装 MCP
claude plugin marketplace add fastslack/mtw-e2e-runner
claude plugin install e2e-runner@matware
这会为 Claude 提供 17 个 MCP 工具、一个工作流技能、4 个斜杠命令(/e2e-runner:run、/e2e-runner:create-test、/e2e-runner:verify-issue、/e2e-runner:capture),以及 3 个专用 Agent(test-analyzer、test-creator、test-improver)。
仅安装 MCP(仅工具,不含技能/命令/Agent):
claude mcp add --transport stdio --scope user e2e-runner \
-- npx -y -p @matware/e2e-runner e2e-runner-mcp
OpenCode
cp node_modules/@matware/e2e-runner/opencode.json ./
mkdir -p .opencode && cp -r node_modules/@matware/e2e-runner/.opencode/* .opencode/
详情请参阅 OPENCODE.md。
17 个 MCP 工具
| 工具 | 描述 |
|---|---|
e2e_run | 运行测试(全部、按测试套件或按文件) |
e2e_list | 列出可用的测试套件 |
e2e_create_test | 创建新的测试 JSON 文件 |
e2e_create_module | 创建可复用模块 |
e2e_pool_status | 检查 Chrome 池的运行状况 |
e2e_app_pool_status | 检查应用环境池(分支、端口、驱动程序) |
e2e_screenshot | 根据哈希值获取截图 |
e2e_capture | 截取任意 URL 的页面 |
e2e_analyze | 提取页面结构(交互元素、表单、标题),并生成测试脚手架 |
e2e_dashboard_start | 启动 Web 仪表板 |
e2e_dashboard_stop | 停止 Web 仪表板 |
e2e_dashboard_restart | 重启仪表板(新的项目目录/端口,清除过期会话) |
e2e_issue | 获取问题并生成测试 |
e2e_network_logs | 查询某次运行的网络日志 |
e2e_learnings | 查询稳定性洞察 |
e2e_vars | 管理由 SQLite 提供支持的 {{var.KEY}} 项目变量 |
e2e_neo4j | 管理 Neo4j 知识图谱 |
池的启动/停止仅支持通过 CLI 操作,不通过 MCP 提供。
视觉验证 — 描述页面,由 AI 判定
描述页面应呈现的样子,AI 会根据截图判定通过或失败:
{
"name": "dashboard-loads",
"expect": "Patient list with at least 3 rows, no error messages, sidebar with navigation links",
"actions": [
{ "type": "goto", "value": "/dashboard" },
{ "type": "wait", "selector": ".patient-list" }
]
}
测试操作完成后,运行器会自动截取验证截图。MCP 响应中包含截图哈希值,Claude Code 会获取截图,并根据你的 expect 描述进行视觉验证。无需 API 密钥。
从问题生成测试 — 将 Bug 报告转为可运行的测试
将 GitHub 和 GitLab 问题转为可执行的 E2E 测试。粘贴问题 URL,即可自动获取可运行的测试。
工作原理:
- 获取 — 通过
gh或glabCLI 拉取问题详情(标题、正文、标签) - 生成 — AI 根据问题描述创建 JSON 测试操作
- 运行 — 可选择立即执行测试,以验证 Bug 是否可复现
# Fetch and display
e2e-runner issue https://github.com/owner/repo/issues/42
# Generate a test file via Claude API
e2e-runner issue https://github.com/owner/repo/issues/42 --generate
# Generate + run + report
e2e-runner issue https://github.com/owner/repo/issues/42 --verify
# -> "BUG CONFIRMED" or "NOT REPRODUCIBLE"
在 Claude Code 中,只需提出请求:
“获取问题 #42 并为其创建 E2E 测试”
Bug 验证逻辑:生成的测试会断言正确行为。测试失败 = 确认存在 Bug。所有测试通过 = 无法复现。
**身份验证:**GitHub 需要 gh CLI,GitLab 需要 glab CLI。支持自托管的 GitLab。
📊 仪表板与洞察
e2e-runner dashboard # Start on default port 8484
e2e-runner dashboard --port 9090 # Custom port
Web 仪表板介绍 — 实时视图、历史记录、图库、池状态
实时执行 — 实时监控测试,查看逐步进度、耗时和活跃工作器数量。
测试套件 — 浏览各项目中的所有测试套件。只需单击一下,即可运行单个测试套件或所有测试。
运行详情 — PASS/FAIL 徽标、可复制哈希值(ss:77c28b5a)的截图缩略图、格式化的控制台错误和网络请求日志。
截图库 — 浏览所有捕获的截图并按哈希搜索(操作、错误和验证截图)。
池状态 — Chrome 池健康状况:可用槽位、运行中的会话和内存压力。
学习系统 — 不稳定测试、不稳定选择器、缓慢的 API
运行器会从每次测试运行中学习,逐步积累有关测试套件的知识。通过 e2e_learnings MCP 工具查询洞察:
| 查询 | 返回内容 |
|---|---|
summary | 完整的健康状况概览:通过率、不稳定测试、不稳定选择器、API 问题 |
flaky | 仅在重试后通过的测试 |
selectors | 失败率较高的 CSS 选择器 |
pages | 存在控制台错误、网络故障或加载时间问题的页面 |
apis | 存在错误率和延迟问题的 API 端点(自动规范化:UUID、哈希值、ID) |
errors | 按类别整理的最常见错误模式 |
trends | 随时间变化的通过率(当所有数据都来自同一天时,自动切换为按小时统计) |
test:<name> | 特定测试的历史详情 |
page:<path> | 特定页面的历史详情 |
selector:<value> | 特定选择器的历史详情 |
存储与导出:
- SQLite(
~/.e2e-runner/dashboard.db)——默认选项,无需设置 - Neo4j 知识图谱——可选,用于基于关系的分析。通过
e2e_neo4jMCP 工具或docker compose管理 - Markdown 报告(
e2e/learnings.md)——每次运行后自动生成
测试叙述: 每次测试运行都会生成按步骤描述执行过程的易读说明,可在 CLI 输出和仪表板中查看。
网络错误处理 — 断言、全局标志、完整日志
显式断言 — 在关键页面加载后放置 assert_no_network_errors:
{ "type": "goto", "value": "/dashboard" },
{ "type": "wait", "selector": ".loaded" },
{ "type": "assert_no_network_errors" }
全局标志 — 设置 failOnNetworkError: true,自动使任何出现网络错误的测试失败:
e2e-runner run --all --fail-on-network-error
禁用时(默认),运行器仍会收集并报告网络错误——如果测试通过但存在网络错误,MCP 响应中会包含警告。
完整网络日志 — 捕获所有 XHR/fetch 请求,包括 URL、方法、状态、耗时、请求/响应标头和响应正文(截断至 50KB)。可在仪表板中查看,并可展开请求详情行。
MCP 深入查询流程:
1. e2e_run → compact networkSummary + runDbId
2. e2e_network_logs(runDbId) → all requests (url, method, status, duration)
3. e2e_network_logs(runDbId, errorsOnly: true) → only failed requests
4. e2e_network_logs(runDbId, includeHeaders: true) → with headers
5. e2e_network_logs(runDbId, includeBodies: true) → full request/response bodies
无论捕获了多少请求,e2e_run 响应都保持精简(约 5KB)。使用返回的 runDbId 调用 e2e_network_logs,按需深入查看详情。
截图捕获 — 按需截取任意 URL
按需截取任意 URL 的屏幕截图——无需测试套件:
e2e-runner capture https://example.com
e2e-runner capture https://example.com --full-page --selector ".loaded" --delay 2000
通过 MCP,e2e_capture 工具支持用于身份验证页面的 authToken 和 authStorageKey——它会在导航前将令牌注入 localStorage。
每张截图都会获得一个确定性哈希(ss:a3f2b1c9)。使用 e2e_screenshot 按哈希检索任意截图 — 它会返回附带元数据(测试名称、步骤、类型)的图像。
🌐 浏览器驱动
运行器可以通过不同驱动与多个浏览器引擎交互。默认值为 auto — 它会探测每个池 URL,并为每个池选择合适的驱动。
| 驱动 | 引擎 | 探测方式 | 适用场景 |
|---|---|---|---|
browserless | 通过 browserless 使用真实 Chromium | /pressure 返回 JSON | 默认选项。生产级 JS 执行、屏幕录制、完整 Chrome 行为 |
cdp | 通用 CDP 兼容引擎(原始 Chrome 等) | 可访问 /json/version | 其他 CDP 服务都不适用时的备用选项 |
lightpanda | Lightpanda(Zig) | /json/version 的 Browser=lightpanda | 速度约快 9×,内存占用约为无头 Chrome 的 1/16 — 非常适合高吞吐量的抓取类测试 |
obscura | Obscura(Rust + V8) | /json/version 的 Browser=obscura | 内存占用约 30 MB,内置反检测(--stealth),通过 Puppeteer 保持与真实 Chrome 接近的表现 |
steel | Steel Browser | /v1/sessions 返回 JSON | 托管会话生命周期,用于编排的 REST API |
为每项测试选择驱动 / 为整个运行强制指定驱动
{
"tests": [
{
"name": "checkout flow (heavy JS, real Chrome)",
"driver": "browserless",
"actions": [...]
},
{
"name": "scrape product page (lightweight)",
"driver": "obscura",
"fallbackDriver": "cdp",
"actions": [...]
}
]
}
driver 是可选项。设置后,只有检测到的驱动与之匹配的池才会成为候选项。fallbackDriver 必须显式选择启用 — 否则,缺少目标驱动时,测试会以清晰的提示失败。池的繁忙状态不会触发回退;运行器会在筛选后的候选池中等待。
为整个运行强制指定驱动(CLI 参数优先于单项测试字段 — 适用于 A/B 基准测试):
e2e-runner run --all --driver obscura
e2e-runner run --all --driver obscura --fallback-driver cdp
在本地运行各驱动
# browserless (default) — managed by `pool start`
e2e-runner pool start
# Lightpanda — pool start uses templates/docker-compose-lightpanda.yml
e2e-runner pool start # with poolDriver: 'lightpanda' in config
# Obscura — install the binary and run it yourself
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-x86_64-linux.tar.gz
tar xzf obscura-x86_64-linux.tar.gz
./obscura serve --port 9222 --stealth
# then point the runner at it: poolUrls: ['http://localhost:9222'], poolDriver: 'obscura'
⚙️ CLI、配置与 CI
CLI 命令
# Run tests
e2e-runner run --all # All suites
e2e-runner run --suite auth # Single suite
e2e-runner run --tests path/to.json # Specific file
e2e-runner run --inline '<json>' # Inline JSON
# Pool management (CLI only, not MCP)
e2e-runner pool start # Start Chrome container
e2e-runner pool stop # Stop Chrome container
e2e-runner pool status # Check pool health
# Issue-to-test
e2e-runner issue <url> # Fetch issue
e2e-runner issue <url> --generate # Generate test via AI
e2e-runner issue <url> --verify # Generate + run + report
# Dashboard
e2e-runner dashboard # Start web dashboard
# Other
e2e-runner list # List available suites
e2e-runner capture <url> # On-demand screenshot
e2e-runner init # Scaffold project
CLI 选项
| 标志 | 默认值 | 说明 |
|---|---|---|
--base-url <url> | http://host.docker.internal:3000 | 应用基础 URL |
--pool-url <ws> | ws://localhost:3333 | Chrome 池 WebSocket URL |
--concurrency <n> | 3 | 并行测试工作线程数 |
--retries <n> | 0 | 失败测试的重试次数 |
--action-retries <n> | 0 | 失败操作的重试次数 |
--test-timeout <ms> | 60000 | 单项测试超时时间 |
--timeout <ms> | 10000 | 默认操作超时时间 |
--output <format> | json | 报告格式:json、junit、both |
--env <name> | default | 环境配置 |
--fail-on-network-error | false | 网络错误时使测试失败 |
--project-name <name> | 目录名称 | 项目显示名称 |
--driver <name> | (单项测试) | 为整个运行强制指定池驱动:browserless、cdp、lightpanda、obscura、steel |
--fallback-driver <name> | 无 | 使用 --driver 指定的驱动不可达时显式启用的备用驱动 |
配置 — e2e.config.js 与优先级
在项目根目录创建 e2e.config.js:
export default {
baseUrl: 'http://host.docker.internal:3000',
concurrency: 4,
retries: 2,
actionRetries: 1,
testTimeout: 30000,
outputFormat: 'both',
failOnNetworkError: true,
exclude: ['explore-*', 'debug-*'],
hooks: {
beforeEach: [{ type: 'goto', value: '/' }],
},
environments: {
staging: { baseUrl: 'https://staging.example.com' },
production: { baseUrl: 'https://example.com', concurrency: 5 },
},
};
配置优先级(从高到低):
- CLI 标志
- 环境变量
- 配置文件(
e2e.config.js或e2e.config.json) - 默认值
设置 --env <name> 时,匹配的配置会覆盖所有其他设置。
CI/CD — JUnit XML 与 GitHub Actions
e2e-runner run --all --output junit
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx e2e-runner pool start
- run: npx e2e-runner run --all --output junit
- uses: mikepenz/action-junit-report@v4
if: always()
with:
report_paths: e2e/screenshots/junit.xml
编程 API
import { createRunner } from '@matware/e2e-runner';
const runner = await createRunner({ baseUrl: 'http://localhost:3000' });
const report = await runner.runAll();
const report = await runner.runSuite('auth');
const report = await runner.runFile('e2e/tests/login.json');
const report = await runner.runTests([
{ name: 'quick-check', actions: [{ type: 'goto', value: '/' }] },
]);
要求
- Node.js >= 20
- Docker — 仅 选项 3(并行 Chrome 池)需要。选项 1 和 2 无需 Docker。
许可证
版权所有 2026 Matias Aguirre(fastslack)— Matware
本项目依据 Apache License 2.0 许可证授权。详情请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。