DEV Community

Shefali R
Shefali R

Posted on

Complete CI/CD Pipeline Integration for Automated Playwright Test Suites

1. Introduction & Workflow Architecture

Running end-to-end tests locally is fine for development, but automated regression testing requires a continuous integration (CI) workflow.

Executing browser automation inside isolated CI containers ensures that tests run against reproducible environments on every pull request.

+----------------+   +-----------------------+   +--------------------+
| Developer Push | --> | GitHub Actions/CI   | --> | Matrix Execution   |
| (Pull Request) |   | Pipeline Trigger      |   | (Shard 1/3...) |
+----------------+   +-----------------------+   +---------+----------+
                                                           |
                                                           v
                                                 +--------------------+
                                                 | HTML Report        |
                                                 | Artifacts          |
                                                 +--------------------+
Enter fullscreen mode Exit fullscreen mode

2. Playwright Configuration (playwright.config.ts)

Configure Playwright to adjust worker threads dynamically based on whether it runs locally or inside a CI environment:

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  /* Fail the build on CI if you accidentally left test.only in the source code. */
  forbidOnly: !!process.env.CI,
  /* Retry on CI only */
  retries: process.env.CI ? 2 : 0,
  /* Opt out of parallel tests on CI if resource-constrained, or limit worker count */
  workers: process.env.CI ? 2 : undefined,
  reporter: process.env.CI ? [['github'], ['html', { open: 'never' }]] : 'list',
  use: {
    baseURL: process.env.BASE_URL || '[https://staging.example.com](https://staging.example.com)',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
  ],
});
Enter fullscreen mode Exit fullscreen mode

3. GitHub Actions Workflow Configuration (.github/workflows/playwright.yml)

This production-ready workflow installs required system dependencies, caches Node modules, runs parallel test shards, and uploads HTML execution reports as build artifacts:

name: Playwright Regression Pipeline

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

jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shard: [1/3, 2/3, 3/3]
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: 20
        cache: 'npm'

    - name: Install dependencies
      run: npm ci

    - name: Install Playwright Browsers with dependencies
      run: npx playwright install --with-deps

    - name: Run Playwright tests (Shard ${{ matrix.shard }})
      run: npx playwright test --shard=${{ matrix.shard }}
      env:
        BASE_URL: ${{ secrets.STAGING_BASE_URL }}

    - name: Upload Test Report Artifact
      uses: actions/upload-artifact@v4
      if: ${{ !cancelled() }}
      with:
        name: playwright-report-shard-${{ strategy.job-index }}
        path: playwright-report/
        retention-days: 14
Enter fullscreen mode Exit fullscreen mode

4. Key Optimization Takeaways

  • Parallel Sharding: Utilizing matrix strategy (--shard=1/3) splits the test suite across multiple runner virtual machines, cutting overall CI build time by up to 60%.

  • Artifact Storage: Standardized actions/upload-artifact@v4 configurations preserve failure traces and video recordings without bloating git history.

  • Environment Security: Sensitivity parameters like staging endpoints and credentials should always be injected via repository secrets (${{ secrets.STAGING_BASE_URL }}) rather than hardcoded in project settings.

Top comments (0)