mcpField
P
playwright-skill

playwright-skill

github.com/zenobi-us测试与质量6837 次安装今天更新
代码仓库

关于 playwright-skill

playwright-skill 是用于编写和运行 Playwright 浏览器自动化的技能,面向处理网页测试与重复浏览器任务的 Agent。它规定了服务器检测、临时脚本和默认显示浏览器的执行流程。

主要功能

  1. 01检测本地开发服务器
  2. 02运行自定义 Playwright 脚本
  3. 03将脚本放在 /tmp
  4. 04支持配置 URL 和请求头
  5. 05提供浏览器自动化辅助工具

适用场景

  1. 01检查登录与表单流程
  2. 02测试响应式布局
  3. 03查找失效链接
  4. 04截取网页截图

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/zenobi-us/dotfiles --skill playwright-skill

安装后在 Claude Code 中输入/playwright-skill确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

.temp-execution-1770342857274.js
API_REFERENCE.md
package.json
run.js
lib/
helpers.js

详细介绍

重要 - 路径解析: 此技能可安装在不同位置(插件系统、手动安装、全局或项目特定)。在执行任何命令之前,请根据你加载此 SKILL.md 文件的位置确定技能目录,并在以下所有命令中使用该路径。将 $SKILL_DIR 替换为实际发现的路径。

常见安装路径:

  • 插件系统:~/.claude/plugins/marketplaces/playwright-skill/skills/playwright-skill
  • 手动全局安装:~/.claude/skills/playwright-skill
  • 项目特定:<project>/.claude/skills/playwright-skill

Playwright 浏览器自动化

通用浏览器自动化技能。我将为你请求的任何自动化任务编写自定义 Playwright 代码,并通过通用执行器执行。

关键工作流 - 按顺序遵循以下步骤:

  1. 自动检测开发服务器 - 对于 localhost 测试,务必首先运行服务器检测:

    cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(servers => console.log(JSON.stringify(servers)))"
    
    • 如果找到 1 个服务器:自动使用它,并告知用户
    • 如果找到多个服务器:询问用户要测试哪一个
    • 如果未找到服务器:询问 URL 或提出帮助启动开发服务器
  2. 将脚本写入 /tmp - 切勿将测试文件写入技能目录;始终使用 /tmp/playwright-test-*.js

  3. 默认使用可见浏览器 - 始终使用 headless: false,除非用户明确要求无头模式

  4. 参数化 URL - 始终通过环境变量或脚本顶部的常量使 URL 可配置

工作原理

  1. 你描述想要测试/自动化的内容
  2. 我自动检测正在运行的开发服务器(如果测试外部站点,则询问 URL)
  3. 我在 /tmp/playwright-test-*.js 中编写自定义 Playwright 代码(不会弄乱你的项目)
  4. 我通过以下命令执行:cd $SKILL_DIR && node run.js /tmp/playwright-test-*.js
  5. 结果实时显示,浏览器窗口可见以便调试
  6. 测试文件由你的操作系统自动从 /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:将带 URL 参数的测试脚本写入 /tmp

// /tmp/playwright-test-page.js
const { chromium } = require('playwright');

// Parameterized URL (detected or user-provided)
const TARGET_URL = 'http://localhost:3001'; // <-- Auto-detected or from user

(async () => {
  const browser = await chromium.launch({ headless: false });
  const page = await browser.newPage();

  await page.goto(TARGET_URL);
  console.log('Page loaded:', await page.title());

  await page.screenshot({ path: '/tmp/screenshot.png', fullPage: true });
  console.log('📸 Screenshot saved to /tmp/screenshot.png');

  await browser.close();
})();

步骤 3:从技能目录执行

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'; // Auto-detected

(async () => {
  const browser = await chromium.launch({ headless: false, slowMo: 100 });
  const page = await browser.newPage();

  // Desktop test
  await page.setViewportSize({ width: 1920, height: 1080 });
  await page.goto(TARGET_URL);
  console.log('Desktop - Title:', await page.title());
  await page.screenshot({ path: '/tmp/desktop.png', fullPage: true });

  // Mobile test
  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'; // Auto-detected

(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"]', '[email protected]');
  await page.fill('input[name="password"]', 'password123');
  await page.click('button[type="submit"]');

  // Wait for redirect
  await page.waitForURL('**/dashboard');
  console.log('✅ Login successful, redirected to dashboard');

  await browser.close();
})();
填写并提交表单
// /tmp/playwright-test-form.js
const { chromium } = require('playwright');

const TARGET_URL = 'http://localhost:3001'; // Auto-detected

(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"]', '[email protected]');
  await page.fill('textarea[name="message"]', 'Test message');
  await page.click('button[type="submit"]');

  // Verify submission
  await page.waitForSelector('.success-message');
  console.log('✅ Form submitted successfully');

  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(`✅ Working links: ${results.working}`);
  console.log(`❌ Broken links:`, 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('📸 Screenshot saved to /tmp/screenshot.png');
  } catch (error) {
    console.error('❌ Error:', error.message);
  } finally {
    await browser.close();
  }
})();
测试响应式设计
// /tmp/playwright-test-responsive-full.js
const { chromium } = require('playwright');

const TARGET_URL = 'http://localhost:3001'; // Auto-detected

(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(
      `Testing ${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('✅ All viewports tested');
  await browser.close();
})();

内联执行(简单任务)

对于快速的一次性任务,你可以内联执行代码而无需创建文件:

# Take a quick screenshot
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();
"

何时使用内联与文件:

  • 内联:快速的一次性任务(截图、检查元素是否存在、获取页面标题)
  • 文件:复杂测试、响应式设计检查、用户可能想要重新运行的所有内容

可用辅助函数

lib/helpers.js 中的可选实用函数:

const helpers = require('./lib/helpers');

// Detect running dev servers (CRITICAL - use this first!)
const servers = await helpers.detectDevServers();
console.log('Found servers:', servers);

// Safe click with retry
await helpers.safeClick(page, 'button.submit', { retries: 3 });

// Safe type with clear
await helpers.safeType(page, '#username', 'testuser');

// Take timestamped screenshot
await helpers.takeScreenshot(page, 'test-result');

// Handle cookie banners
await helpers.handleCookieBanner(page);

// Extract table data
const data = await helpers.extractTableData(page, 'table.results');

完整列表请参见 lib/helpers.js。

自定义 HTTP 头

通过环境变量为所有 HTTP 请求配置自定义头。适用于:

  • 向你的后端标识自动化流量
  • 获取针对大模型优化的响应(例如,纯文本错误而非样式化 HTML)
  • 全局添加身份验证令牌
配置

单个头(常见情况):

PW_HEADER_NAME=X-Automated-By PW_HEADER_VALUE=playwright-skill \
  cd $SKILL_DIR && node run.js /tmp/my-script.js

多个头(JSON 格式):

PW_EXTRA_HEADERS='{"X-Automated-By":"playwright-skill","X-Debug":"true"}' \
  cd $SKILL_DIR && node run.js /tmp/my-script.js
工作原理

使用 helpers.createContext() 时,头会自动应用:

const context = await helpers.createContext(browser);
const page = await context.newPage();
// All requests from this page include your custom headers

对于使用原始 Playwright API 的脚本,请使用注入的 getContextOptionsWithHeaders():

const context = await browser.newContext(
  getContextOptionsWithHeaders({ viewport: { width: 1920, height: 1080 } }),
);

高级用法

有关全面的 Playwright API 文档,请参见 API_REFERENCE.md:

  • 选择器与定位器最佳实践
  • 网络拦截与 API 模拟
  • 身份验证与会话管理
  • 视觉回归测试
  • 移动设备仿真
  • 性能测试
  • 调试技术
  • CI/CD 集成

提示

  • 关键:先检测服务 - 在编写用于 localhost 测试的测试代码之前,始终先运行 detectDevServers()
  • 自定义请求头 - 使用 PW_HEADER_NAME/PW_HEADER_VALUE 环境变量来标识发往后端的自动化流量
  • 将测试文件放在 /tmp 中 - 写入 /tmp/playwright-test-*.js,切勿写入 skill 目录或用户的项目
  • 参数化 URL - 将检测到/提供的 URL 放入每个脚本顶部的 TARGET_URL 常量中
  • 默认:可见浏览器 - 始终使用 headless: false,除非用户明确要求无头模式
  • 无头模式 - 仅当用户明确要求 "headless" 或 "background" 执行时才使用 headless: true
  • 放慢速度: 使用 slowMo: 100 让操作可见且更易跟随
  • 等待策略: 使用 waitForURL、waitForSelector、waitForLoadState 代替固定超时
  • 错误处理: 始终使用 try-catch 实现稳健的自动化
  • 控制台输出: 使用 console.log() 跟踪进度并显示正在发生的情况

故障排查

Playwright 未安装:

cd $SKILL_DIR && npm run setup

找不到模块: 确保通过 run.js 包装器从 skill 目录运行

浏览器无法打开: 检查 headless: false 并确保显示可用

找不到元素: 添加等待:await page.waitForSelector('.element', { timeout: 10000 })

使用示例

User: "Test if the marketing page looks good"

Claude: I'll test the marketing page across multiple viewports. Let me first detect running servers...
[Runs: detectDevServers()]
[Output: Found server on port 3001]
I found your dev server running on http://localhost:3001

[Writes custom automation script to /tmp/playwright-test-marketing.js with URL parameterized]
[Runs: cd $SKILL_DIR && node run.js /tmp/playwright-test-marketing.js]
[Shows results with screenshots from /tmp/]
User: "Check if login redirects correctly"

Claude: I'll test the login flow. First, let me check for running servers...
[Runs: detectDevServers()]
[Output: Found servers on ports 3000 and 3001]
I found 2 dev servers. Which one should I test?
- http://localhost:3000
- http://localhost:3001

User: "Use 3001"

[Writes login automation to /tmp/playwright-test-login.js]
[Runs: cd $SKILL_DIR && node run.js /tmp/playwright-test-login.js]
[Reports: ✅ Login successful, redirected to /dashboard]

备注

  • 每项自动化都是针对你的具体请求定制的
  • 不限于预构建脚本 - 任何浏览器任务都可以实现
  • 自动检测正在运行的开发服务,消除硬编码 URL
  • 测试脚本写入 /tmp 以便自动清理(无杂乱)
  • 通过 run.js 进行正确的模块解析,代码可靠执行
  • 渐进式披露 - 仅在需要高级功能时加载 API_REFERENCE.md

评论

支持 Markdown