@roopamgarg/web-vitals-guardian
v0.1.5
Published
Run scripted interactions, compute INP, and fail builds on budget breach.
Readme
Web Vitals Guardian
A Node.js library that automatically runs user interaction scenarios and measures Web Vitals performance metrics using Playwright.
Features
- 🔍 Automatic Scenario Discovery: Finds and runs all
*.scenario.jsonfiles in a directory - 🎭 Playwright Integration: Uses Playwright for reliable browser automation
- 📊 Web Vitals Measurement: Measures FCP, LCP, CLS, INP, TTFB, and other performance metrics
- 🎯 Budget Enforcement: Fails builds when performance budgets are exceeded
- 📝 Detailed Reporting: Generates comprehensive performance reports
- 🏗️ Modular Architecture: Clean, maintainable code structure with separated concerns
Installation
npm install @roopamgarg/web-vitals-guardianQuick Start
Check out the examples in the
examples/folder for complete working examples:# Run a basic example node examples/example-usage.js # Run with modular imports node examples/example-modular-usage.js # Run with custom budgets node examples/custom-budgets.js # Run CI integration example node examples/ci-integration.jsCreate scenario files with the
.scenario.jsonextension (seeexamples/for templates):
{
"name": "Homepage Performance Test",
"description": "Test the homepage loading and basic interactions",
"url": "https://example.com",
"timeout": 30000,
"steps": [
{
"type": "wait",
"waitFor": "body",
"timeout": 10000
},
{
"type": "click",
"selector": "a[href='/about']",
"timeout": 5000
},
{
"type": "wait",
"timeout": 2000
},
{
"type": "scroll"
}
],
"webVitals": {
"budgets": {
"FCP": 1800,
"LCP": 2500,
"CLS": 0.1,
"INP": 200,
"TTFB": 600
}
}
}- Run the guardian:
import { runWebVitalsGuardian } from '@your-scope/inp-guardian';
const config = {
scenariosPath: './scenarios',
headless: true,
budgets: {
FCP: 1800, // First Contentful Paint
LCP: 2500, // Largest Contentful Paint
CLS: 0.1, // Cumulative Layout Shift
INP: 200, // Interaction to Next Paint
TTFB: 600 // Time to First Byte
}
};
const result = await runWebVitalsGuardian(config);
console.log(`Passed: ${result.summary.passed}/${result.summary.totalScenarios}`);
if (result.summary.budgetViolations.length > 0) {
console.log('Budget violations:', result.summary.budgetViolations);
}Scenario File Format
Required Fields
name: Unique identifier for the scenariourl: Initial URL to navigate tosteps: Array of interaction steps
Optional Fields
description: Human-readable descriptiontimeout: Global timeout for the scenario (default: 30000ms)webVitals.budgets: Performance budgets specific to this scenario
Supported Step Types
navigate
Navigate to a different URL:
{
"type": "navigate",
"url": "https://example.com/page",
"timeout": 10000
}click
Click on an element:
{
"type": "click",
"selector": "button[data-testid='submit']",
"timeout": 5000
}type
Type text into an input field:
{
"type": "type",
"selector": "input[name='email']",
"text": "[email protected]",
"timeout": 5000
}wait
Wait for an element or timeout:
{
"type": "wait",
"waitFor": ".loading-complete",
"timeout": 10000
}Or wait for a specific duration:
{
"type": "wait",
"timeout": 2000
}scroll
Scroll to the bottom of the page:
{
"type": "scroll"
}hover
Hover over an element:
{
"type": "hover",
"selector": ".dropdown-trigger",
"timeout": 5000
}Configuration Options
GuardianConfig
scenariosPath(required): Directory path to scan for scenario filesoutputPath(optional): Directory to save reportsheadless(optional): Run browser in headless mode (default: true)timeout(optional): Global timeout for scenarios (default: 30000ms)budgets(optional): Global performance budgetswebVitals(optional): Web Vitals measurement configurationusePerformanceObserver(optional): Force using PerformanceObserver (CSP-safe, default: true)fallbackToPackage(optional): Allow fallback to web-vitals package (default: false)
CSP-Safe Configuration
For environments with Content Security Policy (CSP) that block external scripts:
const config = {
scenariosPath: './scenarios',
headless: true,
webVitals: {
usePerformanceObserver: true, // Use PerformanceObserver (CSP-safe)
fallbackToPackage: false // Disable external web-vitals package
}
};This configuration ensures no external scripts are loaded, making it compatible with strict CSP policies.
Web Vitals Measured
- FCP (First Contentful Paint): Time until first content is painted
- LCP (Largest Contentful Paint): Time until largest content is painted
- CLS (Cumulative Layout Shift): Visual stability score
- INP (Interaction to Next Paint): Responsiveness to user interactions
- TTFB (Time to First Byte): Server response time
Performance Budgets
Set performance budgets to fail builds when metrics exceed thresholds:
const budgets = {
FCP: 1800, // 1.8 seconds
LCP: 2500, // 2.5 seconds
CLS: 0.1, // 0.1 layout shift score
INP: 200, // 200ms interaction delay
TTFB: 600 // 600ms server response
};Examples
The examples/ folder contains comprehensive examples:
example-usage.js- Basic usage exampleexample-modular-usage.js- Advanced modular importsexample.scenario.json- Basic scenario templateecommerce-checkout.scenario.json- Complex e-commerce flowblog-navigation.scenario.json- Blog interaction examplecustom-budgets.js- Different budget configurationsci-integration.js- CI/CD pipeline integrationcsp-safe-example.js- CSP-safe configuration for Content Security Policy environmentsperformanceObserver.ts- Alternative measurement approach
Run any example:
node examples/example-usage.jsIntegration with CI/CD
Add to your build pipeline to prevent performance regressions:
{
"scripts": {
"test:performance": "node examples/ci-integration.js",
"build": "npm run test:performance && npm run build:app"
}
}See examples/ci-integration.js for a complete CI/CD integration example with GitHub Actions.
Architecture
The library is organized into a clean modular structure:
src/
├── types/ # TypeScript interfaces and type definitions
│ └── index.ts
├── utils/ # Utility functions
│ └── fileUtils.ts # File discovery and loading
├── measurements/ # Web Vitals measurement logic
│ └── webVitals.ts # Performance metrics collection
├── scenarios/ # Scenario execution
│ └── runner.ts # Step execution and scenario running
├── guardian/ # Main guardian logic
│ ├── index.ts # Main guardian function
│ └── budgetChecker.ts # Budget validation
└── index.ts # Main exportsModule Responsibilities
types/: All TypeScript interfaces and type definitionsutils/: File system operations and scenario file discoverymeasurements/: Web Vitals and performance metrics collectionscenarios/: Scenario step execution and browser automationguardian/: Main orchestration logic and budget checkingindex.ts: Public API exports
API Reference
runWebVitalsGuardian(config: GuardianConfig)
Main function that runs all scenarios and returns performance reports.
Returns:
{
reports: WebVitalsReport[];
summary: {
totalScenarios: number;
passed: number;
failed: number;
budgetViolations: string[];
};
}findScenarioFiles(directory: string): string[]
Finds all *.scenario.json files in a directory recursively.
loadScenarioFile(filePath: string): ScenarioFile
Loads and validates a scenario file.
measureWebVitals(page: Page): Promise<WebVitalsReport['metrics']>
Measures Web Vitals metrics on a page.
executeScenarioStep(page: Page, step: ScenarioStep): Promise<void>
Executes a single scenario step.
runScenario(browser: Browser, scenario: ScenarioFile): Promise<WebVitalsReport>
Runs a complete scenario and measures Web Vitals.
License
ISC
