Running Playwright in CI means installing browsers with their system dependencies, executing the suite (often sharded) headlessly, and uploading the HTML report and traces as artifacts so failures are diagnosable after the run.
The canonical GitHub Actions job checks out the repo, sets up Node, runs npm ci, then npx playwright install --with-deps to fetch browsers plus the Linux libraries they need. It runs npx playwright test (headless by default). Crucially, it uploads the playwright-report/ (and traces) as an artifact with if: always/!cancelled(), so you can open the report and traces for failed CI runs. For speed, a matrix shards the suite across jobs (--shard=${{ matrix.shard }}/N) and a final job merges the blob reports into one HTML report. Using the official mcr.microsoft.com/playwright container avoids installing system deps each run and, importantly, matches the OS used to generate visual baselines. Caching browser binaries further trims time.
A PR check runs the suite sharded across four GitHub Actions jobs inside the Playwright container. When a test fails, the reviewer downloads the merged HTML report artifact, opens the embedded trace for the failing test, and sees the exact DOM at failure — all without pulling the branch locally.
# .github/workflows/e2e.yml
name: e2e
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
container: mcr.microsoft.com/playwright:v1.50.0-jammy
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npx playwright test --shard=${{ matrix.shard }}/4
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: report-${{ matrix.shard }}
path: blob-report/
retention-days: 7Scenario: Playwright tests pass locally but the GitHub Actions job fails immediately with 'browserType.launch: Host system is missing dependencies' — and even after fixing that, reviewers can't debug failures because nothing is saved. Walk through the correct CI setup.