mirror of
https://github.com/cloud-shuttle/leptos-shadcn-ui.git
synced 2025-12-22 22:00:00 +00:00
🏗️ 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
348 lines
10 KiB
TypeScript
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()
|
|
};
|
|
}
|
|
}
|