/** * Console Error Monitoring Infrastructure for ROA2WEB Testing * * Provides comprehensive console error tracking, classification, and performance monitoring * for Playwright tests with real Oracle data integration. */ /** * Error classification system for console messages */ export const ErrorClassifier = { CRITICAL: [ 'Authentication failed', 'Database connection', 'Uncaught TypeError', 'Uncaught ReferenceError', 'Oracle connection error', 'SSH tunnel failed', 'Failed to authenticate', 'Cannot read property', 'Cannot access before initialization' ], WARNING: [ '404 Not Found', 'Failed to fetch', 'Network request failed', 'Component warning', 'Vue warn', 'Resource loading error', 'Timeout exceeded', 'Connection refused' ], INFO: [ 'Development build', 'Vue devtools', '[HMR]', 'Hot reload', 'DevTools', 'webpack', 'vite' ], /** * Classify a console message based on its content * @param {Object} message - Console message object * @returns {string} Classification level */ classify(message) { const text = message.text || message.error || ''; if (this.CRITICAL.some(pattern => text.includes(pattern))) return 'CRITICAL'; if (this.WARNING.some(pattern => text.includes(pattern))) return 'WARNING'; if (this.INFO.some(pattern => text.includes(pattern))) return 'INFO'; return 'UNKNOWN'; }, /** * Check if message should be ignored in tests * @param {Object} message - Console message object * @returns {boolean} True if message should be ignored */ shouldIgnore(message) { const ignoredPatterns = [ 'DevTools listening', 'Debugging information', 'Chrome extension', 'webpack-dev-server', 'Live reload enabled' ]; const text = message.text || message.error || ''; return ignoredPatterns.some(pattern => text.includes(pattern)); } }; /** * Performance monitoring utilities */ export const PerformanceMonitor = { /** * Measure page load performance metrics * @param {Page} page - Playwright page object * @returns {Object} Performance metrics */ async measurePageLoad(page) { return await page.evaluate(() => { const timing = performance.timing; const navigation = performance.getEntriesByType('navigation')[0]; return { domContentLoaded: timing.domContentLoadedEventEnd - timing.navigationStart, loadComplete: timing.loadEventEnd - timing.navigationStart, firstPaint: performance.getEntriesByType('paint').find(p => p.name === 'first-paint')?.startTime || 0, firstContentfulPaint: performance.getEntriesByType('paint').find(p => p.name === 'first-contentful-paint')?.startTime || 0, timeToInteractive: navigation?.domInteractive - navigation?.fetchStart || 0 }; }); }, /** * Measure API response time * @param {Page} page - Playwright page object * @param {string} apiPattern - API endpoint pattern to monitor * @returns {Promise} Response time in milliseconds */ async measureApiResponse(page, apiPattern) { const startTime = Date.now(); await page.waitForResponse(response => response.url().includes(apiPattern), { timeout: 10000 }); return Date.now() - startTime; }, /** * Monitor network performance during test execution * @param {Page} page - Playwright page object * @returns {Object} Network performance data */ async getNetworkMetrics(page) { const resourceTiming = await page.evaluate(() => { return performance.getEntriesByType('resource').map(entry => ({ name: entry.name, duration: entry.duration, transferSize: entry.transferSize, type: entry.initiatorType })); }); const slowResources = resourceTiming .filter(resource => resource.duration > 1000) .sort((a, b) => b.duration - a.duration); return { totalResources: resourceTiming.length, slowResources: slowResources.slice(0, 5), averageResponseTime: resourceTiming.reduce((sum, r) => sum + r.duration, 0) / resourceTiming.length }; } }; /** * Console monitoring setup function for test beforeEach hooks * @param {Page} page - Playwright page object * @returns {Object} Monitoring data collectors */ export function setupConsoleCapture(page) { const consoleMessages = []; const networkErrors = []; const performanceMetrics = { startTime: Date.now(), apiCalls: [] }; // Capture console messages page.on('console', msg => { const message = { type: msg.type(), text: msg.text(), location: msg.location(), timestamp: new Date().toISOString(), args: msg.args() }; if (!ErrorClassifier.shouldIgnore(message)) { consoleMessages.push(message); } }); // Capture JavaScript errors page.on('pageerror', error => { const errorMessage = { type: 'pageerror', error: error.message, stack: error.stack, timestamp: new Date().toISOString() }; consoleMessages.push(errorMessage); }); // Capture network failures page.on('requestfailed', request => { const networkError = { url: request.url(), method: request.method(), failure: request.failure(), timestamp: new Date().toISOString() }; networkErrors.push(networkError); }); // Monitor API responses page.on('response', response => { if (response.url().includes('/api/')) { performanceMetrics.apiCalls.push({ url: response.url(), status: response.status(), timing: Date.now() - performanceMetrics.startTime }); } }); // Store collectors on page object for test access page.consoleMessages = consoleMessages; page.networkErrors = networkErrors; page.performanceMetrics = performanceMetrics; return { consoleMessages, networkErrors, performanceMetrics }; } /** * Generate comprehensive error report * @param {Page} page - Playwright page object * @param {string} testName - Name of the test * @returns {Object} Error report */ export function generateErrorReport(page, testName) { const consoleMessages = page.consoleMessages || []; const networkErrors = page.networkErrors || []; // Classify console messages const classified = { critical: consoleMessages.filter(msg => ErrorClassifier.classify(msg) === 'CRITICAL'), warning: consoleMessages.filter(msg => ErrorClassifier.classify(msg) === 'WARNING'), info: consoleMessages.filter(msg => ErrorClassifier.classify(msg) === 'INFO'), unknown: consoleMessages.filter(msg => ErrorClassifier.classify(msg) === 'UNKNOWN') }; // Find error patterns const errorPatterns = {}; consoleMessages.forEach(msg => { if (msg.type === 'error') { const pattern = findErrorPattern(msg.text); errorPatterns[pattern] = (errorPatterns[pattern] || 0) + 1; } }); return { testName, timestamp: new Date().toISOString(), summary: { totalConsoleMessages: consoleMessages.length, totalNetworkErrors: networkErrors.length, classifications: { critical: classified.critical.length, warning: classified.warning.length, info: classified.info.length, unknown: classified.unknown.length } }, details: { criticalErrors: classified.critical, warnings: classified.warning, networkErrors, errorPatterns }, performance: page.performanceMetrics }; } /** * Find common error patterns in console messages * @param {string} errorText - Error message text * @returns {string} Error pattern category */ function findErrorPattern(errorText) { const patterns = [ { pattern: /Failed to fetch/, category: 'Network Error' }, { pattern: /404.*not found/i, category: '404 Error' }, { pattern: /Uncaught TypeError/, category: 'JavaScript TypeError' }, { pattern: /Vue warn/, category: 'Vue Warning' }, { pattern: /Component.*not found/, category: 'Component Error' }, { pattern: /Oracle.*connection/, category: 'Database Error' }, { pattern: /Authentication.*failed/, category: 'Auth Error' } ]; const match = patterns.find(p => p.pattern.test(errorText)); return match ? match.category : 'Unknown Error'; } /** * Assert no critical console errors in test * @param {Page} page - Playwright page object * @param {Object} expect - Playwright expect object */ export function assertNoCriticalErrors(page, expect) { const consoleMessages = page.consoleMessages || []; const criticalErrors = consoleMessages.filter(msg => ErrorClassifier.classify(msg) === 'CRITICAL' ); if (criticalErrors.length > 0) { const errorDetails = criticalErrors.map(err => `${err.type}: ${err.text || err.error} at ${err.location?.url || 'unknown'}:${err.location?.lineNumber || 0}` ).join('\n'); expect(criticalErrors, `Critical console errors found:\n${errorDetails}`).toHaveLength(0); } } /** * Performance baselines for ROA2WEB application */ export const PerformanceBaselines = { loginTime: 2000, // Max 2s for login dashboardLoad: 3000, // Max 3s for dashboard reportGeneration: 5000, // Max 5s for reports apiResponse: 1500, // Max 1.5s for API calls pageLoad: 4000 // Max 4s for page loads }; /** * Assert performance meets baselines * @param {number} actualTime - Actual measured time * @param {number} baseline - Performance baseline * @param {string} operation - Operation name * @param {Object} expect - Playwright expect object */ export function assertPerformanceBaseline(actualTime, baseline, operation, expect) { expect(actualTime, `${operation} took ${actualTime}ms, expected < ${baseline}ms`).toBeLessThan(baseline); }