Files
SkillCompiler/data/skills-bench/tasks/react-performance-debugging/environment/skills/browser-testing/measure-cls.ts
T
2026-09-04 14:58:42 +08:00

74 lines
1.9 KiBLFS
TypeScript

import { chromium } from 'playwright';
interface CLSResult {
url: string;
cls: number;
rating: 'good' | 'needs-improvement' | 'poor';
metrics: Record<string, number>;
}
async function measureCLS(url: string, scroll: boolean = false): Promise<CLSResult> {
const browser = await chromium.launch();
const page = await browser.newPage();
const client = await page.context().newCDPSession(page);
// Enable Performance domain
await client.send('Performance.enable');
// Navigate and wait for load
await page.goto(url, { waitUntil: 'networkidle' });
// Optional: scroll to trigger lazy-loaded content shifts
if (scroll) {
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await page.waitForTimeout(1000);
await page.evaluate(() => window.scrollTo(0, 0));
await page.waitForTimeout(500);
}
// Wait for any remaining shifts
await page.waitForTimeout(2000);
// Get all metrics including CumulativeLayoutShift
const perfMetrics = await client.send('Performance.getMetrics');
await browser.close();
// Convert metrics array to object
const metrics: Record<string, number> = {};
for (const m of perfMetrics.metrics) {
metrics[m.name] = m.value;
}
// CLS is directly available from CDP
const cls = metrics['CumulativeLayoutShift'] || 0;
// Determine rating (Google's thresholds)
let rating: 'good' | 'needs-improvement' | 'poor';
if (cls < 0.1) {
rating = 'good';
} else if (cls < 0.25) {
rating = 'needs-improvement';
} else {
rating = 'poor';
}
return {
url,
cls: Math.round(cls * 1000) / 1000,
rating,
metrics
};
}
// Main
const url = process.argv[2] || 'http://localhost:3000';
const scroll = process.argv.includes('--scroll');
measureCLS(url, scroll)
.then(result => console.log(JSON.stringify(result, null, 2)))
.catch(err => {
console.error('CLS measurement failed:', err.message);
process.exit(1);
});