playwright-skill

playwright-skill

熱門

重要提示 - 路徑解析:此 Skill 可安裝於不同位置(外掛系統、手動安裝、全域或專案特定)。在執行任何命令之前,請先根據載入此 SKILL.md 檔案的位置確認 Skill 目錄,並在下方所有命令中使用該路徑。

4.4萬星標
6463分支
更新於 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 程式碼,並透過通用執行器(universal executor)執行。

關鍵工作流程 - 請依序執行以下步驟:

  1. 自動偵測開發伺服器(Dev Server) - 進行 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 mode),否則一律使用 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');

// 參數化 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('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:從 Skill 目錄執行

cd $SKILL_DIR && node run.js /tmp/playwright-test-page.js

常見模式

測試頁面(多種 Viewport 尺寸)

// /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('Desktop - Title:', 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('✅ Login successful, redirected to dashboard');

  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('✅ Form submitted successfully');

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

檢查失效連結 (Broken Links)

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();
  }
})();

測試響應式設計 (Responsive Design)

// /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(
      `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();
})();

行內執行(簡單任務)

對於快速的一次性任務,你可以直接使用行內命令執行程式碼,無需建立檔案:

# 快速擷取螢幕截圖
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 檔案:

  • 行內執行:快速一次性任務(如拍照截圖、檢查元素是否存在、取得頁面標題)
  • 檔案形式:複雜測試、響應式設計檢查,或任何使用者可能需要重新執行的內容

可用輔助函式 (Helpers)

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 彈窗橫幅
await helpers.handleCookieBanner(page);

// 擷取表格資料
const data = await helpers.extractTableData(page, 'table.results');

完整清單請參閱 lib/helpers.js

客製化 HTTP 標頭 (Custom HTTP Headers)

可透過環境變數為所有 HTTP 請求設定客製化 Header。適用於:

  • 標示傳送至後端的自動化流量
  • 取得適合 LLM 處理的回應(例如純文字錯誤而非帶有樣式的 HTML)
  • 在全域範圍加入身分驗證 Token

設定方式

單一標頭(常見用法):

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();
// 此頁面的所有請求都會包含你自訂的 Headers

若腳本直接使用 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)」或「背景 (background)」執行時使用 headless: true
  • 放慢執行速度: 使用 slowMo: 100 讓操作過程可視化且更容易追蹤
  • 等待策略: