Agent skill

synthetic-monitoring

Set up synthetic monitoring with Lighthouse CI and Playwright. Use when implementing automated performance testing, CI/CD performance gates, or proactive monitoring.

Stars 0
Forks 0

Install this agent skill to your Project

npx add-skill https://github.com/nexus-labs-automation/web-observability/tree/main/skills/synthetic-monitoring

SKILL.md

Synthetic Monitoring

Automated testing for consistent, proactive performance monitoring.

RUM vs Synthetic

Aspect RUM (Real User) Synthetic (Lab)
Data source Actual users Controlled tests
Variability High Low
Coverage What users do What you test
Issues Finds unknown Validates known
Timing Reactive Proactive

Use both: Synthetic catches regressions before users; RUM shows real-world impact.

Lighthouse CI

Setup

bash
npm install -D @lhci/cli
json
// lighthouserc.json
{
  "ci": {
    "collect": {
      "url": [
        "http://localhost:3000/",
        "http://localhost:3000/products",
        "http://localhost:3000/checkout"
      ],
      "numberOfRuns": 3
    },
    "assert": {
      "preset": "lighthouse:recommended",
      "assertions": {
        "categories:performance": ["error", { "minScore": 0.9 }],
        "categories:accessibility": ["error", { "minScore": 0.9 }],
        "first-contentful-paint": ["error", { "maxNumericValue": 2000 }],
        "largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
        "interactive": ["error", { "maxNumericValue": 3500 }],
        "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }]
      }
    },
    "upload": {
      "target": "temporary-public-storage"
    }
  }
}

GitHub Actions

yaml
# .github/workflows/lighthouse.yml
name: Lighthouse CI

on: [push]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - name: Install & Build
        run: |
          npm ci
          npm run build

      - name: Start server
        run: npm run start &

      - name: Run Lighthouse
        run: |
          npm install -g @lhci/cli
          lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

Playwright Performance Testing

Setup

bash
npm install -D @playwright/test
typescript
// tests/performance.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Performance Tests', () => {
  test('homepage loads within budget', async ({ page }) => {
    // Start tracing
    await page.context().tracing.start({ screenshots: true, snapshots: true });

    const startTime = Date.now();
    await page.goto('/');
    await page.waitForLoadState('networkidle');
    const loadTime = Date.now() - startTime;

    // Assert load time
    expect(loadTime).toBeLessThan(3000);

    // Get Core Web Vitals
    const vitals = await page.evaluate(() => {
      return new Promise((resolve) => {
        new PerformanceObserver((list) => {
          const entries = list.getEntries();
          const lcp = entries.find((e) => e.entryType === 'largest-contentful-paint');
          resolve({
            lcp: lcp?.startTime,
            cls: entries
              .filter((e) => e.entryType === 'layout-shift')
              .reduce((sum: number, e: any) => sum + e.value, 0),
          });
        }).observe({ type: 'largest-contentful-paint', buffered: true });

        // Fallback timeout
        setTimeout(() => resolve({ lcp: null, cls: null }), 5000);
      });
    });

    console.log('Vitals:', vitals);

    // Stop tracing
    await page.context().tracing.stop({ path: 'trace.zip' });
  });

  test('checkout flow performance', async ({ page }) => {
    const metrics: Record<string, number> = {};

    // Navigate through checkout
    await page.goto('/products/1');
    metrics.productPageLoad = await measurePageLoad(page);

    await page.click('[data-testid="add-to-cart"]');
    await page.goto('/cart');
    metrics.cartPageLoad = await measurePageLoad(page);

    await page.click('[data-testid="checkout"]');
    metrics.checkoutPageLoad = await measurePageLoad(page);

    // Assert all pages load within budget
    expect(metrics.productPageLoad).toBeLessThan(2000);
    expect(metrics.cartPageLoad).toBeLessThan(1500);
    expect(metrics.checkoutPageLoad).toBeLessThan(2000);
  });
});

async function measurePageLoad(page: any): Promise<number> {
  const timing = await page.evaluate(() => {
    const nav = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
    return nav.loadEventEnd - nav.fetchStart;
  });
  return timing;
}

Performance Trace Collection

typescript
// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    trace: 'retain-on-failure',
    video: 'retain-on-failure',
  },
  projects: [
    {
      name: 'Desktop Chrome',
      use: {
        browserName: 'chromium',
        viewport: { width: 1280, height: 720 },
      },
    },
    {
      name: 'Mobile Chrome',
      use: {
        browserName: 'chromium',
        ...devices['Pixel 5'],
      },
    },
    {
      name: 'Slow 3G',
      use: {
        browserName: 'chromium',
        // Simulate slow network
        launchOptions: {
          args: ['--force-effective-connection-type=3g'],
        },
      },
    },
  ],
});

Scheduled Monitoring

Checkly (or similar)

typescript
// __checks__/homepage.check.ts
import { check, group } from 'checkly';
import { Playwright } from 'checkly/constructs';

const homepage = new Playwright('homepage-check', {
  name: 'Homepage Performance',
  frequency: 5, // Every 5 minutes
  locations: ['us-east-1', 'eu-west-1'],
  runtimeId: '2024.02',
  script: `
    const { expect } = require('@playwright/test');

    async function run({ page }) {
      const response = await page.goto('https://yoursite.com');

      expect(response.status()).toBe(200);

      const timing = await page.evaluate(() => {
        const nav = performance.getEntriesByType('navigation')[0];
        return nav.loadEventEnd - nav.fetchStart;
      });

      expect(timing).toBeLessThan(3000);
    }

    module.exports = { run };
  `,
  alertChannels: ['email-oncall'],
});

Metrics to Track

Metric Threshold Priority
LCP <2.5s P0
TTI <3.5s P0
Total Blocking Time <200ms P0
CLS <0.1 P0
Speed Index <3.4s P1
Time to First Byte <600ms P1

CI/CD Integration Pattern

yaml
# Performance gate in CI
performance:
  runs-on: ubuntu-latest
  steps:
    - name: Run Lighthouse
      run: lhci autorun

    - name: Run Playwright
      run: npx playwright test tests/performance/

    - name: Upload results
      run: |
        # Send to your observability platform
        curl -X POST \
          -H "Authorization: Bearer $TOKEN" \
          -d @lighthouse-results.json \
          https://api.yourplatform.com/synthetic-results

Anti-Patterns

  • Only running in CI (miss production issues)
  • Testing only happy path
  • Ignoring mobile/slow network
  • No alerting on failures
  • Not tracking trends over time
  • Missing real user comparison

Related Skills

  • See skills/core-web-vitals for RUM comparison
  • See skills/bundle-performance for what to optimize
  • See skills/route-transition-tracking for SPA performance

References

  • references/performance.md - Performance budgets
  • references/platforms/*.md - Vendor integrations

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results