/** * Console Error Pattern Analysis Tests * Analyzes frontend console errors, categorizes patterns, and monitors error frequencies * Provides insights into application stability and potential issues */ import { test, expect } from '@playwright/test'; import { authenticateWithRealCredentials, selectCompany, REAL_CREDENTIALS } from '../../utils/real-auth.js'; import { setupConsoleCapture, ErrorClassifier, generateErrorReport } from '../../utils/console-monitor.js'; test.describe('Console Error Pattern Analysis', () => { const commonErrorPatterns = [ { pattern: /Failed to fetch/i, category: 'Network Error', severity: 'WARNING' }, { pattern: /Network request failed/i, category: 'Network Error', severity: 'WARNING' }, { pattern: /404.*not found/i, category: '404 Error', severity: 'WARNING' }, { pattern: /Uncaught TypeError/i, category: 'JavaScript Error', severity: 'CRITICAL' }, { pattern: /Uncaught ReferenceError/i, category: 'JavaScript Error', severity: 'CRITICAL' }, { pattern: /Vue warn/i, category: 'Vue Warning', severity: 'WARNING' }, { pattern: /Component.*not found/i, category: 'Component Error', severity: 'WARNING' }, { pattern: /Oracle.*connection/i, category: 'Database Error', severity: 'CRITICAL' }, { pattern: /Authentication.*failed/i, category: 'Auth Error', severity: 'CRITICAL' }, { pattern: /Cannot read property/i, category: 'Property Error', severity: 'CRITICAL' }, { pattern: /Cannot access before initialization/i, category: 'Initialization Error', severity: 'CRITICAL' } ]; test.beforeEach(async ({ page }) => { setupConsoleCapture(page); }); test.afterEach(async ({ page }) => { const report = generateErrorReport(page, test.info().title); if (report.summary.classifications.critical > 0) { console.warn('🚨 Critical console errors detected:', report.details.criticalErrors); } // Log error pattern summary if (Object.keys(report.details.errorPatterns).length > 0) { console.log('📊 Error patterns detected:', report.details.errorPatterns); } }); test('should detect and categorize frontend errors during navigation', async ({ page }) => { console.log('🔍 Analyzing console errors during complete navigation flow...'); // Navigate through all main application views const navigationFlow = [ { action: () => page.goto('/login'), name: 'Login Page' }, { action: () => authenticateWithRealCredentials(page), name: 'Authentication' }, { action: () => page.goto('/dashboard'), name: 'Dashboard' }, { action: () => selectCompany(page, REAL_CREDENTIALS.company), name: 'Company Selection' }, { action: () => page.goto('/invoices'), name: 'Invoices Page' }, { action: () => page.goto('/payments'), name: 'Payments Page' }, { action: () => page.goto('/dashboard'), name: 'Return to Dashboard' } ]; const errorsByStep = {}; for (const step of navigationFlow) { console.log(`📍 Navigating to: ${step.name}`); const initialErrorCount = (page.consoleMessages || []).length; try { await step.action(); await page.waitForLoadState('networkidle', { timeout: 10000 }); } catch (error) { console.warn(`âš ī¸ Navigation warning for ${step.name}:`, error.message); } const newErrors = (page.consoleMessages || []).slice(initialErrorCount); errorsByStep[step.name] = newErrors; console.log(`📊 ${step.name}: ${newErrors.length} new console messages`); } // Analyze error patterns across all steps const allErrors = Object.values(errorsByStep).flat(); const errorsByPattern = {}; const errorsBySeverity = { CRITICAL: 0, WARNING: 0, INFO: 0, UNKNOWN: 0 }; allErrors.forEach(error => { // Classify by severity const severity = ErrorClassifier.classify(error); errorsBySeverity[severity]++; // Classify by pattern const pattern = commonErrorPatterns.find(p => p.pattern.test(error.text || error.error || '')); if (pattern) { errorsByPattern[pattern.category] = (errorsByPattern[pattern.category] || 0) + 1; } else if (error.type === 'error') { errorsByPattern['Unclassified Error'] = (errorsByPattern['Unclassified Error'] || 0) + 1; } }); console.log('📈 Error Analysis Summary:'); console.log(` Total Console Messages: ${allErrors.length}`); console.log(` By Severity:`, errorsBySeverity); console.log(` By Pattern:`, errorsByPattern); // Validate error thresholds expect(errorsBySeverity.CRITICAL, 'Critical errors detected during navigation').toBe(0); expect(errorsBySeverity.WARNING, 'Excessive warnings during navigation').toBeLessThan(10); // Check for high-frequency patterns Object.entries(errorsByPattern).forEach(([pattern, count]) => { if (count > 3) { console.warn(`âš ī¸ High frequency error pattern: ${pattern} (${count} occurrences)`); } }); console.log('✅ Console error pattern analysis completed'); }); test('should monitor error frequencies and identify recurring issues', async ({ page }) => { console.log('📊 Monitoring error frequencies across multiple operations...'); // Authenticate first await authenticateWithRealCredentials(page); await selectCompany(page, REAL_CREDENTIALS.company); const operations = [ { name: 'Dashboard Refresh', action: () => page.reload() }, { name: 'Invoices Navigation', action: () => page.goto('/invoices') }, { name: 'Payments Navigation', action: () => page.goto('/payments') }, { name: 'Dashboard Return', action: () => page.goto('/dashboard') }, { name: 'Company Re-selection', action: () => selectCompany(page, REAL_CREDENTIALS.company) } ]; const errorFrequencies = {}; const operationErrors = {}; for (let cycle = 0; cycle < 2; cycle++) { console.log(`🔄 Error monitoring cycle ${cycle + 1}/2`); for (const operation of operations) { const initialMessageCount = (page.consoleMessages || []).length; try { await operation.action(); await page.waitForLoadState('networkidle', { timeout: 8000 }); } catch (error) { console.warn(`âš ī¸ Operation ${operation.name} encountered issue:`, error.message); } const newMessages = (page.consoleMessages || []).slice(initialMessageCount); const errorMessages = newMessages.filter(msg => msg.type === 'error' || msg.type === 'pageerror'); operationErrors[`${operation.name}_Cycle${cycle + 1}`] = errorMessages; // Track error frequencies errorMessages.forEach(error => { const errorText = error.text || error.error || ''; const pattern = commonErrorPatterns.find(p => p.pattern.test(errorText)); const key = pattern ? pattern.category : 'Unclassified'; errorFrequencies[key] = (errorFrequencies[key] || 0) + 1; }); console.log(` ${operation.name}: ${errorMessages.length} errors`); } } // Analyze recurring patterns console.log('🔍 Error Frequency Analysis:'); const recurringIssues = Object.entries(errorFrequencies) .filter(([_pattern, count]) => count > 2) .sort((a, b) => b[1] - a[1]); if (recurringIssues.length > 0) { console.log('🚨 Recurring Error Patterns:'); recurringIssues.forEach(([pattern, count]) => { console.log(` ${pattern}: ${count} occurrences`); }); } else { console.log('✅ No recurring error patterns detected'); } // Validate error thresholds const totalErrors = Object.values(errorFrequencies).reduce((sum, count) => sum + count, 0); expect(totalErrors, 'Excessive total errors across operations').toBeLessThan(20); // Critical patterns should not recur const criticalRecurring = recurringIssues.filter(([pattern]) => commonErrorPatterns.find(p => p.category === pattern && p.severity === 'CRITICAL') ); expect(criticalRecurring.length, `Critical recurring errors: ${JSON.stringify(criticalRecurring)}`).toBe(0); console.log('✅ Error frequency monitoring completed'); }); test('should detect performance-related console warnings', async ({ page }) => { console.log('⚡ Detecting performance-related console warnings...'); await authenticateWithRealCredentials(page); const performanceKeywords = [ 'slow', 'performance', 'memory', 'leak', 'timeout', 'blocking', 'lag', 'delay', 'optimization', 'cache' ]; // Perform operations that might trigger performance warnings const heavyOperations = [ { name: 'Large Data Load', action: () => selectCompany(page, REAL_CREDENTIALS.company) }, { name: 'Invoices with Filtering', action: async () => { await page.goto('/invoices'); await page.waitForSelector('[data-testid="invoices-table"]', { timeout: 15000 }); // Trigger filtering operations if (await page.locator('[data-testid="search-input"]').isVisible()) { await page.fill('[data-testid="search-input"]', 'test'); await page.keyboard.press('Enter'); await page.waitForTimeout(2000); } }}, { name: 'Multiple Page Navigation', action: async () => { const pages = ['/dashboard', '/invoices', '/payments', '/dashboard']; for (const pagePath of pages) { await page.goto(pagePath); await page.waitForLoadState('networkidle', { timeout: 5000 }); } }} ]; const performanceWarnings = []; for (const operation of heavyOperations) { console.log(`🔧 Executing: ${operation.name}`); const initialMessageCount = (page.consoleMessages || []).length; const startTime = Date.now(); await operation.action(); const operationTime = Date.now() - startTime; const newMessages = (page.consoleMessages || []).slice(initialMessageCount); const perfMessages = newMessages.filter(msg => { const text = msg.text || msg.error || ''; return performanceKeywords.some(keyword => text.toLowerCase().includes(keyword.toLowerCase()) ); }); if (perfMessages.length > 0) { performanceWarnings.push({ operation: operation.name, operationTime, warnings: perfMessages, count: perfMessages.length }); console.log(`âš ī¸ ${operation.name}: ${perfMessages.length} performance warnings (${operationTime}ms)`); } else { console.log(`✅ ${operation.name}: No performance warnings (${operationTime}ms)`); } } // Analyze performance warnings if (performanceWarnings.length > 0) { console.log('📊 Performance Warning Analysis:'); performanceWarnings.forEach(warning => { console.log(` ${warning.operation}: ${warning.count} warnings, ${warning.operationTime}ms`); warning.warnings.forEach(w => { console.log(` - ${w.text || w.error}`); }); }); // Performance warnings should be investigated but not fail tests const totalPerfWarnings = performanceWarnings.reduce((sum, w) => sum + w.count, 0); if (totalPerfWarnings > 5) { console.warn(`âš ī¸ High number of performance warnings: ${totalPerfWarnings}`); } } else { console.log('✅ No performance-related console warnings detected'); } // Critical performance issues should not be present const criticalPerfIssues = (page.consoleMessages || []).filter(msg => { const text = msg.text || msg.error || ''; return msg.type === 'error' && performanceKeywords.some(keyword => text.toLowerCase().includes(keyword.toLowerCase()) ); }); expect(criticalPerfIssues.length, `Critical performance errors: ${JSON.stringify(criticalPerfIssues)}`).toBe(0); console.log('✅ Performance warning detection completed'); }); test('should analyze error context and provide debugging information', async ({ page }) => { console.log('đŸ”Ŧ Analyzing error context for debugging insights...'); await authenticateWithRealCredentials(page); // Collect errors with context const contextualErrors = []; // Navigate through application collecting error context const testScenarios = [ { name: 'Invalid Route Access', action: () => page.goto('/nonexistent-route'), expectErrors: true }, { name: 'Rapid Navigation', action: async () => { await page.goto('/dashboard'); await page.goto('/invoices'); await page.goto('/payments'); await page.goto('/dashboard'); }, expectErrors: false }, { name: 'Form Interaction', action: async () => { await page.goto('/invoices'); if (await page.locator('[data-testid="search-input"]').isVisible()) { await page.fill('[data-testid="search-input"]', 'test search'); await page.keyboard.press('Enter'); } }, expectErrors: false } ]; for (const scenario of testScenarios) { console.log(`🎭 Testing scenario: ${scenario.name}`); const initialMessageCount = (page.consoleMessages || []).length; try { await scenario.action(); await page.waitForLoadState('networkidle', { timeout: 8000 }); } catch (error) { console.log(`â„šī¸ Expected error in ${scenario.name}:`, error.message); } const newMessages = (page.consoleMessages || []).slice(initialMessageCount); const errors = newMessages.filter(msg => msg.type === 'error' || msg.type === 'pageerror'); if (errors.length > 0) { errors.forEach(error => { contextualErrors.push({ scenario: scenario.name, error: error, url: page.url(), timestamp: error.timestamp, expected: scenario.expectErrors }); }); console.log(`📍 ${scenario.name}: ${errors.length} errors (expected: ${scenario.expectErrors})`); } else { console.log(`✅ ${scenario.name}: No errors detected`); } } // Analyze contextual errors if (contextualErrors.length > 0) { console.log('🔍 Contextual Error Analysis:'); // Group errors by type and scenario const errorsByScenario = {}; const errorsByType = {}; contextualErrors.forEach(error => { // Group by scenario if (!errorsByScenario[error.scenario]) { errorsByScenario[error.scenario] = []; } errorsByScenario[error.scenario].push(error); // Group by error type const errorText = error.error.text || error.error.error || ''; const pattern = commonErrorPatterns.find(p => p.pattern.test(errorText)); const category = pattern ? pattern.category : 'Unclassified'; errorsByType[category] = (errorsByType[category] || 0) + 1; }); console.log('📊 Errors by Scenario:'); Object.entries(errorsByScenario).forEach(([scenario, errors]) => { console.log(` ${scenario}: ${errors.length} errors`); }); console.log('📊 Errors by Type:'); Object.entries(errorsByType).forEach(([type, count]) => { console.log(` ${type}: ${count} occurrences`); }); // Identify unexpected errors (those in scenarios that shouldn't have errors) const unexpectedErrors = contextualErrors.filter(error => !error.expected); if (unexpectedErrors.length > 0) { console.warn('🚨 Unexpected errors detected:'); unexpectedErrors.forEach(error => { console.warn(` ${error.scenario}: ${error.error.text || error.error.error}`); }); // Unexpected critical errors should fail the test const criticalUnexpected = unexpectedErrors.filter(error => ErrorClassifier.classify(error.error) === 'CRITICAL' ); expect(criticalUnexpected.length, `Unexpected critical errors: ${JSON.stringify(criticalUnexpected.map(e => e.error.text))}`).toBe(0); } } else { console.log('✅ No contextual errors to analyze'); } console.log('✅ Error context analysis completed'); }); test('should generate comprehensive error report for debugging', async ({ page }) => { console.log('📋 Generating comprehensive error report...'); // Perform full application workflow await authenticateWithRealCredentials(page); await selectCompany(page, REAL_CREDENTIALS.company); const workflow = [ () => page.goto('/dashboard'), () => page.goto('/invoices'), () => page.goto('/payments'), () => page.goto('/dashboard') ]; for (const step of workflow) { await step(); await page.waitForLoadState('networkidle', { timeout: 8000 }); } // Generate final error report const finalReport = generateErrorReport(page, 'Complete Application Workflow'); console.log('📊 Final Error Report:'); console.log(' Test:', finalReport.testName); console.log(' Timestamp:', finalReport.timestamp); console.log(' Summary:', finalReport.summary); if (finalReport.details.criticalErrors.length > 0) { console.log('🚨 Critical Errors:'); finalReport.details.criticalErrors.forEach(error => { console.log(` - ${error.text || error.error} (${error.location?.url || 'unknown'})`); }); } if (finalReport.details.warnings.length > 0) { console.log('âš ī¸ Warnings:'); finalReport.details.warnings.slice(0, 5).forEach(warning => { console.log(` - ${warning.text || warning.error}`); }); if (finalReport.details.warnings.length > 5) { console.log(` ... and ${finalReport.details.warnings.length - 5} more warnings`); } } if (Object.keys(finalReport.details.errorPatterns).length > 0) { console.log('📈 Error Patterns:'); Object.entries(finalReport.details.errorPatterns).forEach(([pattern, count]) => { console.log(` ${pattern}: ${count} occurrences`); }); } // Performance metrics if (finalReport.performance && finalReport.performance.apiCalls) { const slowApiCalls = finalReport.performance.apiCalls.filter(call => call.timing > 2000); if (slowApiCalls.length > 0) { console.log('⚡ Slow API Calls:'); slowApiCalls.forEach(call => { console.log(` ${call.url}: ${call.timing}ms`); }); } } // Final validation expect(finalReport.summary.classifications.critical, 'Critical errors in comprehensive workflow').toBe(0); console.log('✅ Comprehensive error report generated successfully'); }); });