You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

6.4 KiB

Performance Monitoring in Playwright Tests

Performance monitoring is more than just numbers — it’s about understanding how your users experience your app during automated test runs. This guide will teach you not just how to set it up, but also why each step matters.


Why Performance Monitoring Matters

Think of Playwright tests as quality control for your app’s speed and responsiveness. By adding performance monitoring, you can:

  • 🚨 Catch regressions early before users feel them
  • 🎯 Keep user experience consistent across releases
  • 🔎 Spot bottlenecks in login, navigation, or heavy data flows
  • 📊 Make informed decisions with hard data, not guesses
  • 🏆 Maintain performance standards as features grow

Key Insight: Without metrics, “fast” is just a feeling. With metrics, it’s a fact.


How It Works: The Architecture

The monitoring system has four pillars:

  1. PerformanceCollector Class – Collects raw metrics
  2. Performance Utilities – Easy-to-use helper functions
  3. Test Integration – Hooks directly into Playwright tests
  4. Report Generation – Creates JSON reports you can analyze later

Here’s a mental model:

Playwright Test
      |
      v
PerformanceCollector (collects data)
      |
      v
Report Generation → JSON / HTML / CI attachments

Core Collector

// performanceUtils.ts
export class PerformanceCollector {
  private page: Page;
  public metrics: any;
  public navigationMetrics: any[];
  private cdpSession: any;

  // Methods for collecting various metrics
  async collectNavigationMetrics(label: string)
  async collectWebVitals()
  async measureUserAction(actionName: string, actionFn: () => Promise<void>)
  generateReport()
}

👉 Teaching Point: measureUserAction wraps a user action and times it, giving you reproducible benchmarks.


Quick Start: A Simple Example

import { createPerformanceCollector, attachPerformanceData } from './performanceUtils';

test('My test with performance monitoring', async ({ page }, testInfo) => {
  const perfCollector = await createPerformanceCollector(page);

  // Measure user action
  await perfCollector.measureUserAction('click-button', async () => {
    await page.click('#my-button');
  });

  // Attach data to the test report
  await attachPerformanceData(testInfo, perfCollector);
});

After this test runs, you’ll find performance data directly in the Playwright report.


Advanced Example: A Complete User Flow

test('Complex user flow with performance tracking', async ({ page }, testInfo) => {
  const perfCollector = await createPerformanceCollector(page);

  await perfCollector.collectNavigationMetrics('initial-load');

  await perfCollector.measureUserAction('login', async () => {
    await page.fill('#username', 'user');
    await page.fill('#password', 'pass');
    await page.click('#login-button');
  });

  await perfCollector.measureUserAction('navigate-to-dashboard', async () => {
    await page.goto('/dashboard');
  });

  await attachPerformanceData(testInfo, perfCollector);
});

Pro Tip: Use descriptive labels like 'login' or 'navigate-to-dashboard' to make reports easy to scan.


What Metrics You’ll See

Navigation Metrics (Page Load)

  • domContentLoaded → When DOM is ready
  • loadComplete → When page is fully loaded
  • firstPaint → When users first see something
  • serverResponse → How quickly the backend responds

User Action Metrics

  • duration → How long the action took
  • metrics → Detailed performance snapshots

Memory Usage

  • used, total, limit → Helps catch leaks and spikes

Web Vitals

  • LCP → Largest Contentful Paint
  • FID → First Input Delay
  • CLS → Layout stability

Reading the Data: A Beginner’s Lens

Here’s what a healthy test run might look like:

{
  "label": "home-page-load",
  "metrics": {
    "domContentLoaded": 294,
    "loadComplete": 295,
    "serverResponse": 27.6,
    "resourceCount": 96
  }
}

Interpretation:

  • DOM loaded fast (<500ms)
  • Server response is excellent (<100ms)
  • Resource count is reasonable for a SPA

Now, here’s a problematic run:

{
  "label": "slow-page-load",
  "metrics": {
    "domContentLoaded": 2500,
    "loadComplete": 5000,
    "serverResponse": 800,
    "resourceCount": 200
  }
}

Interpretation:

  • DOM took 2.5s
  • Full load took 5s
  • Too many resources (200)

Lesson: Slow page loads often mean large bundles, too many requests, or server lag.


Performance Threshold Cheat Sheet

Metric Excellent Good Poor
domContentLoaded < 500ms < 1000ms > 2000ms
loadComplete < 1000ms < 2000ms > 3000ms
userAction duration < 100ms < 300ms > 500ms
memory usage < 50MB < 100MB > 150MB

👉 Use these thresholds to set alerts in your regression tests.


Common Patterns

  1. Initial Page Load

    • DOM in <500ms
    • Load in <1000ms
    • ⚠️ Watch out for large bundles
  2. User Interaction

    • Actions under 100ms
    • Few/no extra requests
    • ⚠️ Avoid bloated API calls
  3. Navigation

    • <200ms between pages
    • ⚠️ Inconsistency usually means missing cache headers

Best Practices

  • 📏 Consistency – Measure the same flows every run
  • 🧪 Realism – Test with production-like data
  • 🏗 Environment Control – Use stable test environments
  • 📉 Set Thresholds – Define what “slow” means for your team
  • 🔁 Continuous Monitoring – Run in CI/CD and watch trends

Remember: A fast app last release doesn’t guarantee it’s fast today.


Migration Tip

Before (Manual Timing):

const start = Date.now();
await page.click('#button');
console.log(Date.now() - start);

After (Structured Monitoring):

await perfCollector.measureUserAction('button-click', async () => {
  await page.click('#button');
});

Cleaner, more consistent, and automatically reported.


Key Takeaway

Performance monitoring in Playwright isn’t just about collecting data — it’s about making your tests teach you how users experience your app. The PerformanceCollector class plus good testing habits give you a clear, data-driven picture of app health.