playwright-skill

playwright-skill

热门

【重要 - 路径解析】此 Skill 可能安装在不同的位置(如插件系统、手动安装、全局或特定项目)。在执行任何命令之前,请先根据加载此 SKILL.md 文件的位置确定 Skill 所在目录,并在后续所有命令中使用该路径。

4.4万Star
6463Fork
更新于 2026/7/21
SKILL.md
只读
名称
playwright-skill
描述

【重要 - 路径解析】此 Skill 可能安装在不同的位置(如插件系统、手动安装、全局或特定项目)。在执行任何命令之前,请先根据加载此 SKILL.md 文件的位置确定 Skill 所在目录,并在后续所有命令中使用该路径。

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

常见安装路径:

  • 插件系统:<plugin-root>/skills/playwright-skill
  • 手动全局安装:<agent-home>/skills/playwright-skill
  • 特定项目内部:<project>/.agent/skills/playwright-skill

Playwright 浏览器自动化

通用浏览器自动化 Skill。我会根据你的自动化需求编写自定义 Playwright 代码,并通过通用执行器完成操作。

核心工作流 - 请严格按以下顺序执行:

  1. 自动检测本地开发服务器 - 针对 localhost 测试,务必先运行服务器检测命令:

    cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(servers => console.log(JSON.stringify(servers)))"
    
    • 如果找到 1 个服务器:直接自动使用,并告知用户
    • 如果找到多个服务器:询问用户具体要测试哪一个
    • 如果未找到服务器:提示用户提供 URL,或询问是否需要协助启动开发服务器
  2. 测试脚本统一写入 /tmp - 绝不要把测试文件写到 Skill 目录中,始终保存为 /tmp/playwright-test-*.js

  3. 默认打开有头(显示)浏览器 - 除非用户明确要求使用无头(headless)模式,否则始终使用 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:在 /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('截图已保存');
await browser.close();
"

何时用内联命令 vs 文件脚本:

  • 内联命令:快速单次任务(例如临时截个图、检查某个元素在不在、抓取页面标题)
  • 文件脚本:复杂的逻辑测试、响应式排查,或是后续可能需要重复运行的脚本

辅助函数库 (Helpers)

lib/helpers.js 中内置了各种开箱即用的实用工具函数:

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

// 检测正在运行的开发服务器(非常关键,建议优先调用!)
const servers = await helpers.detectDevServers();
console.log('已找到的服务器:', servers);

// 带重试机制的安全点击
await helpers.safeClick(page, 'button.submit', { retries: 3 });

// 带自动清空功能的输入框填写
await helpers.safeType(page, '#username', 'testuser');

// 截取带时间戳的截图
await helpers.takeScreenshot(page, 'test-result');

// 自动处理 Cookie 弹窗通知
await helpers.handleCookieBanner(page);

// 提取表格数据
const data = await helpers.extractTableData(page, 'table.results');

完整列表参见 lib/helpers.js

自定义 HTTP 请求头 (Headers)

可以通过环境变量全局配置所有 HTTP 请求的 Header。常用于:

  • 向后端标注自动化测试的流量标识
  • 获取适合大语言模型解析的响应(例如纯文本错误信息而非带样式的 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 Mocking
  • 用户认证与 Session 会话状态管理
  • 视觉回归测试 (Visual regression testing)
  • 移动端设备模拟
  • 性能基准测试
  • 调试与排错技巧
  • CI/CD 自动化集成

踩坑经验与技巧

  • 关键点:务必先检测服务器 - 在为 localhost 编写任何测试代码前,先执行 detectDevServers()
  • 自定义 请求头 - 善用 PW_HEADER_NAME/PW_HEADER_VALUE 环境变量来标识发送给后端的测试流量
  • 测试代码存放至 /tmp - 脚本统一保存为 /tmp/playwright-test-*.js,切勿直接保存在 Skill 目录或用户的项目根目录下
  • URL 参数化 - 将检测到或用户指定的 URL 提取为脚本顶部全局的 TARGET_URL 常量
  • 默认策略:开窗可视化运行 - 始终使用 headless: false,除非用户明确要求在后台无头运行
  • 无头模式 - 仅当用户明确要求“无界面”或“后台静默”执行时才开启 headless: true
  • 放慢执行速度: 设置 slowMo: 100 可以降低步骤速度,便于清晰观察自动化操作流程
  • 等待策略: