Skip to content

Test Execution

Advanced strategies and patterns for executing Playwright tests across different environments and scenarios.

Overview

Test execution encompasses:

  • Environment-specific execution across development, staging, and production
  • Parallel and distributed testing for optimal performance
  • Cross-browser and cross-device testing for comprehensive coverage
  • CI/CD integration patterns for automated testing workflows
  • Test result management and reporting strategies

Execution Strategies

Local Development Execution

flowchart TD
    DEV[Developer Machine] --> LOCAL_ENV[Local Environment]
    LOCAL_ENV --> QUICK[Quick Feedback Loop]
    QUICK --> INDIVIDUAL[Individual Test Files]
    QUICK --> BROWSER[Single Browser]
    QUICK --> DEBUG[Debug Mode]

    subgraph "Development Workflow"
        WRITE[Write Test] --> RUN[Run Test]
        RUN --> DEBUG_ISSUE[Debug Issues]
        DEBUG_ISSUE --> FIX[Fix Code]
        FIX --> WRITE
    end

    DEBUG --> WRITE

CI/CD Execution Pipeline

flowchart LR
    subgraph "Commit Stage"
        COMMIT[Code Commit] --> SMOKE[Smoke Tests]
        SMOKE --> FAST_FEEDBACK[< 5 minutes]
    end

    subgraph "Integration Stage"
        FAST_FEEDBACK --> FULL_SUITE[Full Test Suite]
        FULL_SUITE --> PARALLEL[Parallel Execution]
        PARALLEL --> MULTI_BROWSER[Multi-Browser]
    end

    subgraph "Deployment Stage"
        MULTI_BROWSER --> E2E[E2E Tests]
        E2E --> ACCEPTANCE[Acceptance Tests]
        ACCEPTANCE --> DEPLOY[Deploy]
    end

Production Monitoring

flowchart TD
    PROD[Production Environment] --> SYNTHETIC[Synthetic Tests]
    SYNTHETIC --> CRITICAL_PATHS[Critical User Paths]
    CRITICAL_PATHS --> ALERTS[Failure Alerts]
    ALERTS --> INCIDENT[Incident Response]

    subgraph "Monitoring Schedule"
        HOURLY[Hourly Checks]
        DAILY[Daily Full Suite]
        WEEKLY[Weekly Regression]
    end

    SYNTHETIC --> HOURLY
    SYNTHETIC --> DAILY
    SYNTHETIC --> WEEKLY

Execution Environments

Local Development

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Quick development cycle
cd tests/playwright-runner

# Run single test file
npx playwright test tests/auth/login.spec.ts

# Run with headed browser for debugging
npx playwright test tests/auth/login.spec.ts --headed

# Run with debug mode
npx playwright test tests/auth/login.spec.ts --debug

# Watch mode for continuous testing
npx playwright test --watch

Docker-based Execution

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Standard container execution
docker run --rm \
  -v $(pwd):/app \
  -w /app/tests/playwright-runner \
  webgrip/playwright-runner:latest \
  npx playwright test

# With environment variables
docker run --rm \
  -v $(pwd):/app \
  -w /app/tests/playwright-runner \
  -e BASE_URL=http://staging.example.com \
  -e TEST_USER_EMAIL=test@example.com \
  -e TEST_USER_PASSWORD=password123 \
  webgrip/playwright-runner:latest \
  npx playwright test --project=chromium

# With volume for reports
docker run --rm \
  -v $(pwd):/app \
  -v playwright-reports:/app/tests/playwright-runner/playwright-report \
  -w /app/tests/playwright-runner \
  webgrip/playwright-runner:latest \
  npx playwright test --reporter=html

GitHub Actions Integration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# .github/workflows/playwright-tests.yml
name: Playwright Tests
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    container: webgrip/playwright-runner:latest

    strategy:
      fail-fast: false
      matrix:
        browser: [chromium, firefox, webkit]
        shard: [1, 2, 3, 4]

    steps:
      - uses: actions/checkout@v4

      - name: Setup application
        run: |
          composer install --no-dev --optimize-autoloader
          php artisan config:cache
          php artisan route:cache

      - name: Setup database
        env:
          DB_CONNECTION: sqlite
          DB_DATABASE: ":memory:"
        run: |
          php artisan migrate --force
          php artisan db:seed --force

      - name: Start application server
        run: |
          php artisan serve --port=8000 &
          sleep 10
          curl http://localhost:8000/health || exit 1

      - name: Run Playwright tests
        env:
          BASE_URL: http://localhost:8000
        run: |
          cd tests/playwright-runner
          npm ci
          npx playwright test \
            --project=${{ matrix.browser }} \
            --shard=${{ matrix.shard }}/4 \
            --reporter=blob

      - name: Upload test results
        uses: actions/upload-artifact@v3
        if: always()
        with:
          name: playwright-results-${{ matrix.browser }}-${{ matrix.shard }}
          path: tests/playwright-runner/test-results/

  merge-reports:
    if: always()
    needs: test
    runs-on: ubuntu-latest
    container: webgrip/playwright-runner:latest
    steps:
      - uses: actions/checkout@v4

      - name: Download all test results
        uses: actions/download-artifact@v3
        with:
          pattern: playwright-results-*
          merge-multiple: true
          path: test-results/

      - name: Merge test reports
        run: |
          cd tests/playwright-runner
          npx playwright merge-reports --reporter=html test-results/

      - name: Upload merged report
        uses: actions/upload-artifact@v3
        with:
          name: playwright-report
          path: tests/playwright-runner/playwright-report/

Parallel Execution

Browser Parallelization

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// playwright.config.ts - Browser parallelization
export default defineConfig({
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});
1
2
3
4
5
6
7
8
# Run all browsers in parallel
npx playwright test

# Run specific browser
npx playwright test --project=chromium

# Run multiple specific browsers
npx playwright test --project=chromium --project=firefox

Test Sharding

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Split tests across multiple runners
npx playwright test --shard=1/4  # Run 1st quarter
npx playwright test --shard=2/4  # Run 2nd quarter
npx playwright test --shard=3/4  # Run 3rd quarter
npx playwright test --shard=4/4  # Run 4th quarter

# In CI with matrix
strategy:
  matrix:
    shard: [1, 2, 3, 4]
steps:
  - name: Run tests (Shard ${{ matrix.shard }})
    run: npx playwright test --shard=${{ matrix.shard }}/4

Worker Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// playwright.config.ts - Worker optimization
export default defineConfig({
  // Local development - use all cores
  workers: process.env.CI ? 2 : undefined,

  // Or specify exact number
  workers: 4,

  // Full parallelization
  fullyParallel: true,

  // Global timeout
  globalTimeout: 60 * 60 * 1000, // 1 hour

  // Per-test timeout
  timeout: 30 * 1000, // 30 seconds
});

Test Selection and Filtering

Tag-based Execution

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// tests/auth.spec.ts
test.describe('Authentication @smoke @auth', () => {
  test('should login successfully @critical', async ({ page }) => {
    // Test implementation
  });

  test('should handle invalid credentials @error-handling', async ({ page }) => {
    // Test implementation
  });
});

test.describe('User Management @integration @users', () => {
  test('should create new user @crud', async ({ page }) => {
    // Test implementation
  });
});
1
2
3
4
5
6
7
8
9
# Run tests by tags
npx playwright test --grep "@smoke"              # Smoke tests only
npx playwright test --grep "@critical"          # Critical tests only
npx playwright test --grep "@smoke|@critical"   # Smoke OR critical
npx playwright test --grep-invert "@slow"       # Everything except slow tests

# Run by test suite
npx playwright test --grep "@auth"              # All auth tests
npx playwright test tests/auth/                 # All files in auth directory

Environment-specific Selection

1
2
3
4
5
6
7
8
# Development environment - fast feedback
npx playwright test --grep "@smoke" --project=chromium

# Staging environment - comprehensive testing
npx playwright test --grep "@smoke|@integration"

# Production monitoring - critical paths only
npx playwright test --grep "@critical|@monitoring"

File-based Selection

1
2
3
4
5
6
7
8
9
# Run specific test files
npx playwright test tests/auth/login.spec.ts
npx playwright test tests/auth/

# Run multiple specific files
npx playwright test tests/auth/login.spec.ts tests/users/profile.spec.ts

# Run tests matching pattern
npx playwright test "**/*auth*.spec.ts"

Cross-Environment Testing

Environment Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// config/environments.ts
export const environments = {
  development: {
    baseURL: 'http://localhost:8000',
    timeout: 10000,
    retries: 0,
  },
  staging: {
    baseURL: 'https://staging.webgrip.nl',
    timeout: 30000,
    retries: 2,
  },
  production: {
    baseURL: 'https://app.webgrip.nl',
    timeout: 15000,
    retries: 1,
  },
};

// playwright.config.ts
const env = process.env.TEST_ENV || 'development';
const config = environments[env];

export default defineConfig({
  use: {
    baseURL: config.baseURL,
    actionTimeout: config.timeout,
  },
  retries: config.retries,
});

Environment-specific Execution

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Test against different environments
TEST_ENV=development npx playwright test
TEST_ENV=staging npx playwright test --grep "@smoke"
TEST_ENV=production npx playwright test --grep "@monitoring"

# With Docker
docker run --rm \
  -v $(pwd):/app \
  -w /app/tests/playwright-runner \
  -e TEST_ENV=staging \
  -e BASE_URL=https://staging.webgrip.nl \
  webgrip/playwright-runner:latest \
  npx playwright test --grep "@smoke"

Cross-browser Testing Matrix

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Complete cross-browser testing
strategy:
  matrix:
    browser: [chromium, firefox, webkit]
    environment: [staging, production]

steps:
  - name: Test ${{ matrix.browser }} on ${{ matrix.environment }}
    env:
      TEST_ENV: ${{ matrix.environment }}
    run: npx playwright test --project=${{ matrix.browser }}

Performance Optimization

Test Execution Performance

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// playwright.config.ts - Performance optimizations
export default defineConfig({
  // Faster test execution
  fullyParallel: true,
  workers: process.env.CI ? 4 : undefined,

  // Reasonable timeouts
  timeout: 30000,
  actionTimeout: 10000,
  navigationTimeout: 15000,

  // Optimize for CI
  use: {
    // Disable animations for faster execution
    reducedMotion: 'reduce',

    // Optimize screenshots
    screenshot: 'only-on-failure',

    // Minimal trace collection
    trace: 'retain-on-failure',

    // Disable video in CI
    video: process.env.CI ? 'off' : 'retain-on-failure',
  },

  // Global setup for shared state
  globalSetup: require.resolve('./global-setup'),
  globalTeardown: require.resolve('./global-teardown'),
});

Resource Management

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// global-setup.ts - Shared setup
import { chromium, FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
  // Setup shared database state
  await setupTestDatabase();

  // Warm up application
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto(config.projects[0].use.baseURL);
  await browser.close();

  // Cache authentication state
  await setupAuthenticationState();
}

export default globalSetup;

Caching Strategies

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// fixtures/auth-cache.ts
import { test as base } from '@playwright/test';

const STORAGE_STATE = 'auth-state.json';

export const test = base.extend({
  // Reuse authentication state
  storageState: STORAGE_STATE,

  authenticatedPage: async ({ page }, use) => {
    // Use cached authentication if available
    await use(page);
  },
});

Test Result Management

Report Generation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// playwright.config.ts - Multiple reporters
export default defineConfig({
  reporter: [
    // HTML report for local development
    ['html', { 
      outputFolder: 'playwright-report',
      open: process.env.CI ? 'never' : 'on-failure'
    }],

    // JSON for CI integration
    ['json', { outputFile: 'test-results.json' }],

    // JUnit for test management systems
    ['junit', { outputFile: 'test-results.xml' }],

    // Line reporter for CI logs
    ['line'],

    // Custom reporter for notifications
    ['./reporters/slack-reporter.js'],
  ],
});

Artifact Management

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Generate and collect artifacts
npx playwright test \
  --reporter=html \
  --output-dir=test-results \
  --screenshot=on \
  --video=on \
  --trace=on

# Archive results
tar -czf "test-results-$(date +%Y%m%d-%H%M%S).tar.gz" \
  test-results/ \
  playwright-report/ \
  test-results.json

CI/CD Integration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Complete artifact handling
- name: Run Playwright tests
  run: npx playwright test --reporter=blob

- name: Generate HTML report
  if: always()
  run: npx playwright merge-reports --reporter=html blob-report/

- name: Upload test report
  uses: actions/upload-artifact@v3
  if: always()
  with:
    name: playwright-report-${{ github.run_id }}
    path: |
      playwright-report/
      test-results/
    retention-days: 30

- name: Upload to test management
  if: always()
  run: |
    curl -X POST \
      -H "Content-Type: application/json" \
      -d @test-results.json \
      https://test-management.webgrip.nl/api/results

Monitoring and Alerting

Synthetic Monitoring

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// tests/monitoring/critical-paths.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Critical Path Monitoring @monitoring @critical', () => {
  test('user login flow', async ({ page }) => {
    const startTime = Date.now();

    await page.goto('/login');
    await page.fill('[name="email"]', process.env.MONITOR_USER_EMAIL!);
    await page.fill('[name="password"]', process.env.MONITOR_USER_PASSWORD!);
    await page.click('button[type="submit"]');

    await expect(page).toHaveURL('/dashboard');

    const loadTime = Date.now() - startTime;
    expect(loadTime).toBeLessThan(5000); // 5 second SLA
  });

  test('checkout process', async ({ page }) => {
    // Simulate critical business process
    await page.goto('/products/1');
    await page.click('button:has-text("Add to Cart")');
    await page.goto('/checkout');

    // Verify checkout loads within SLA
    await expect(page.locator('h1')).toContainText('Checkout');
  });
});

Alert Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash
# monitoring/run-synthetic-tests.sh

# Run critical path tests
npx playwright test --grep "@monitoring" --reporter=json > monitoring-results.json

# Check for failures
FAILURES=$(jq '.stats.failed' monitoring-results.json)

if [ "$FAILURES" -gt 0 ]; then
  # Send alert to Slack
  curl -X POST -H 'Content-type: application/json' \
    --data '{"text":"🚨 Synthetic monitoring tests failed: '"$FAILURES"' failures detected"}' \
    $SLACK_WEBHOOK_URL

  # Page on-call team
  curl -X POST \
    -H "Authorization: Token $PAGERDUTY_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "incident": {
        "type": "incident",
        "title": "Synthetic Test Failures",
        "service": {"id": "'"$PAGERDUTY_SERVICE_ID"'", "type": "service_reference"}
      }
    }' \
    https://api.pagerduty.com/incidents
fi

Performance Monitoring

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// tests/performance/load-times.spec.ts
test('performance metrics', async ({ page }) => {
  await page.goto('/');

  const metrics = await page.evaluate(() => {
    const perfData = performance.getEntriesByType('navigation')[0];
    return {
      domContentLoaded: perfData.domContentLoadedEventEnd - perfData.fetchStart,
      loadComplete: perfData.loadEventEnd - perfData.fetchStart,
      firstPaint: performance.getEntriesByType('paint')[0]?.startTime || 0,
    };
  });

  // Assert performance SLAs
  expect(metrics.domContentLoaded).toBeLessThan(2000);
  expect(metrics.loadComplete).toBeLessThan(5000);
  expect(metrics.firstPaint).toBeLessThan(1500);

  // Log metrics for monitoring
  console.log('Performance Metrics:', JSON.stringify(metrics));
});

Troubleshooting Test Execution

Common Execution Issues

Tests timing out

1
2
3
4
5
6
7
// Increase timeouts for specific tests
test('slow operation', async ({ page }) => {
  test.setTimeout(60000); // 60 seconds for this test

  await page.goto('/slow-page');
  await page.waitForSelector('.slow-component', { timeout: 30000 });
});

Flaky tests

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Add proper waits and retries
test('potentially flaky test', async ({ page }) => {
  // Retry this test up to 3 times
  test.fixme(({ browserName }) => browserName === 'webkit', 'Flaky on WebKit');

  await page.goto('/');

  // Wait for network to be idle
  await page.waitForLoadState('networkidle');

  // Use auto-waiting assertions
  await expect(page.locator('.dynamic-content')).toBeVisible();
});

Resource exhaustion

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Reduce parallel workers
npx playwright test --workers=1

# Run tests sequentially
npx playwright test --workers=1 --fullyParallel=false

# Increase system resources
docker run --rm \
  --memory=4g \
  --cpus=2 \
  -v $(pwd):/app \
  webgrip/playwright-runner:latest \
  npx playwright test

Debug Execution

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Debug with verbose output
DEBUG=pw:* npx playwright test

# Debug specific test
npx playwright test --debug tests/auth/login.spec.ts

# Debug in headed mode
npx playwright test --headed --slowMo=1000

# Generate trace for debugging
npx playwright test --trace=on
npx playwright show-trace test-results/example-test/trace.zip

Assumption: Test execution primarily occurs in containerized environments with reliable network connectivity. High-latency or unreliable network conditions may require additional timeout and retry configuration. Validation needed: Confirm network and infrastructure requirements for test execution environments.

Maintainer: WebGrip Ops Team
Configuration: tests/playwright-runner/playwright.config.ts
Container: webgrip/playwright-runner