Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
8.7 kB · 246 lines
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247// Chrome DevTools Performance Testing with Puppeteer// Automated performance analysis of aesthetic.computer boot sequence
import puppeteer from 'puppeteer';import lighthouse from 'lighthouse';import { writeFileSync } from 'fs';import { join } from 'path';
const TEST_URL = process.env.TEST_URL || 'https://localhost:8888';const OUTPUT_DIR = './tests/performance/reports';
/** * Measure boot performance with Chrome Performance API */async function measureBootPerformance() { console.log('🚀 Starting Chrome DevTools Performance Test\n'); const browser = await puppeteer.launch({ headless: true, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--ignore-certificate-errors', // For local SSL '--disable-web-security', // For local CORS testing ] });
try { const page = await browser.newPage(); // Enable Performance monitoring await page.coverage.startJSCoverage(); const client = await page.target().createCDPSession(); await client.send('Performance.enable'); console.log('📊 Loading page and capturing metrics...\n'); const startTime = Date.now(); // Navigate and wait for network idle await page.goto(TEST_URL, { waitUntil: 'networkidle2', timeout: 30000 }); // Wait for boot complete or timeout try { await page.waitForFunction( () => window.acBOOT_START_TIME && document.readyState === 'complete', { timeout: 10000 } ); } catch (e) { console.warn('⚠️ Boot completion markers not found, using basic timing'); } const loadTime = Date.now() - startTime; // Collect Performance metrics const performanceMetrics = await page.evaluate(() => { const perf = performance.getEntriesByType('navigation')[0]; const paint = performance.getEntriesByType('paint'); return { // Navigation timing dns: perf.domainLookupEnd - perf.domainLookupStart, tcp: perf.connectEnd - perf.connectStart, ttfb: perf.responseStart - perf.requestStart, download: perf.responseEnd - perf.responseStart, domInteractive: perf.domInteractive, domComplete: perf.domComplete, loadComplete: perf.loadEventEnd - perf.loadEventStart, // Paint timing firstPaint: paint.find(p => p.name === 'first-paint')?.startTime || 0, firstContentfulPaint: paint.find(p => p.name === 'first-contentful-paint')?.startTime || 0, // Custom timing bootStartTime: window.acBOOT_START_TIME, currentTime: performance.now(), }; }); // Get resource timing const resources = await page.evaluate(() => { return performance.getEntriesByType('resource').map(r => ({ name: r.name.split('/').pop() || r.name, duration: r.duration, size: r.transferSize, type: r.initiatorType, })).sort((a, b) => b.duration - a.duration).slice(0, 10); // Top 10 slowest }); // Get JS coverage const jsCoverage = await page.coverage.stopJSCoverage(); const totalBytes = jsCoverage.reduce((sum, entry) => sum + entry.text.length, 0); const usedBytes = jsCoverage.reduce((sum, entry) => { const used = entry.ranges.reduce((s, range) => s + (range.end - range.start), 0); return sum + used; }, 0); const coveragePercent = ((usedBytes / totalBytes) * 100).toFixed(1); // Print results console.log('⏱️ Performance Metrics:'); console.log(` Total Load Time: ${loadTime}ms`); console.log(` DNS Lookup: ${performanceMetrics.dns.toFixed(2)}ms`); console.log(` TCP Connect: ${performanceMetrics.tcp.toFixed(2)}ms`); console.log(` Time to First Byte: ${performanceMetrics.ttfb.toFixed(2)}ms`); console.log(` Download: ${performanceMetrics.download.toFixed(2)}ms`); console.log(` DOM Interactive: ${performanceMetrics.domInteractive.toFixed(2)}ms`); console.log(` DOM Complete: ${performanceMetrics.domComplete.toFixed(2)}ms`); console.log(` First Paint: ${performanceMetrics.firstPaint.toFixed(2)}ms`); console.log(` First Contentful Paint: ${performanceMetrics.firstContentfulPaint.toFixed(2)}ms\n`); console.log('📦 Top 10 Slowest Resources:'); resources.forEach((r, i) => { const size = r.size ? `(${(r.size / 1024).toFixed(1)}KB)` : ''; console.log(` ${i + 1}. ${r.name} - ${r.duration.toFixed(2)}ms ${size}`); }); console.log(); console.log('📊 JavaScript Coverage:'); console.log(` Total JS: ${(totalBytes / 1024).toFixed(1)}KB`); console.log(` Used JS: ${(usedBytes / 1024).toFixed(1)}KB`); console.log(` Coverage: ${coveragePercent}%`); console.log(` Unused: ${((totalBytes - usedBytes) / 1024).toFixed(1)}KB\n`); // Performance thresholds const thresholds = { fcp: 1500, // First Contentful Paint tti: 3000, // Time to Interactive (approximated by domComplete) load: 5000, // Total load time }; console.log('✅ Performance Checks:'); console.log(` ${performanceMetrics.firstContentfulPaint < thresholds.fcp ? '✅' : '❌'} FCP under ${thresholds.fcp}ms: ${performanceMetrics.firstContentfulPaint.toFixed(0)}ms`); console.log(` ${performanceMetrics.domComplete < thresholds.tti ? '✅' : '❌'} TTI under ${thresholds.tti}ms: ${performanceMetrics.domComplete.toFixed(0)}ms`); console.log(` ${loadTime < thresholds.load ? '✅' : '❌'} Load under ${thresholds.load}ms: ${loadTime}ms\n`); return { loadTime, metrics: performanceMetrics, resources, coverage: { totalBytes, usedBytes, percent: coveragePercent }, }; } finally { await browser.close(); }}
/** * Run Lighthouse audit */async function runLighthouseAudit() { console.log('💡 Running Lighthouse Audit...\n'); const browser = await puppeteer.launch({ headless: true, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--ignore-certificate-errors', ] });
try { const { lhr } = await lighthouse(TEST_URL, { port: new URL(browser.wsEndpoint()).port, output: 'json', onlyCategories: ['performance'], formFactor: 'desktop', screenEmulation: { disabled: true }, });
const scores = { performance: lhr.categories.performance.score * 100, fcp: lhr.audits['first-contentful-paint'].numericValue, lcp: lhr.audits['largest-contentful-paint'].numericValue, tbt: lhr.audits['total-blocking-time'].numericValue, cls: lhr.audits['cumulative-layout-shift'].numericValue, speedIndex: lhr.audits['speed-index'].numericValue, };
console.log('💡 Lighthouse Performance Score:'); console.log(` Overall: ${scores.performance.toFixed(0)}/100`); console.log(` First Contentful Paint: ${scores.fcp.toFixed(0)}ms`); console.log(` Largest Contentful Paint: ${scores.lcp.toFixed(0)}ms`); console.log(` Total Blocking Time: ${scores.tbt.toFixed(0)}ms`); console.log(` Cumulative Layout Shift: ${scores.cls.toFixed(3)}`); console.log(` Speed Index: ${scores.speedIndex.toFixed(0)}ms\n`);
// Save full report const reportPath = join(OUTPUT_DIR, `lighthouse-${Date.now()}.json`); try { writeFileSync(reportPath, JSON.stringify(lhr, null, 2)); console.log(`📄 Full report saved to: ${reportPath}\n`); } catch (e) { console.log('⚠️ Could not save report (directory may not exist)\n'); }
return scores;
} finally { await browser.close(); }}
/** * Main test runner */async function runTests() { try { console.log('🎯 Chrome DevTools Performance Testing\n'); console.log(`Testing URL: ${TEST_URL}\n`); console.log('─'.repeat(60) + '\n');
// Run performance measurement const perfResults = await measureBootPerformance(); console.log('─'.repeat(60) + '\n'); // Run Lighthouse audit (optional, can be slow) if (process.env.RUN_LIGHTHOUSE !== 'false') { const lighthouseResults = await runLighthouseAudit(); console.log('─'.repeat(60) + '\n'); }
console.log('✅ All tests completed!\n'); // Exit with error if performance is poor if (perfResults.loadTime > 5000) { console.error('❌ Performance threshold exceeded!'); process.exit(1); }
} catch (error) { console.error('❌ Test failed:', error.message); process.exit(1); }}
// Run tests if called directlyif (import.meta.url === `file://${process.argv[1]}`) { runTests();}
export { measureBootPerformance, runLighthouseAudit };