# 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 ```typescript // 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) generateReport() } ``` πŸ‘‰ **Teaching Point:** `measureUserAction` wraps a user action and times it, giving you reproducible benchmarks. --- ## Quick Start: A Simple Example ```typescript 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 ```typescript 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: ```json { "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**: ```json { "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):** ```typescript const start = Date.now(); await page.click('#button'); console.log(Date.now() - start); ``` **After (Structured Monitoring):** ```typescript 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.