1 项目简介
P
Playwright Skill
v4.1.0 · MIT License
1.5k Stars
85 Forks
Playwright Skill 是一个为 Claude Code 设计的浏览器自动化插件。它使 Claude 能够根据用户请求实时编写和执行 Playwright 自动化代码,从简单的页面测试到复杂的多步骤工作流程。
核心理念
与预构建脚本不同,Claude 会为每个任务生成定制的 Playwright 代码,实现真正的自动化灵活性。
Node.js
运行环境
Playwright
自动化引擎
2 核心功能
自定义代码生成
Claude 为每个自动化请求编写定制的 Playwright 代码,而非使用预构建脚本
可视化浏览器
默认 headless: false,可实时观察自动化执行过程
模块解析
通用执行器消除模块访问错误,确保可靠执行
智能资源管理
自动清理临时文件,避免竞态条件
辅助函数库
提供常用自动化模式的可选工具函数
3 安装配置
环境要求
Node.js 14.0.0 或更高版本
使用 Claude Code 的插件市场系统,支持自动更新和团队分发:
# 从市场添加插件
/plugin marketplace add lackeyjb/playwright-skill
# 安装插件
/plugin install playwright-skill@playwright-skill
# 运行安装脚本
cd ~/.claude/plugins/marketplaces/playwright-skill/skills/playwright-skill
npm run setup
手动克隆并安装到全局 skills 目录:
# 克隆仓库
git clone https://github.com/lackeyjb/playwright-skill.git /tmp/playwright-skill-temp
# 复制到 skills 目录
mkdir -p ~/.claude/skills
cp -r /tmp/playwright-skill-temp/skills/playwright-skill ~/.claude/skills/
# 运行安装脚本
cd ~/.claude/skills/playwright-skill && npm run setup
将 skill 复制到项目的 .claude/skills/ 目录:
# 复制 skill 到项目目录
cp -r skills/playwright-skill <your-project>/.claude/skills/
# 运行安装脚本
cd <your-project>/.claude/skills/playwright-skill
npm run setup
注意
npm run setup 会安装 Playwright 及 Chromium 浏览器,只需运行一次。
4 工作流程
👤
描述需求
🤖
生成代码
▶️
执行脚本
📊
返回结果
关键步骤
1
服务器检测(必须首先执行)
cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(servers => console.log(JSON.stringify(servers)))"
2
创建脚本
脚本写入 /tmp/playwright-test-*.js(不要写入 skill 目录),使用 TARGET_URL 常量参数化 URL。
3
执行脚本
cd $SKILL_DIR && node run.js /tmp/playwright-test-*.js
5 辅助函数
lib/helpers.js 提供常用自动化模式的工具函数:
| 函数名 | 功能描述 |
|---|---|
detectDevServers() |
扫描本地端口,识别运行中的开发服务器 |
safeClick() |
带重试逻辑的点击操作(默认3次尝试) |
safeType() |
带重试逻辑的文本输入 |
takeScreenshot() |
带时间戳的截图功能 |
handleCookieBanner() |
自动处理 Cookie 同意横幅 |
extractTableData() |
解析 HTML 表格为结构化对象 |
createContext() |
创建带自定义 Headers 的浏览器上下文 |
authenticate() |
自动化登录流程 |
scrollPage() |
页面滚动(上/下/顶部/底部) |
retryWithBackoff() |
指数退避重试策略 |
6 API 参考
// 启动浏览器
const browser = await chromium.launch({ headless: false, slowMo: 100 });
// 创建隔离上下文
const context = await browser.newContext({
viewport: { width: 1920, height: 1080 },
userAgent: 'custom-agent'
});
// 创建新页面
const page = await context.newPage();
// 关闭浏览器
await browser.close();
// 推荐的选择方式
page.locator('[data-testid="submit"]') // data 属性
page.getByRole('button', { name: '提交' }) // 角色
page.getByText('欢迎') // 文本内容
page.getByLabel('用户名') // 表单标签
page.getByPlaceholder('请输入...') // 占位符
// 定位器链式调用
locator.filter({ hasText: '活跃' }) // 文本过滤
locator.nth(0) // 索引选择
locator.and(anotherLocator) // 组合条件
// 文本输入
await locator.fill('文本内容'); // 替换整个字段
await locator.type('文本', { delay: 50 }); // 模拟逐字输入
await locator.clear(); // 清空字段
// 表单控件
await locator.check(); // 勾选复选框
await page.selectOption('#dropdown', 'value'); // 下拉选择
await page.setInputFiles('#upload', 'file.pdf'); // 文件上传
// 鼠标操作
await page.click('#btn'); // 点击
await page.dblclick('#btn'); // 双击
await page.hover('#menu'); // 悬停
await page.dragAndDrop('#src', '#dest'); // 拖放
// 键盘操作
await page.keyboard.press('Enter'); // 按键
await page.keyboard.press('Control+A'); // 组合键
// 元素状态等待
await locator.waitFor({ state: 'visible' });
await locator.waitFor({ state: 'hidden' });
await locator.waitFor({ state: 'attached' });
// 页面加载等待
await page.waitForLoadState('networkidle');
await page.waitForLoadState('domcontentloaded');
// 自定义条件等待
await page.waitForFunction(() => document.title === '首页');
// 网络请求等待
await page.waitForResponse(resp => resp.url().includes('/api/'));
await page.waitForRequest(req => req.method() === 'POST');
// 页面级断言
await expect(page).toHaveTitle('首页');
await expect(page).toHaveURL(/dashboard/);
// 元素状态断言
await expect(locator).toBeVisible();
await expect(locator).toBeHidden();
await expect(locator).toBeEnabled();
await expect(locator).toBeDisabled();
await expect(locator).toBeChecked();
// 内容断言
await expect(locator).toHaveText('确切文本');
await expect(locator).toContainText('部分文本');
await expect(locator).toHaveValue('输入值');
await expect(locator).toHaveAttribute('href', '/link');
await expect(locator).toHaveCSS('color', 'red');
await expect(locator).toHaveCount(5);
// 拦截并修改请求
await page.route('**/api/**', route => {
route.continue({ headers: { ...route.request().headers(), 'X-Custom': 'value' } });
});
// 模拟响应
await page.route('**/api/data', route => {
route.fulfill({ status: 200, body: JSON.stringify({ mock: true }) });
});
// 阻止资源加载
await page.route('**/*.{png,jpg}', route => route.abort());
7 使用示例
测试不同视口尺寸的页面展示:
const viewports = [
{ name: 'desktop', width: 1920, height: 1080 },
{ name: 'tablet', width: 768, height: 1024 },
{ name: 'mobile', width: 375, height: 667 }
];
for (const vp of viewports) {
await page.setViewportSize({ width: vp.width, height: vp.height });
await page.screenshot({ path: `/tmp/${vp.name}.png`, fullPage: true });
}
await page.goto(TARGET_URL + '/login');
// 填写凭据
await page.fill('[name="username"]', 'testuser');
await page.fill('[name="password"]', 'password123');
// 提交表单
await page.click('button[type="submit"]');
// 验证登录成功
await page.waitForURL('**/dashboard');
await expect(page.getByText('欢迎')).toBeVisible();
await page.goto(TARGET_URL + '/register');
// 填写表单
await page.fill('#name', '张三');
await page.fill('#email', 'zhang@example.com');
await page.selectOption('#country', 'CN');
await page.check('#terms');
// 提交
await page.click('#submit');
// 验证成功
await page.waitForSelector('.success-message');
const msg = await page.textContent('.success-message');
console.log('注册结果:', msg);
await page.goto(TARGET_URL);
const links = await page.locator('a[href]').all();
const brokenLinks = [];
for (const link of links) {
const href = await link.getAttribute('href');
if (href.startsWith('http')) {
const response = await page.request.head(href);
if (!response.ok()) {
brokenLinks.push({ url: href, status: response.status() });
}
}
}
console.log('失效链接:', brokenLinks);
8 配置选项
默认配置
Headless 模式
false(可见浏览器)
慢动作延迟
100ms
超时时间
30 秒
截图目录
/tmp/
自定义 Headers
通过环境变量设置请求头:
# 单个 Header
PW_HEADER_NAME=X-Automated-By PW_HEADER_VALUE=playwright-skill
# 多个 Headers(JSON 格式)
PW_EXTRA_HEADERS='{"X-Automated-By":"playwright-skill","Authorization":"Bearer token"}'
安装路径
插件
~/.claude/plugins/marketplaces/playwright-skill/skills/playwright-skill
全局
~/.claude/skills/playwright-skill
项目
<project>/.claude/skills/playwright-skill