#!/usr/bin/env node /** * Comprehensive Performance Testing Script * Integrates bundle analysis, performance monitoring, and Web Vitals tracking */ const { execSync } = require("child_process"); const fs = require("fs"); const path = require("path"); const TEST_RESULTS_DIR = path.join(__dirname, "..", ".next", "test-results"); class PerformanceTester { constructor() { this.results = { timestamp: new Date().toISOString(), bundleAnalysis: {}, performanceMonitoring: {}, webVitals: {}, lighthouse: {}, summary: { passed: 0, failed: 0, warnings: 0, total: 0, }, }; } /** * Run comprehensive performance testing */ async runTests() { console.log("๐Ÿงช Starting comprehensive performance testing..."); try { // Ensure test results directory exists if (!fs.existsSync(TEST_RESULTS_DIR)) { fs.mkdirSync(TEST_RESULTS_DIR, { recursive: true }); } // 1. Bundle Analysis console.log("๐Ÿ“Š Running bundle analysis..."); await this.runBundleAnalysis(); // 2. Performance Monitoring console.log("๐Ÿ“ˆ Running performance monitoring..."); await this.runPerformanceMonitoring(); // 3. Web Vitals Tracking console.log("๐Ÿ“Š Setting up Web Vitals tracking..."); await this.runWebVitalsTracking(); // 4. Lighthouse CI (if server is available) console.log("๐Ÿ” Running Lighthouse CI..."); await this.runLighthouseCI(); // 5. Generate comprehensive report this.generateComprehensiveReport(); console.log("โœ… Performance testing complete!"); console.log(`๐Ÿ“ Results saved to: ${TEST_RESULTS_DIR}`); // Return exit code based on results const hasFailures = this.results.summary.failed > 0; if (hasFailures) { console.log("โŒ Performance tests failed"); process.exit(1); } else { console.log("โœ… All performance tests passed"); process.exit(0); } } catch (error) { console.error("โŒ Performance testing failed:", error.message); process.exit(1); } } /** * Run bundle analysis */ async runBundleAnalysis() { try { execSync("npm run bundle:analyze", { stdio: "inherit" }); // Parse bundle analysis results const bundleReportPath = path.join( __dirname, "..", ".next", "analyze", "bundle-analysis.json", ); if (fs.existsSync(bundleReportPath)) { const bundleData = JSON.parse( fs.readFileSync(bundleReportPath, "utf8"), ); this.results.bundleAnalysis = bundleData; // Check for budget violations if ( bundleData.budgetViolations && bundleData.budgetViolations.length > 0 ) { this.results.summary.failed += bundleData.budgetViolations.length; console.log( `โš ๏ธ Found ${bundleData.budgetViolations.length} budget violations`, ); } else { this.results.summary.passed += 1; console.log("โœ… Bundle analysis passed"); } } this.results.summary.total += 1; } catch (error) { console.error("โŒ Bundle analysis failed:", error.message); this.results.summary.failed += 1; this.results.summary.total += 1; } } /** * Run performance monitoring */ async runPerformanceMonitoring() { try { execSync("npm run performance:monitor", { stdio: "inherit" }); // Parse performance monitoring results const perfReportPath = path.join( __dirname, "..", ".next", "monitoring", "performance-report.json", ); if (fs.existsSync(perfReportPath)) { const perfData = JSON.parse(fs.readFileSync(perfReportPath, "utf8")); this.results.performanceMonitoring = perfData; // Check for budget violations if (perfData.budgetViolations && perfData.budgetViolations.length > 0) { this.results.summary.failed += perfData.budgetViolations.length; console.log( `โš ๏ธ Found ${perfData.budgetViolations.length} performance violations`, ); } else { this.results.summary.passed += 1; console.log("โœ… Performance monitoring passed"); } } this.results.summary.total += 1; } catch (error) { console.error("โŒ Performance monitoring failed:", error.message); this.results.summary.failed += 1; this.results.summary.total += 1; } } /** * Run Web Vitals tracking */ async runWebVitalsTracking() { try { execSync("npm run web-vitals:track", { stdio: "inherit" }); // Parse Web Vitals results const vitalsReportPath = path.join( __dirname, "..", ".next", "web-vitals", "report.json", ); if (fs.existsSync(vitalsReportPath)) { const vitalsData = JSON.parse( fs.readFileSync(vitalsReportPath, "utf8"), ); this.results.webVitals = vitalsData; console.log("โœ… Web Vitals tracking setup complete"); } this.results.summary.passed += 1; this.results.summary.total += 1; } catch (error) { console.error("โŒ Web Vitals tracking failed:", error.message); this.results.summary.failed += 1; this.results.summary.total += 1; } } /** * Run Lighthouse CI */ async runLighthouseCI() { try { // Check if server is running try { execSync("curl -s http://localhost:3000 > /dev/null", { stdio: "pipe", }); } catch { console.warn( "โš ๏ธ Development server not running, skipping Lighthouse CI...", ); this.results.summary.warnings += 1; this.results.summary.total += 1; return; } execSync("npm run lhci", { stdio: "inherit" }); // Parse Lighthouse results const lhciResultsPath = path.join(__dirname, "..", ".lighthouseci"); if (fs.existsSync(lhciResultsPath)) { const files = fs.readdirSync(lhciResultsPath); const resultFile = files.find((f) => f.endsWith(".json")); if (resultFile) { const lhciData = JSON.parse( fs.readFileSync(path.join(lhciResultsPath, resultFile), "utf8"), ); this.results.lighthouse = lhciData; console.log("โœ… Lighthouse CI completed"); } } this.results.summary.passed += 1; this.results.summary.total += 1; } catch (error) { console.warn("โš ๏ธ Lighthouse CI failed:", error.message); this.results.summary.warnings += 1; this.results.summary.total += 1; } } /** * Generate comprehensive test report */ generateComprehensiveReport() { // Ensure test results directory exists if (!fs.existsSync(TEST_RESULTS_DIR)) { fs.mkdirSync(TEST_RESULTS_DIR, { recursive: true }); } const reportPath = path.join( TEST_RESULTS_DIR, "performance-test-report.json", ); fs.writeFileSync(reportPath, JSON.stringify(this.results, null, 2)); // Generate markdown report this.generateMarkdownReport(); } /** * Generate markdown test report */ generateMarkdownReport() { const reportPath = path.join( TEST_RESULTS_DIR, "performance-test-report.md", ); let report = `# Performance Test Report\n\n`; report += `**Generated:** ${this.results.timestamp}\n\n`; // Summary report += `## Test Summary\n\n`; report += `- **Total Tests:** ${this.results.summary.total}\n`; report += `- **Passed:** ${this.results.summary.passed} โœ…\n`; report += `- **Failed:** ${this.results.summary.failed} โŒ\n`; report += `- **Warnings:** ${this.results.summary.warnings} โš ๏ธ\n\n`; // Bundle Analysis Results if (Object.keys(this.results.bundleAnalysis).length > 0) { report += `## Bundle Analysis\n\n`; if ( this.results.bundleAnalysis.budgetViolations && this.results.bundleAnalysis.budgetViolations.length > 0 ) { report += `### Budget Violations\n\n`; this.results.bundleAnalysis.budgetViolations.forEach((violation) => { report += `- **${violation.file}**: ${ violation.currentSize }KB (exceeds ${violation.maxSize}KB by ${ violation.overage }KB) - ${violation.severity.toUpperCase()}\n`; }); } else { report += `โœ… No bundle budget violations found\n\n`; } } // Performance Monitoring Results if (Object.keys(this.results.performanceMonitoring).length > 0) { report += `## Performance Monitoring\n\n`; if ( this.results.performanceMonitoring.budgetViolations && this.results.performanceMonitoring.budgetViolations.length > 0 ) { report += `### Budget Violations\n\n`; this.results.performanceMonitoring.budgetViolations.forEach( (violation) => { report += `- **${violation.metric}**: ${ violation.current } (exceeds ${ violation.budget }) - ${violation.severity.toUpperCase()}\n`; }, ); } else { report += `โœ… No performance budget violations found\n\n`; } } // Web Vitals Results if (Object.keys(this.results.webVitals).length > 0) { report += `## Web Vitals Tracking\n\n`; report += `โœ… Web Vitals tracking setup complete\n\n`; } // Lighthouse Results if (Object.keys(this.results.lighthouse).length > 0) { report += `## Lighthouse CI\n\n`; report += `โœ… Lighthouse CI completed successfully\n\n`; } // Recommendations report += `## Recommendations\n\n`; report += `- Monitor bundle sizes regularly\n`; report += `- Track Core Web Vitals in production\n`; report += `- Run performance tests in CI/CD pipeline\n`; report += `- Set up performance budgets and alerts\n`; fs.writeFileSync(reportPath, report); } } // Run if called directly if (require.main === module) { const tester = new PerformanceTester(); tester.runTests().catch(console.error); } module.exports = PerformanceTester;