Files
leptos-shadcn-ui/tests/e2e/wasm-performance-monitor.ts
Peter Hanssens c3759fb019 feat: Complete Phase 2 Infrastructure Implementation
🏗️ MAJOR MILESTONE: Phase 2 Infrastructure Complete

This commit delivers a comprehensive, production-ready infrastructure system
for leptos-shadcn-ui with full automation, testing, and monitoring capabilities.

## 🎯 Infrastructure Components Delivered

### 1. WASM Browser Testing 
- Cross-browser WASM compatibility testing (Chrome, Firefox, Safari, Mobile)
- Performance monitoring with initialization time, memory usage, interaction latency
- Memory leak detection and pressure testing
- Automated error handling and recovery
- Bundle analysis and optimization recommendations
- Comprehensive reporting (HTML, JSON, Markdown)

### 2. E2E Test Integration 
- Enhanced Playwright configuration with CI/CD integration
- Multi-browser testing with automated execution
- Performance regression testing and monitoring
- Comprehensive reporting with artifact management
- Environment detection (CI vs local)
- GitHub Actions workflow with notifications

### 3. Performance Benchmarking 
- Automated regression testing with baseline comparison
- Real-time performance monitoring with configurable intervals
- Multi-channel alerting (console, file, webhook, email)
- Performance trend analysis and prediction
- CLI benchmarking tools and automated monitoring
- Baseline management and optimization recommendations

### 4. Accessibility Automation 
- WCAG compliance testing (A, AA, AAA levels)
- Comprehensive accessibility audit automation
- Screen reader support and keyboard navigation testing
- Color contrast and focus management validation
- Custom accessibility rules and violation detection
- Component-specific accessibility testing

## 🚀 Key Features

- **Production Ready**: All systems ready for immediate production use
- **CI/CD Integration**: Complete GitHub Actions workflow
- **Automated Monitoring**: Real-time performance and accessibility monitoring
- **Cross-Browser Support**: Chrome, Firefox, Safari, Mobile Chrome, Mobile Safari
- **Comprehensive Reporting**: Multiple output formats with detailed analytics
- **Error Recovery**: Graceful failure handling and recovery mechanisms

## 📁 Files Added/Modified

### New Infrastructure Files
- tests/e2e/wasm-browser-testing.spec.ts
- tests/e2e/wasm-performance-monitor.ts
- tests/e2e/wasm-test-config.ts
- tests/e2e/e2e-test-runner.ts
- tests/e2e/accessibility-automation.ts
- tests/e2e/accessibility-enhanced.spec.ts
- performance-audit/src/regression_testing.rs
- performance-audit/src/automated_monitoring.rs
- performance-audit/src/bin/performance-benchmark.rs
- scripts/run-wasm-tests.sh
- scripts/run-performance-benchmarks.sh
- scripts/run-accessibility-audit.sh
- .github/workflows/e2e-tests.yml
- playwright.config.ts

### Enhanced Configuration
- Enhanced Makefile with comprehensive infrastructure commands
- Enhanced global setup and teardown for E2E tests
- Performance audit system integration

### Documentation
- docs/infrastructure/PHASE2_INFRASTRUCTURE_GUIDE.md
- docs/infrastructure/INFRASTRUCTURE_SETUP_GUIDE.md
- docs/infrastructure/PHASE2_COMPLETION_SUMMARY.md
- docs/testing/WASM_TESTING_GUIDE.md

## 🎯 Usage

### Quick Start
```bash
# Run all infrastructure tests
make test

# Run WASM browser tests
make test-wasm

# Run E2E tests
make test-e2e-enhanced

# Run performance benchmarks
make benchmark

# Run accessibility audit
make accessibility-audit
```

### Advanced Usage
```bash
# Run tests on specific browsers
make test-wasm-browsers BROWSERS=chromium,firefox

# Run with specific WCAG level
make accessibility-audit-wcag LEVEL=AAA

# Run performance regression tests
make regression-test

# Start automated monitoring
make performance-monitor
```

## 📊 Performance Metrics

- **WASM Initialization**: <5s (Chrome) to <10s (Mobile Safari)
- **First Paint**: <3s (Chrome) to <5s (Mobile Safari)
- **Interaction Latency**: <100ms average
- **Memory Usage**: <50% increase during operations
- **WCAG Compliance**: AA level with AAA support

## 🎉 Impact

This infrastructure provides:
- **Reliable Component Development**: Comprehensive testing and validation
- **Performance Excellence**: Automated performance monitoring and optimization
- **Accessibility Compliance**: WCAG compliance validation and reporting
- **Production Deployment**: CI/CD integration with automated testing

## 🚀 Next Steps

Ready for Phase 3: Component Completion
- Complete remaining 41 components using established patterns
- Leverage infrastructure for comprehensive testing
- Ensure production-ready quality across all components

**Status**:  PHASE 2 COMPLETE - READY FOR PRODUCTION

Closes: Phase 2 Infrastructure Implementation
Related: #infrastructure #testing #automation #ci-cd
2025-09-20 12:31:11 +10:00

348 lines
10 KiB
TypeScript

/**
* WASM Performance Monitoring Utility
*
* This utility provides comprehensive monitoring and analysis of WASM performance
* across different browsers and scenarios.
*/
export interface WASMPerformanceMetrics {
initializationTime: number;
memoryUsage: {
initial: number;
peak: number;
current: number;
};
bundleSize: number;
loadTime: number;
firstPaint: number;
firstContentfulPaint: number;
interactionLatency: number[];
errorCount: number;
browserInfo: {
name: string;
version: string;
userAgent: string;
webAssemblySupport: boolean;
};
}
export interface WASMTestResult {
testName: string;
browser: string;
success: boolean;
metrics: WASMPerformanceMetrics;
errors: string[];
timestamp: Date;
}
export class WASMPerformanceMonitor {
private metrics: Partial<WASMPerformanceMetrics> = {};
private startTime: number = 0;
private errors: string[] = [];
constructor() {
this.startTime = performance.now();
this.setupErrorHandling();
}
private setupErrorHandling(): void {
// Capture WASM-related errors
window.addEventListener('error', (event) => {
if (event.message.includes('wasm') || event.message.includes('WebAssembly')) {
this.errors.push(`WASM Error: ${event.message}`);
}
});
window.addEventListener('unhandledrejection', (event) => {
if (event.reason && event.reason.toString().includes('wasm')) {
this.errors.push(`WASM Promise Rejection: ${event.reason}`);
}
});
}
/**
* Start monitoring WASM initialization
*/
async startInitializationMonitoring(): Promise<void> {
this.startTime = performance.now();
// Monitor memory usage
if (performance.memory) {
this.metrics.memoryUsage = {
initial: performance.memory.usedJSHeapSize,
peak: performance.memory.usedJSHeapSize,
current: performance.memory.usedJSHeapSize
};
}
// Monitor bundle size
this.metrics.bundleSize = await this.measureBundleSize();
}
/**
* Complete initialization monitoring and capture metrics
*/
async completeInitializationMonitoring(): Promise<void> {
const endTime = performance.now();
this.metrics.initializationTime = endTime - this.startTime;
// Capture final memory usage
if (performance.memory && this.metrics.memoryUsage) {
this.metrics.memoryUsage.current = performance.memory.usedJSHeapSize;
this.metrics.memoryUsage.peak = Math.max(
this.metrics.memoryUsage.peak,
performance.memory.usedJSHeapSize
);
}
// Capture paint timing
const paintEntries = performance.getEntriesByType('paint');
paintEntries.forEach(entry => {
if (entry.name === 'first-paint') {
this.metrics.firstPaint = entry.startTime;
} else if (entry.name === 'first-contentful-paint') {
this.metrics.firstContentfulPaint = entry.startTime;
}
});
// Capture load timing
const navEntries = performance.getEntriesByType('navigation');
if (navEntries.length > 0) {
const navEntry = navEntries[0] as PerformanceNavigationTiming;
this.metrics.loadTime = navEntry.loadEventEnd - navEntry.loadEventStart;
}
// Capture browser info
this.metrics.browserInfo = this.getBrowserInfo();
}
/**
* Measure interaction latency
*/
measureInteractionLatency(interaction: () => void): number {
const start = performance.now();
interaction();
const end = performance.now();
const latency = end - start;
if (!this.metrics.interactionLatency) {
this.metrics.interactionLatency = [];
}
this.metrics.interactionLatency.push(latency);
return latency;
}
/**
* Get comprehensive performance metrics
*/
getMetrics(): WASMPerformanceMetrics {
return {
initializationTime: this.metrics.initializationTime || 0,
memoryUsage: this.metrics.memoryUsage || {
initial: 0,
peak: 0,
current: 0
},
bundleSize: this.metrics.bundleSize || 0,
loadTime: this.metrics.loadTime || 0,
firstPaint: this.metrics.firstPaint || 0,
firstContentfulPaint: this.metrics.firstContentfulPaint || 0,
interactionLatency: this.metrics.interactionLatency || [],
errorCount: this.errors.length,
browserInfo: this.metrics.browserInfo || this.getBrowserInfo()
};
}
/**
* Get errors encountered during monitoring
*/
getErrors(): string[] {
return [...this.errors];
}
/**
* Check if performance meets benchmarks
*/
meetsBenchmarks(): { passed: boolean; failures: string[] } {
const failures: string[] = [];
const metrics = this.getMetrics();
// Performance benchmarks
if (metrics.initializationTime > 5000) {
failures.push(`WASM initialization too slow: ${metrics.initializationTime}ms (max: 5000ms)`);
}
if (metrics.firstPaint > 3000) {
failures.push(`First paint too slow: ${metrics.firstPaint}ms (max: 3000ms)`);
}
if (metrics.firstContentfulPaint > 4000) {
failures.push(`First contentful paint too slow: ${metrics.firstContentfulPaint}ms (max: 4000ms)`);
}
if (metrics.interactionLatency.length > 0) {
const avgLatency = metrics.interactionLatency.reduce((a, b) => a + b, 0) / metrics.interactionLatency.length;
if (avgLatency > 100) {
failures.push(`Average interaction latency too high: ${avgLatency.toFixed(2)}ms (max: 100ms)`);
}
}
// Memory benchmarks
if (metrics.memoryUsage.peak > metrics.memoryUsage.initial * 2) {
failures.push(`Memory usage doubled during initialization`);
}
return {
passed: failures.length === 0,
failures
};
}
/**
* Generate performance report
*/
generateReport(): string {
const metrics = this.getMetrics();
const benchmarks = this.meetsBenchmarks();
let report = `# WASM Performance Report\n\n`;
report += `**Browser**: ${metrics.browserInfo.name} ${metrics.browserInfo.version}\n`;
report += `**Test Time**: ${new Date().toISOString()}\n\n`;
report += `## Performance Metrics\n\n`;
report += `- **Initialization Time**: ${metrics.initializationTime.toFixed(2)}ms\n`;
report += `- **First Paint**: ${metrics.firstPaint.toFixed(2)}ms\n`;
report += `- **First Contentful Paint**: ${metrics.firstContentfulPaint.toFixed(2)}ms\n`;
report += `- **Load Time**: ${metrics.loadTime.toFixed(2)}ms\n`;
report += `- **Bundle Size**: ${(metrics.bundleSize / 1024).toFixed(2)}KB\n\n`;
report += `## Memory Usage\n\n`;
report += `- **Initial**: ${(metrics.memoryUsage.initial / 1024 / 1024).toFixed(2)}MB\n`;
report += `- **Peak**: ${(metrics.memoryUsage.peak / 1024 / 1024).toFixed(2)}MB\n`;
report += `- **Current**: ${(metrics.memoryUsage.current / 1024 / 1024).toFixed(2)}MB\n\n`;
if (metrics.interactionLatency.length > 0) {
const avgLatency = metrics.interactionLatency.reduce((a, b) => a + b, 0) / metrics.interactionLatency.length;
report += `## Interaction Performance\n\n`;
report += `- **Average Latency**: ${avgLatency.toFixed(2)}ms\n`;
report += `- **Max Latency**: ${Math.max(...metrics.interactionLatency).toFixed(2)}ms\n`;
report += `- **Min Latency**: ${Math.min(...metrics.interactionLatency).toFixed(2)}ms\n\n`;
}
report += `## Benchmark Results\n\n`;
if (benchmarks.passed) {
report += `✅ **All benchmarks passed**\n\n`;
} else {
report += `❌ **Benchmark failures**:\n`;
benchmarks.failures.forEach(failure => {
report += `- ${failure}\n`;
});
report += `\n`;
}
if (this.errors.length > 0) {
report += `## Errors Encountered\n\n`;
this.errors.forEach(error => {
report += `- ${error}\n`;
});
report += `\n`;
}
return report;
}
private async measureBundleSize(): Promise<number> {
try {
// Try to measure bundle size from network requests
const scripts = Array.from(document.querySelectorAll('script[src]'));
let totalSize = 0;
for (const script of scripts) {
const src = script.getAttribute('src');
if (src && (src.includes('.wasm') || src.includes('wasm'))) {
try {
const response = await fetch(src, { method: 'HEAD' });
const contentLength = response.headers.get('content-length');
if (contentLength) {
totalSize += parseInt(contentLength);
}
} catch (e) {
// Ignore fetch errors
}
}
}
return totalSize;
} catch (e) {
return 0;
}
}
private getBrowserInfo(): WASMPerformanceMetrics['browserInfo'] {
const userAgent = navigator.userAgent;
let name = 'Unknown';
let version = 'Unknown';
if (userAgent.includes('Chrome')) {
name = 'Chrome';
const match = userAgent.match(/Chrome\/(\d+)/);
if (match) version = match[1];
} else if (userAgent.includes('Firefox')) {
name = 'Firefox';
const match = userAgent.match(/Firefox\/(\d+)/);
if (match) version = match[1];
} else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
name = 'Safari';
const match = userAgent.match(/Version\/(\d+)/);
if (match) version = match[1];
} else if (userAgent.includes('Edge')) {
name = 'Edge';
const match = userAgent.match(/Edge\/(\d+)/);
if (match) version = match[1];
}
return {
name,
version,
userAgent,
webAssemblySupport: typeof WebAssembly !== 'undefined'
};
}
}
/**
* Utility function to run WASM performance tests
*/
export async function runWASMPerformanceTest(
testName: string,
browserName: string,
testFunction: (monitor: WASMPerformanceMonitor) => Promise<void>
): Promise<WASMTestResult> {
const monitor = new WASMPerformanceMonitor();
try {
await monitor.startInitializationMonitoring();
await testFunction(monitor);
await monitor.completeInitializationMonitoring();
return {
testName,
browser: browserName,
success: true,
metrics: monitor.getMetrics(),
errors: monitor.getErrors(),
timestamp: new Date()
};
} catch (error) {
return {
testName,
browser: browserName,
success: false,
metrics: monitor.getMetrics(),
errors: [...monitor.getErrors(), `Test Error: ${(error as Error).message}`],
timestamp: new Date()
};
}
}