GitHub Actions × Playwright 完整實戰 — yml、PR 留言、Artifact、Sharding
從零配出 production-ready 的 Playwright CI。完整 yml、失敗截圖/trace 自動上傳、PR 自動留言報告、平行 sharding 加速。
💡 本文原刊於 qa.9niche.com,2026-08 併入 9niche.com 懶人包,內容照原文完整搬遷。
目錄
1. 前言
Playwright 預設生出來的 yml 只能算「跑得起來」。要 production-ready 還缺:artifact 上傳、PR 留言、平行 sharding、快取。這篇給你一份能直接 copy 的 yml。
2. 起點:預設 yml 不夠用
npm init playwright@latest 會幫你生這個:
name: Playwright Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 7
問題:
- 沒快取 → 每次重裝 Playwright 瀏覽器(>1 GB、~2 分鐘)
- PR 看不到測試結果 → 要點進 Actions 找 artifact
- 只跑單一 worker → 慢
- Trace、screenshot 沒分開 upload → 點開 zip 找半天
3. Production-ready yml(直接 copy)
name: E2E Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
# 同個 PR 推新 commit 時,取消舊跑的
concurrency:
group: e2e-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
test:
name: E2E (Shard ${{ matrix.shard }}/4)
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false # 一個 shard 壞不影響其他
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install deps
run: npm ci
# ⚡ Playwright 瀏覽器快取(最大時間殺手)
- name: Get Playwright version
id: pw-version
run: |
echo "version=$(npm ls @playwright/test --json | jq -r '.dependencies["@playwright/test"].version')" >> $GITHUB_OUTPUT
- name: Cache Playwright browsers
id: pw-cache
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: pw-browsers-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
- name: Install Playwright browsers
if: steps.pw-cache.outputs.cache-hit != 'true'
run: npx playwright install --with-deps
- name: Install system deps only (cache hit)
if: steps.pw-cache.outputs.cache-hit == 'true'
run: npx playwright install-deps
# 跑測試(用 shard 平行)
- name: Run Playwright tests
run: npx playwright test --shard=${{ matrix.shard }}/4
env:
BASE_URL: ${{ secrets.STAGING_URL }}
TEST_USER: ${{ secrets.TEST_USER }}
TEST_PASS: ${{ secrets.TEST_PASS }}
# 上傳 HTML report
- name: Upload HTML report
if: always()
uses: actions/upload-artifact@v4
with:
name: html-report-shard-${{ matrix.shard }}
path: playwright-report/
retention-days: 7
# 上傳 traces(失敗 case 的,給 debug 用)
- name: Upload traces
if: failure()
uses: actions/upload-artifact@v4
with:
name: traces-shard-${{ matrix.shard }}
path: test-results/
retention-days: 7
# 合併 4 個 shard 報告 + PR 留言
report:
name: Merge reports & comment PR
if: always()
needs: [test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- name: Install deps
run: npm ci
# 下載所有 shard 的 blob report
- name: Download blob reports
uses: actions/download-artifact@v4
with:
path: all-blob-reports
pattern: blob-report-*
merge-multiple: true
- name: Merge into single HTML report
run: npx playwright merge-reports --reporter html ./all-blob-reports
- name: Upload merged HTML report
uses: actions/upload-artifact@v4
with:
name: html-report-merged
path: playwright-report/
retention-days: 14
# 從 JSON summary 抽 pass/fail 數字
- name: Comment on PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const summary = JSON.parse(fs.readFileSync('./all-blob-reports/summary.json', 'utf8'));
const { passed, failed, skipped, duration } = summary;
const status = failed > 0 ? '❌ Failed' : '✅ Passed';
const body = `## ${status} — Playwright E2E
| Passed | Failed | Skipped | Duration |
|--------|--------|---------|----------|
| ${passed} | ${failed} | ${skipped} | ${(duration/1000).toFixed(1)}s |
📊 [Full HTML report](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})
`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body,
});
4. 關鍵設計解釋
1. Concurrency cancel-in-progress
同 PR 推新 commit 時,自動取消正在跑的 CI,省 runner minutes。main 不取消(避免漏跑)。
2. Playwright 瀏覽器快取
最大時間殺手是 playwright install --with-deps(裝 Chromium + Firefox + WebKit 約 1-2 分鐘)。
關鍵:key 要包 Playwright 版本,不然版本升上去快取就壞了。
升版時自動 cache miss、重灌。
3. Shard 平行
npx playwright test --shard=1/4
npx playwright test --shard=2/4
npx playwright test --shard=3/4
npx playwright test --shard=4/4
4 個 runner 同時跑、總時長除以 4。Matrix 跑出來 4 個 job 一起。
幾個 shard 合適? 看你的測試數:
- < 50 個 test → 不用 shard
- 50-200 → shard 2-4
- 200+ → shard 4-8
shard 太多會被 GitHub Actions free tier minute 燒爆。
4. Trace 跟 HTML report 分開傳
- HTML report:永遠傳,給 review 看
- Trace:只有 fail 時傳,因為很大(單個 case 可能 5-50 MB)
Trace viewer 開啟:
npx playwright show-trace trace.zip
直接看到當下 DOM、network、console、step screenshot。Trace 是 debug 神器。
5. PR 自動留言
把測試結果 inline 在 PR 上,review 不用點 Actions。這是 dev 最有感的 QA 投資。
5. 設定要點
playwright.config.ts(搭配上面 yml)
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0, // CI 上 fail retry 2 次
workers: process.env.CI ? 4 : undefined,
reporter: process.env.CI
? [['blob'], ['html', { open: 'never' }], ['json', { outputFile: 'summary.json' }]]
: 'html',
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: 'retain-on-failure', // fail 才存 trace
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
// 起步先單一瀏覽器、穩定後加
// { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
// { name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
});
Secrets 設定
GitHub repo → Settings → Secrets and variables → Actions:
STAGING_URL— staging 環境網址TEST_USER/TEST_PASS— 測試用帳號
不要把這些寫死在 yml 或 code。
6. 跑得起來後的優化清單
- 拆 PR vs main 行為
on: pull_request: branches: [main]PR 只跑 smoke + 改動的 spec / main 跑 full
- 依改動檔案決定跑什麼
- uses: dorny/paths-filter@v3 id: filter with: filters: | frontend: - 'src/**' - if: steps.filter.outputs.frontend == 'true' run: npx playwright test - 失敗時自動 retry 整個 job
- uses: nick-fields/retry@v3 with: max_attempts: 2 command: npx playwright test - timeout 設嚴格
jobs: test: timeout-minutes: 30防止 hang job 燒 runner
- 失敗時通知 Slack
- if: failure() && github.ref == 'refs/heads/main' uses: slackapi/slack-github-action@v1 with: channel-id: 'qa-alerts' slack-message: 'main E2E failed: ${{ github.event.head_commit.message }}'
7. 常見坑
- 快取沒 invalidate → 升 Playwright 後不更新瀏覽器 → 跑舊版 bug
- shard 不均勻 → 某個 shard 跑 30 分鐘其他 5 分鐘 → 用
--shardPlaywright 自動分配,不要手動分檔案 - Test 改了 baseURL 但 secret 沒改 → 全 fail
- trace 沒上傳 → Debug 時沒料看
- PR 留言每次重發 → 蓋舊的不刪,PR 留言一坨。改用
peter-evans/find-comment找舊的、更新而不是新增
8. 最後
E2E + CI 跑通的那一刻,你會發現每個 PR 都自動驗證,QA 從「全部都要看」變「只看 CI 沒抓的」。剩下時間留給探索性測試、spec review、AI 工具 — 真正能加值的工作。
相關連結
給「會寫 test 但不懂 CI/CD」的 QA 看的入門。Pipeline 七階段、QA 在每一階段做什麼、quality gate 怎麼設、常見坑。
Cypress 完整入門 + 跟 Playwright 的真實對比。架構差異、Selector 哲學、Cross-origin / Multi-tab / 並行、生態系、價格、何時該選 / 何時該遷。
Playwright 安裝、第一個測試、Selectors 選擇策略、debug 技巧、CI 整合。給有測試經驗但沒寫過自動化的 QA。
相關懶人包
2026 QA 趨勢實戰:我看到的 5 個轉變(AI、Shift-Left、Observability)
從手動 QA 到 AI 輔助、從測試金字塔到測試獎盃。這篇分享我這 10+ 年看 QA 從「測完才知道」到「shift-left + AI」的真實觀察。
2026 QA 面試的 AI 題 — 12 題 + 答題框架(面試官想聽什麼)
2026 QA 面試新增一整類「你怎麼用 AI」的問題。這篇整理 12 個高頻 AI 面試題、每題附面試官真正想聽的點與答題框架,從「你用過哪些 AI 工具」到「AI 生的 test 怎麼信任」。
AI / LLM 功能 Spec Review — 幻覺 / 評估 / 成本 / 法遵 8 個必問
AI 功能 spec review 完整指南。LLM 不確定性處理、評估指標、Prompt versioning、成本控制、安全護欄、法遵(EU AI Act / GDPR)、Fallback、人工 review 流程。
一般聲明
本站提供之資訊僅供參考,不保證其完整性與正確性。使用者應自行判斷資訊之適用性。