基于 Playwright 实现全方位的浏览器自动化。支持自动检测开发服务器,并将干净的测试脚本写入 /tmp 目录。涵盖页面测试、表单填写、截图保存、响应式设计检查、UX 验证、登录流程测试、死链检测以及任意浏览器任务自动化。当用户需要测试网站、自动化浏览器交互、验证 Web 功能或进行任何基于浏览器的测试时使用。
重要说明 - 路径解析:
此 Skill 可以安装在不同位置(插件系统、手动全局安装、全局或特定项目)。在执行任何命令之前,请根据加载此 SKILL.md 文件的实际位置来确定 Skill 目录,并在后续所有命令中使用该路径。将 $SKILL_DIR 替换为实际查找到的路径。
常见的安装路径:
- 插件系统:
~/.claude/plugins/marketplaces/playwright-skill/skills/playwright-skill - 手动全局安装:
~/.claude/skills/playwright-skill - 项目特定路径:
<project>/.claude/skills/playwright-skill
Playwright 浏览器自动化
通用浏览器自动化 Skill。我会根据你请求的任何自动化任务编写自定义 Playwright 代码,并通过通用执行器运行它。
关键工作流 - 请按顺序执行以下步骤:
-
自动检测开发服务器 - 对于 localhost 测试,务必首先运行服务器检测:
cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(servers => console.log(JSON.stringify(servers)))"- 如果找到 1 个服务器:自动使用该服务器并告知用户
- 如果找到多个服务器:询问用户具体测试哪一个
- 如果未找到服务器:提示输入 URL 或提供协助启动开发服务器
-
将脚本写入 /tmp - 绝不要把测试文件写到 Skill 目录中;始终使用
/tmp/playwright-test-*.js -
默认使用有头(可视化)浏览器 - 除非用户明确要求无头模式,否则始终使用
headless: false -
URL 参数化 - 始终在脚本顶部通过环境变量或常量让 URL 可配置
工作原理
- 你描述想要测试或自动化的内容
- 我自动检测正在运行的开发服务器(如果测试外部网站则询问 URL)
- 我在
/tmp/playwright-test-*.js中编写自定义 Playwright 代码(不会污染你的项目) - 我通过以下命令执行它:
cd $SKILL_DIR && node run.js /tmp/playwright-test-*.js - 结果实时显示,并保持浏览器窗口可见以便调试
- 测试文件会由操作系统自动从 /tmp 清理
安装与初始化(首次使用)
cd $SKILL_DIR
npm run setup
这会安装 Playwright 和 Chromium 浏览器。只需运行一次。
执行模式
步骤 1:检测开发服务器(针对 localhost 测试)
cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(s => console.log(JSON.stringify(s)))"
步骤 2:在 /tmp 中编写带 URL 参数的测试脚本
// /tmp/playwright-test-page.js
const { chromium } = require('playwright');
// 参数化 URL(自动检测或由用户提供)
const TARGET_URL = 'http://localhost:3001'; // <-- 自动检测或来自用户
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto(TARGET_URL);
console.log('页面已加载:', await page.title());
await page.screenshot({ path: '/tmp/screenshot.png', fullPage: true });
console.log('📸 截图已保存至 /tmp/screenshot.png');
await browser.close();
})();
步骤 3:从 Skill 目录执行
cd $SKILL_DIR && node run.js /tmp/playwright-test-page.js
常见用例模式
测试页面(多视口)
// /tmp/playwright-test-responsive.js
const { chromium } = require('playwright');
const TARGET_URL = 'http://localhost:3001'; // 自动检测
(async () => {
const browser = await chromium.launch({ headless: false, slowMo: 100 });
const page = await browser.newPage();
// 桌面端测试
await page.setViewportSize({ width: 1920, height: 1080 });
await page.goto(TARGET_URL);
console.log('桌面端 - 标题:', await page.title());
await page.screenshot({ path: '/tmp/desktop.png', fullPage: true });
// 移动端测试
await page.setViewportSize({ width: 375, height: 667 });
await page.screenshot({ path: '/tmp/mobile.png', fullPage: true });
await browser.close();
})();
测试登录流程
// /tmp/playwright-test-login.js
const { chromium } = require('playwright');
const TARGET_URL = 'http://localhost:3001'; // 自动检测
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto(`${TARGET_URL}/login`);
await page.fill('input[name="email"]', 'test@example.com');
await page.fill('input[name="password"]', 'password123');
await page.click('button[type="submit"]');
// 等待重定向
await page.waitForURL('**/dashboard');
console.log('✅ 登录成功,已重定向至控制台');
await browser.close();
})();
填写并提交表单
// /tmp/playwright-test-form.js
const { chromium } = require('playwright');
const TARGET_URL = 'http://localhost:3001'; // 自动检测
(async () => {
const browser = await chromium.launch({ headless: false, slowMo: 50 });
const page = await browser.newPage();
await page.goto(`${TARGET_URL}/contact`);
await page.fill('input[name="name"]', 'John Doe');
await page.fill('input[name="email"]', 'john@example.com');
await page.fill('textarea[name="message"]', 'Test message');
await page.click('button[type="submit"]');
// 验证提交结果
await page.waitForSelector('.success-message');
console.log('✅ 表单提交成功');
await browser.close();
})();
检查失效链接(死链)
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto('http://localhost:3000');
const links = await page.locator('a[href^="http"]').all();
const results = { working: 0, broken: [] };
for (const link of links) {
const href = await link.getAttribute('href');
try {
const response = await page.request.head(href);
if (response.ok()) {
results.working++;
} else {
results.broken.push({ url: href, status: response.status() });
}
} catch (e) {
results.broken.push({ url: href, error: e.message });
}
}
console.log(`✅ 正常链接: ${results.working}`);
console.log(`❌ 失效链接:`, results.broken);
await browser.close();
})();
带错误处理的页面截图
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
try {
await page.goto('http://localhost:3000', {
waitUntil: 'networkidle',
timeout: 10000,
});
await page.screenshot({
path: '/tmp/screenshot.png',
fullPage: true,
});
console.log('📸 截图已保存至 /tmp/screenshot.png');
} catch (error) {
console.error('❌ 出错:', error.message);
} finally {
await browser.close();
}
})();
测试响应式设计
// /tmp/playwright-test-responsive-full.js
const { chromium } = require('playwright');
const TARGET_URL = 'http://localhost:3001'; // 自动检测
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
const viewports = [
{ name: 'Desktop', width: 1920, height: 1080 },
{ name: 'Tablet', width: 768, height: 1024 },
{ name: 'Mobile', width: 375, height: 667 },
];
for (const viewport of viewports) {
console.log(
`正在测试 ${viewport.name} (${viewport.width}x${viewport.height})`,
);
await page.setViewportSize({
width: viewport.width,
height: viewport.height,
});
await page.goto(TARGET_URL);
await page.waitForTimeout(1000);
await page.screenshot({
path: `/tmp/${viewport.name.toLowerCase()}.png`,
fullPage: true,
});
}
console.log('✅ 所有视口测试完毕');
await browser.close();
})();
单行/行内执行(简单任务)
对于快速的一次性任务,可以直接行内执行代码而无需创建文件:
# 快速截图
cd $SKILL_DIR && node run.js "
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto('http://localhost:3001');
await page.screenshot({ path: '/tmp/quick-screenshot.png', fullPage: true });
console.log('Screenshot saved');
await browser.close();
"
何时使用行内代码 vs 脚本文件:
- 行内代码:快速一次性任务(如截图、检查元素是否存在、获取页面标题)
- 脚本文件:复杂测试、响应式检查、任何用户可能需要重新运行的内容
可用的 Helper 工具函数
lib/helpers.js 中提供的可选工具函数:
const helpers = require('./lib/helpers');
// 检测运行中的开发服务器(关键 - 务必优先使用此函数!)
const servers = await helpers.detectDevServers();
console.log('Found servers:', servers);
// 带重试机制的安全点击
await helpers.safeClick(page, 'button.submit', { retries: 3 });
// 清空后输入的安全打字
await helpers.safeType(page, '#username', 'testuser');
// 截取带时间戳的截图
await helpers.takeScreenshot(page, 'test-result');
// 处理 Cookie 弹窗 Banner
await helpers.handleCookieBanner(page);
// 提取表格数据
const data = await helpers.extractTableData(page, 'table.results');
完整列表请查阅 lib/helpers.js。
自定义 HTTP 请求头
可通过环境变量为所有 HTTP 请求配置自定义 Header。适用于以下场景:
- 标识向后端的自动化流量
- 获取针对 LLM 优化的响应(如纯文本错误而非带样式的 HTML)
- 全局添加身份验证 Token
配置方式
单 Header 常用场景:
PW_HEADER_NAME=X-Automated-By PW_HEADER_VALUE=playwright-skill \
cd $SKILL_DIR && node run.js /tmp/my-script.js
多 Header(JSON 格式):
PW_EXTRA_HEADERS='{"X-Automated-By":"playwright-skill","X-Debug":"true"}' \
cd $SKILL_DIR && node run.js /tmp/my-script.js
工作原理
当使用 helpers.createContext() 时会自动应用这些 Header:
const context = await helpers.createContext(browser);
const page = await context.newPage();
// 该页面发送的所有请求都会包含你的自定义 Header
对于使用原生 Playwright API 的脚本,请使用注入的 getContextOptionsWithHeaders():
const context = await browser.newContext(
getContextOptionsWithHeaders({ viewport: { width: 1920, height: 1080 } }),
);
高级用法
关于 Playwright API 的完整文档,请参阅 API_REFERENCE.md:
- 选择器与定位器(Selectors & Locators)最佳实践
- 网络拦截与 API Mock
- 身份验证与 Session 管理
- 视觉回归测试
- 移动端设备模拟
- 性能测试
- 调试技巧
- CI/CD 集成
使用建议
- 关键提示:先检测服务器 - 在为 localhost 测试编写代码前,务必先运行
detectDevServers() - 自定义 Header - 使用
PW_HEADER_NAME/PW_HEADER_VALUE环境变量向后端标识自动化流量 - 测试文件统一存放在 /tmp - 将代码写入
/tmp/playwright-test-*.js,切勿写入 Skill 目录或用户的项目目录中 - URL 参数化 - 在每个脚本顶部将检测到或提供的 URL 赋值给常量
TARGET_URL - 默认模式:显示浏览器窗口 - 除非用户明确要求无头模式,否则始终使用
headless: false - 无头模式 - 仅当用户明确要求“无头(headless)”或“后台”运行时才使用
headless: true - 减慢执行速度: 使用
slowMo: 100让操作过程清晰可见、便于跟进 - 等待策略: 优先使用
waitForURL、waitForSelector、waitForLoadState,而非固定等待时长 - 错误处理
<!-- truncated for translation batch; full body continues in source -->




