Answer
Playwright + GitHub Actions
Basic Workflow
YAML
# .github/workflows/playwright.yml
name: Playwright Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
playwright:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
# 1. Checkout code
- uses: actions/checkout@v4
# 2. Set up Node.js
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
# 3. Install npm dependencies
- name: Install dependencies
run: npm ci
# 4. Install Playwright browsers + OS dependencies
- name: Install Playwright browsers
run: npx playwright install --with-deps
# 5. Run tests
- name: Run Playwright tests
run: npx playwright test
env:
BASE_URL: ${{ secrets.STAGING_URL }}
TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
# 6. Upload HTML report (always, even on failure)
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 30
Sharded Matrix Workflow (4 Parallel Machines)
YAML
name: Playwright Tests (Sharded)
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: 'npm' }
- run: npm ci
- run: npx playwright install --with-deps
- name: Run shard
run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
env:
BASE_URL: ${{ secrets.BASE_URL }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: blob-report-${{ matrix.shardIndex }}
path: blob-report/
merge-reports:
needs: test
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: 'npm' }
- run: npm ci
- uses: actions/download-artifact@v4
with: { path: all-reports, pattern: blob-report-* }
- run: npx playwright merge-reports --reporter html ./all-reports
- uses: actions/upload-artifact@v4
with: { name: playwright-report, path: playwright-report/ }
playwright.config.ts for CI
TypeScript
export default defineConfig({
workers: process.env.CI ? 2 : undefined,
retries: process.env.CI ? 2 : 0,
reporter: process.env.CI ? [['github'], ['html']] : [['html']],
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
});
Caching Browser Binaries
YAML
- name: Cache Playwright browsers
uses: actions/cache@v4
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}
- name: Install browsers (only if not cached)
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: npx playwright install --with-deps
