Q22 of 42 · Playwright
How do you test against multiple browsers in CI?
Short answer
Short answer: Define a project per browser in `playwright.config.ts`. The runner spawns each project's tests in parallel by default. In CI, optionally shard each project across machines via `--shard`. Match Playwright's bundled browser version with the runner OS to avoid system-lib drift.
Detail
Cross-browser CI for Playwright is mostly a config exercise.
Step 1: Define projects for each browser:
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
]
A single npx playwright test runs all three sets in parallel.
Step 2: Filter per CI job if you want each browser on its own runner:
strategy:
matrix:
browser: [chromium, firefox, webkit]
steps:
- run: npx playwright test --project=${{ matrix.browser }}
This uses GitHub Actions' matrix to spawn three parallel runners, each handling one browser. You get faster wall time at the cost of triple the runner usage.
Step 3: Shard each browser for more parallelism:
strategy:
matrix:
browser: [chromium, firefox, webkit]
shard: [1, 2, 3]
steps:
- run: npx playwright test --project=${{ matrix.browser }} --shard=${{ matrix.shard }}/3
That's 9 parallel jobs (3 browsers × 3 shards). Wall time drops further; runner cost rises.
Practical decisions:
- Don't run cross-browser on every PR. Run Chromium on PR for fast feedback; run all three on PR-to-main or nightly.
- Pin Playwright version in CI matching your local. Playwright version determines browser binary versions; mixing produces inconsistent results.
- Linux base image with system libs. Use
mcr.microsoft.com/playwright:v1.x.y-jammyor runnpx playwright install-depsto install required system packages. - Smoke vs full. Some teams run a small smoke set on every browser, full regression on Chromium only. Cross-browser bugs are usually CSS / Date / focus-style; targeting smoke covers most.
WebKit caveats: WebKit on Linux ≠ Safari on macOS. Some genuinely-Safari behaviour (extensions, ITP, certain permission flows) doesn't reproduce. For high-fidelity Safari, BrowserStack / SauceLabs or a real Mac runner is required.
// EXAMPLE
.github/workflows/playwright.yml
name: playwright
on:
pull_request:
push:
branches: [main]
jobs:
pr-fast:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
container: mcr.microsoft.com/playwright:v1.45.0-jammy
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx playwright test --project=chromium
cross-browser:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
container: mcr.microsoft.com/playwright:v1.45.0-jammy
strategy:
fail-fast: false
matrix:
browser: [chromium, firefox, webkit]
shard: [1, 2, 3]
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx playwright test --project=${{ matrix.browser }} --shard=${{ matrix.shard }}/3