// tests/performance-engineering.advanced.spec.ts
import { test, expect } from '@playwright/test';
import * as lighthouse from 'lighthouse';
import * as fs from 'fs';
test('production performance analysis', async ({ page }, testInfo) => {
const performanceBudgets = {
fcp: 1500, // First Contentful Paint
lcp: 2500, // Largest Contentful Paint
cls: 0.1, // Cumulative Layout Shift
tti: 3500, // Time to Interactive
loadTime: 3000,
lighthouse: 80
};
// Page load with metric collection
const navigationStart = Date.now();
await page.goto('https://example.com/products', { waitUntil: 'networkidle' });
// Client-side metrics
const clientMetrics = await page.evaluate(() => {
const perf = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
const resources = performance.getEntriesByType('resource') as PerformanceResourceTiming[];
const slowResources = resources
.filter(r => r.duration > 1000)
.sort((a, b) => b.duration - a.duration)
.slice(0, 5);
return {
fcp: performance.getEntriesByName('first-contentful-paint')[0]?.startTime || 0,
lcp: performance.getEntriesByType('largest-contentful-paint').pop()?.startTime || 0,
cls: 0, // Would aggregate from PerformanceObserver
tti: perf.loadEventEnd - perf.fetchStart,
slowResources: slowResources.map(r => ({
name: r.name,
size: r.transferSize || 0,
duration: r.duration
}))
};
});
// Lighthouse audit
const lhReport = await lighthouse(page.url(), {
output: 'json',
port: 9222,
throttling: { cpuSlowdownMultiplier: 1 }
});
const scores = {
performance: lhReport.lhr.categories.performance.score * 100,
accessibility: lhReport.lhr.categories.accessibility.score * 100,
bestPractices: lhReport.lhr.categories['best-practices'].score * 100,
seo: lhReport.lhr.categories.seo.score * 100
};
// Analysis and recommendations
const issues = [];
if (clientMetrics.fcp > performanceBudgets.fcp) {
issues.push(`FCP ${clientMetrics.fcp}ms exceeds budget ${performanceBudgets.fcp}ms`);
}
if (scores.performance < performanceBudgets.lighthouse) {
issues.push(`Lighthouse ${scores.performance} below ${performanceBudgets.lighthouse}`);
}
// Report
const report = {
timestamp: new Date().toISOString(),
url: page.url(),
metrics: clientMetrics,
scores,
issues,
recommendations: generateRecommendations(clientMetrics, lhReport)
};
fs.writeFileSync(
`performance-report-${Date.now()}.json`,
JSON.stringify(report, null, 2)
);
// Assertions
expect(clientMetrics.fcp).toBeLessThan(performanceBudgets.fcp);
expect(scores.performance).toBeGreaterThanOrEqual(performanceBudgets.lighthouse);
expect(issues.length).toBe(0);
});
function generateRecommendations(metrics: any, report: any): string[] {
const recommendations = [];
if (metrics.slowResources.length > 0) {
recommendations.push(`Optimize ${metrics.slowResources.length} slow resources`);
recommendations.push(` - ${metrics.slowResources[0].name} (${metrics.slowResources[0].duration.toFixed(0)}ms)`);
}
const opportunities = report.lhr.audits['performance-budget']?.details?.opportunities || [];
recommendations.push(...opportunities.slice(0, 3).map((o: any) => o.title));
return recommendations;
}