关于 playwright-verify
playwright-verify 是一项技能,通过独立的 Playwright 脚本和 Node.js 快速执行一次性浏览器检查。它帮助开发者验证本地或文件页面、检查浏览器与网络错误,并测量 UI 元素,无需配置测试运行器。
主要功能
- 01运行独立的 .mjs 验证脚本
- 02截取页面截图
- 03收集控制台、页面、HTTP 和请求错误
- 04测量元素尺寸与样式
- 05通过 CDP 保留浏览器状态
适用场景
- 01浏览器工具无法访问时验证 localhost 页面
- 02调查不可见或无法点击的元素
- 03无需创建测试套件即可验证代码假设
- 04保留浏览器会话以执行多步检查
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rheinmir/ setup --skill playwright-verify安装后在 Claude Code 中输入/playwright-verify确认已加载。
详细介绍
技能:playwright-verify
总结自 GH#96——一种廉价(每次 < 5s、不使用 LLM)的验证工具,也是当 claude-in-chrome 在这台机器上屏蔽这两种 scheme 时,与 localhost/file:// 交互的唯一方式。
WHAT
用途和背景
- 用途: 使用 Playwright,通过独立的
.mjs+node快速验证代码,一次性运行(不使用测试运行器):截取localhost/file://页面,提取 console/pageerror/http/requestfailed 错误,测量真实元素,绕过身份验证;需要多步操作时,使用持久会话connectOverCDP。 - 触发条件(何时使用):
claude-in-chrome在localhost:*/file://上报告Frame with ID 0 is showing error page或Can't interact with browser-internal or unparseable URLs。- 需要诊断 UI 元素“看不见”/“点不了”——截图不够,需要真实测量值
(
getBoundingClientRect、getComputedStyle)。 - 需要验证一个代码假设,然后丢弃脚本(不值得创建
*.spec.ts+ 测试运行器)。 - 需要通过开发登录/模拟会话进行测试,而不是每次都登录真实 SSO。
- 用户提到“用 playwright 验证”“截取页面截图”“用脚本快速测试 UI”“多步验证”“在多次运行之间保留会话”“无法点击/看不到元素”或
/playwright-verify。
- 用户提到“用 playwright 验证”“截取页面截图”“用脚本快速测试 UI”“多步验证”“在多次运行之间保留会话”“无法点击/看不到元素”或
- 非目标: 不创建
*.spec.ts+ 测试运行器(npx playwright test),不是 UAT 测试用例集(→ uat-nonit-testcase),不替代 claude-in-chrome 来操作普通外部网页,不将脚本提交到目标仓库。
思维模型
需要验证的假设 →(是否已安装?)→ 一次性 .mjs 脚本:启动 → 接入 4 种错误通道 → goto(加载 + 等待)→ 截图/测量 → 关闭 → 查看日志 + 图片 → 丢弃。多步操作分支:独立的 Chrome CDP(临时 profile)← 每轮由 step-N.mjs 连接,状态在各轮之间保留 → 完成后 pkill。
输入和输出约定
| 字段 | 必填? | 含义 | |
|---|---|---|---|
| In | URL(localhost:* / file:// / 其他) | 是 | 需要验证的页面 |
| In | 项目的 node_modules 中有 @playwright/test | 是 | 脚本必须从该目录运行(或使用 NODE_PATH) |
| In | 开发登录 cookie/localStorage | 否 | 用于绕过身份验证 |
| Out | 截图(如 /tmp/check.png)+ [console]/[pageerror]/[http]/[reqfail] 日志 | 是 | 证据;仅有干净的截图并不足够 |
| Out | getBoundingClientRect/getComputedStyle 测量值 | 诊断元素时 | 真实数值,而非根据截图猜测 |
| Out | “完成” | — | 通过日志 + 截图确认/否定假设;脚本已清理;Chrome CDP(如有)已 pkill |
规则与能力
- RULE-01 (MUST):
import { chromium } from "@playwright/test"必须从包含@playwright/test的项目目录中运行,该目录的node_modules中需包含此包 — ESM 根据cwd解析,而不是根据脚本文件的位置解析。脚本位于其他目录(例如/tmp)→ 运行前先将其cp到项目中,或设置NODE_PATH。 - RULE-02 (MUST):在 Next.js dev server 上,
waitUntil: "networkidle"会无限期挂起 — HMR/webpack dev 会保持一个 long-poll/websocket 连接持续存活,因此网络永远不会“空闲”。使用waitUntil: "load"+ 固定的page.waitForTimeout(N)。 - RULE-03 (MUST):绕过 dev-login 认证:
context.addCookies([{url, name, value}])(优先使用url,而不是单独使用domain+path— 避免 cookie 不匹配)+context.addInitScript(...)设置localStorage— 必须在调用newPage()之前设置状态,而不是之后。 - RULE-04 (MUST):每个独立路由使用一个
context,如果应用有“辅助 API 遇到 401 → 自动清除全局 session”的逻辑 — 多次导航复用同一个 context 会导致路由 N 的错误连带破坏路由 N+1。 - RULE-05 (MUST):诊断“看不到/点不了”:不要只看截图 — 调用
page.evaluate(() => el.getBoundingClientRect())+getComputedStyle(el)获取实际测量值;locator.click()会明确报告错误(“element is not visible”)并附带重试步骤日志,先阅读日志再猜测原因。 - RULE-06 (MUST):干净的截图不能证明任何事 — 始终检查 4 个错误渠道,而不只是 2 个:
console(typeerror)·pageerror·response中status() >= 400的响应 ·requestfailed。Analytics 崩溃、API 5xx、promise rejection 不会体现在任何像素上。在goto之前将以下监听器绑定到page:page.on("response", r => { if (r.status() >= 400) console.log("[http]", r.status(), r.url()); }); page.on("requestfailed", r => console.log("[reqfail]", r.failure()?.errorText, r.url())); - RULE-07 (MUST):持久会话模式:使用独立的 Chrome 和独立的 profile(临时
--user-data-dir)。绝不要对用户的个人 Chrome 使用--remote-debugging-port— macOS 会弹出权限请求对话框,脚本还会干扰用户正在使用的标签页。 - RULE-08 (MUST):独立脚本文件不得进入 git — 从 scratchpad 运行,不要提交到目标 repo。
- RULE-09 (MUST):只修改任务所需内容 — 不要顺手做其他更改。
- 能力:本地运行无头浏览器 + 读取/写入临时文件;仅在安装 binary(CDN)时访问外网,并设置超时。
故障边界
npx playwright install被网络阻止/挂起 → blocked,明确报告错误(设置超时),不要让命令无限期挂起。- ESM 无法解析
@playwright/test(运行时cwd错误)→ 将脚本cp到项目中或设置NODE_PATH,然后重新运行。 networkidle在 Next.js dev 上挂起 → 改用load+waitForTimeout(不是应用错误)。- 看不到/点不了元素 → 测量实际值 + 阅读
locator.click()日志后再下结论;不要根据截图猜测。
HOW
主要工作流
| 步骤 | 类型 | 输入 | 操作 | 输出/退出 | 失败/后续 |
|---|---|---|---|---|---|
| W01 | 确定性 | 机器 | 检查是否已安装(ls ~/Library/Caches/ms-playwright);如果没有则安装 | 二进制文件就绪 | CDN 被拦截 → blocked |
| W02 | 判断 | 假设 + URL | 编写一个可直接用 node 运行的 .mjs 文件,在 goto 前挂接 4 个错误通道 | 脚本 | — |
| W03 | 效果 | 脚本 | 从正确目录运行(node check.mjs),阅读日志 + 图片;清理脚本 | 日志 + 图片 | 解决错误 → cp/NODE_PATH,重复 W03 |
| W04 | 效果 | 多步骤任务 | 持久会话模式 connectOverCDP(B01) | 状态在各轮间保留 | 完成 → pkill |
各步骤详情(W01–W04 的权威说明):
- 检查是否已安装,再决定是否重新安装:
ls ~/Library/Caches/ms-playwright(macOS)— 二进制缓存由所有项目共用,无需每个仓库都重新安装。- 如果没有:执行
pnpm add -D @playwright/test(如果项目的devDependencies中已有,则跳过),然后执行npx playwright install chromium。 npx playwright install会从外部 CDN(cdn.playwright.dev)下载二进制文件——在网络有 允许列表的沙盒/CI 中会卡住或失败。自动调用时设置超时;如果被拦截,应明确报告错误, 而不是让命令无限期挂起。
- 如果没有:执行
- 编写一个可直接用
node运行的.mjs文件(不通过npx playwright test)——对于 验证后即丢弃的任务来说更繁琐:import { chromium } from "@playwright/test"; // export chromium dùng được như package "playwright" const browser = await chromium.launch(); const page = await browser.newPage({ viewport: { width: 1400, height: 900 } }); page.on("console", m => console.log("[console]", m.type(), m.text())); page.on("pageerror", e => console.log("[pageerror]", e.message)); await page.goto("http://localhost:3000/some-page", { waitUntil: "load" }); // KHÔNG "networkidle" — xem Rules #2 await page.waitForTimeout(1500); await page.screenshot({ path: "/tmp/check.png", fullPage: true }); await browser.close(); - 从正确目录运行(参见 Rules #1),执行
node check.mjs,阅读 console/pageerror 日志 + 截图。如果是在某个具体项目中运行,完成后清理.mjs文件(无需从 git 中删除任何内容—— 如果从 scratch 目录运行,脚本不在仓库中)。 - 有条件的多步骤任务 → 持久会话模式(借鉴 browser-use/browser-harness,
09/2026)。一次性脚本要求 Agent 盲目编写整个流程;某一步出错,就得从登录开始重跑。
不如启动一个带 CDP 端口的 Chrome,每轮运行一个小脚本接入——标签页、cookie、
localStorage和滚动位置都会在各轮间保留,Agent 可以先观察,再决定下一步。# một lần — Chrome riêng, profile riêng; KHÔNG bám vào Chrome cá nhân của user CH="$HOME/Library/Caches/ms-playwright/chromium-1243/chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing" "$CH" --remote-debugging-port=9333 --user-data-dir=/tmp/pw-prof --headless=new --no-first-run about:blank &
完成后:// mỗi lượt — step-N.mjs import { chromium } from "@playwright/test"; const browser = await chromium.connectOverCDP("http://localhost:9333"); const ctx = browser.contexts()[0]; const page = ctx.pages()[0] ?? await ctx.newPage(); // bám tab cũ, KHÔNG launch mới // ... một hành động + quan sát; state sống sang lượt sau ... await browser.close(); // chỉ ngắt kết nối, Chrome vẫn chạypkill -f "remote-debugging-port=9333"。 09/09/2026 的测量结果(python-playwright,同一个file://页面):每轮重新启动需要 1.1–1.9 秒,且会丢失状态;connectOverCDP每轮需要 0.65–0.80 秒,window.__marker在新进程启动后仍然存在(3/3)。 上面的.mjs版本在测量会话中尚未用 node 运行——API 与已运行的 python 版本一致。
分支
| ID | 类型 | 条件 | 行为 | 跳过/失败 | 重新汇合 |
|---|---|---|---|---|---|
| B01 | conditional_required | 有条件的多步骤任务(需要先观察,再决定下一步) | 启动一个独立 Chrome,使用 --remote-debugging-port=9333 + 临时配置文件;每轮通过 connectOverCDP 运行 step-N.mjs | 一次性任务 → 跳过;绝不接入个人 Chrome | 结束时执行 pkill -f "remote-debugging-port=9333" |
| B02 | user_optional | 页面需要开发登录/伪造会话 | 在 newPage() 之前执行 context.addCookies + context.addInitScript;如果应用在 401 时会自行删除会话,则每个路由使用一个 context | — | W03 |
| B03 | recovery | 元素“看不见/无法点击” | 执行 page.evaluate(() => el.getBoundingClientRect()) + getComputedStyle(el),阅读 locator.click() 的重试日志 | — | W03 |
验证与停止
结论应基于 4 个错误通道 + 图片 + 测量数据,而不只是图片。每轮运行 < 5 秒,不使用大模型。假设得到证据支持后即停止;清理脚本 + 终止 CDP Chrome。
示例
- 正向: claude-in-chrome 在
http://localhost:3000/some-page上报告Frame with ID 0 is showing error page→ 将check.mjscp 到项目中 → 执行node check.mjs→/tmp/check.png+[console]/[pageerror]/[http]/[reqfail]日志均干净 → 确认页面已渲染,删除脚本。 - 边界/失败: 脚本在 Next.js dev 上使用
waitUntil: "networkidle"→ 无限期挂起;改为waitUntil: "load"+page.waitForTimeout(1500)→ 成功运行。图片干净,但日志中有[http] 500 /api/x→ 不要报告通过。
评论
支持 Markdown