SurfSense/.cursor/skills/playwright-testing/infrastructure-ci-cd/ci-cd.md
2026-05-10 04:19:55 +05:30

9.8 KiB

CI/CD Integration

Table of Contents

  1. GitHub Actions
  2. Docker
  3. Reporting
  4. Sharding
  5. Environment Management
  6. Caching

GitHub Actions

Basic Workflow

# .github/workflows/playwright.yml
name: Playwright Tests

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps

      - name: Run Playwright tests
        run: npx playwright test

      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

With Sharding

name: Playwright Tests

on:
  push:
    branches: [main]

jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    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: 22
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps

      - name: Run Playwright tests
        run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}

      - name: Upload blob report
        if: ${{ !cancelled() }}
        uses: actions/upload-artifact@v4
        with:
          name: blob-report-${{ matrix.shardIndex }}
          path: blob-report
          retention-days: 1

  merge-reports:
    if: ${{ !cancelled() }}
    needs: [test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Download blob reports
        uses: actions/download-artifact@v4
        with:
          path: all-blob-reports
          pattern: blob-report-*
          merge-multiple: true

      - name: Merge reports
        run: npx playwright merge-reports --reporter html ./all-blob-reports

      - name: Upload HTML report
        uses: actions/upload-artifact@v4
        with:
          name: html-report
          path: playwright-report
          retention-days: 14

With Container

jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    container:
      # Use latest or more appropriate playwright version (match package.json)
      image: mcr.microsoft.com/playwright:v1.40.0-jammy
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npx playwright test
        env:
          HOME: /root

Docker

Dockerfile

FROM mcr.microsoft.com/playwright:v1.40.0-jammy

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .

CMD ["npx", "playwright", "test"]

Docker Compose

# docker-compose.yml
version: "3.8"

services:
  playwright:
    build: .
    volumes:
      - ./playwright-report:/app/playwright-report
      - ./test-results:/app/test-results
    environment:
      - CI=true
      - BASE_URL=http://app:3000
    depends_on:
      - app

  app:
    build: ./app
    ports:
      - "3000:3000"

Run with Docker

# Build and run
docker build -t playwright-tests .
docker run --rm -v $(pwd)/playwright-report:/app/playwright-report playwright-tests

# With docker-compose
docker-compose run --rm playwright

Reporting

Configuration

// playwright.config.ts
export default defineConfig({
  reporter: [
    // Always generate
    ["html", { outputFolder: "playwright-report" }],

    // Console output
    ["list"],

    // CI-friendly
    ["github"], // GitHub Actions annotations

    // JUnit for CI integration
    ["junit", { outputFile: "results.xml" }],

    // JSON for custom processing
    ["json", { outputFile: "results.json" }],

    // Blob for merging shards
    ["blob", { outputDir: "blob-report" }],
  ],
});

CI-Specific Reporter

export default defineConfig({
  reporter: process.env.CI
    ? [["github"], ["blob"], ["html"]]
    : [["list"], ["html"]],
});

Sharding

Command Line

# Split into 4 shards, run shard 1
npx playwright test --shard=1/4

# Run shard 2
npx playwright test --shard=2/4

Configuration

// playwright.config.ts
export default defineConfig({
  // Evenly distribute tests across shards
  fullyParallel: true,

  // For blob reporter to merge later
  reporter: process.env.CI ? [["blob"]] : [["html"]],
});

Merge Sharded Reports

# After all shards complete, merge blob reports
npx playwright merge-reports --reporter html ./all-blob-reports

Environment Management

Environment Variables

// playwright.config.ts
import { defineConfig } from "@playwright/test";
import dotenv from "dotenv";

// Load env file based on environment
dotenv.config({ path: `.env.${process.env.NODE_ENV || "development"}` });

export default defineConfig({
  use: {
    baseURL: process.env.BASE_URL || "http://localhost:3000",
  },
});

Multiple Environments

# .github/workflows/playwright.yml
jobs:
  test:
    strategy:
      matrix:
        environment: [staging, production]
    steps:
      - name: Run tests
        run: npx playwright test
        env:
          BASE_URL: ${{ matrix.environment == 'staging' && 'https://staging.example.com' || 'https://example.com' }}
          TEST_USER: ${{ secrets[format('TEST_USER_{0}', matrix.environment)] }}

Secrets Management

# GitHub Actions secrets
- name: Run tests
  run: npx playwright test
  env:
    TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
    TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
// tests use environment variables
test("login", async ({ page }) => {
  await page.getByLabel("Email").fill(process.env.TEST_EMAIL!);
  await page.getByLabel("Password").fill(process.env.TEST_PASSWORD!);
});

Caching

Cache Playwright Browsers

- name: Cache Playwright browsers
  uses: actions/cache@v4
  id: playwright-cache
  with:
    path: ~/.cache/ms-playwright
    key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

- name: Install Playwright browsers
  if: steps.playwright-cache.outputs.cache-hit != 'true'
  run: npx playwright install --with-deps

- name: Install system deps only
  if: steps.playwright-cache.outputs.cache-hit == 'true'
  run: npx playwright install-deps

Cache Node Modules

- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: "npm"

- name: Install dependencies
  run: npm ci

Tag-Based Test Filtering

Run Specific Tags in CI

# Run smoke tests on PR
- name: Run smoke tests
  run: npx playwright test --grep @smoke

# Run full regression nightly
- name: Run regression
  run: npx playwright test --grep @regression

# Exclude flaky tests
- name: Run stable tests
  run: npx playwright test --grep-invert @flaky

PR vs Nightly Strategy

# .github/workflows/pr.yml - Fast feedback
- name: Run critical tests
  run: npx playwright test --grep "@smoke|@critical"

# .github/workflows/nightly.yml - Full coverage
- name: Run all tests
  run: npx playwright test --grep-invert @flaky

Tag Filtering in Config

// playwright.config.ts
export default defineConfig({
  grep: process.env.CI ? /@smoke|@critical/ : undefined,
  grepInvert: process.env.CI ? /@flaky/ : undefined,
});

Project-Based Tag Filtering

// playwright.config.ts
export default defineConfig({
  projects: [
    {
      name: "smoke",
      grep: /@smoke/,
    },
    {
      name: "regression",
      grepInvert: /@smoke/,
    },
  ],
});

Best Practices

Practice Benefit
Use npm ci Deterministic installs
Run headless in CI Faster, no display needed
Set retries in CI only Handle flakiness
Upload artifacts on failure Debug failures
Use sharding for large suites Faster execution
Cache browsers Faster setup
Use blob reporter for shards Merge reports correctly
Use tags for PR vs nightly Fast feedback + coverage
Exclude @flaky in CI Stable pipeline

CI Configuration Reference

// playwright.config.ts - CI optimized
export default defineConfig({
  testDir: "./tests",
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: process.env.CI
    ? [["github"], ["blob"], ["html"]]
    : [["list"], ["html"]],
  use: {
    baseURL: process.env.BASE_URL || "http://localhost:3000",
    trace: "on-first-retry",
    screenshot: "only-on-failure",
    video: "on-first-retry",
  },
});
  • Test tags: See test-tags.md for tagging and filtering patterns
  • Performance optimization: See performance.md for sharding and parallelization
  • Debugging CI failures: See debugging.md for troubleshooting
  • Test reporting: See debugging.md for trace viewer usage