import { Page, ConsoleMessage } from '@playwright/test'; export interface ConsoleError { type: 'error' | 'warning' | 'pageerror'; message: string; location?: string; timestamp: Date; } /** * ConsoleMonitor - Aggregates and tracks all console output * * Purpose: Catch ANY runtime errors during Electron app lifecycle * * Critical Detections: * - "Module has been externalized for browser compatibility" * - "__dirname is not defined" * - "require is not defined" * - Any uncaught exceptions */ export class ConsoleMonitor { private errors: ConsoleError[] = []; private warnings: ConsoleError[] = []; private isMonitoring = false; /** * Start monitoring console output on the page */ startMonitoring(page: Page): void { if (this.isMonitoring) { return; } // Monitor console.error calls page.on('console', (msg: ConsoleMessage) => { if (msg.type() === 'error') { this.errors.push({ type: 'error', message: msg.text(), location: msg.location()?.url, timestamp: new Date(), }); } else if (msg.type() === 'warning') { this.warnings.push({ type: 'warning', message: msg.text(), location: msg.location()?.url, timestamp: new Date(), }); } }); // Monitor uncaught exceptions page.on('pageerror', (error: Error) => { const errorObj: ConsoleError = { type: 'pageerror', message: error.message, timestamp: new Date(), }; if (error.stack) { errorObj.location = error.stack; } this.errors.push(errorObj); }); this.isMonitoring = true; } /** * Check if any errors were detected */ hasErrors(): boolean { return this.errors.length > 0; } /** * Get all detected errors */ getErrors(): ConsoleError[] { return [...this.errors]; } /** * Get all detected warnings */ getWarnings(): ConsoleError[] { return [...this.warnings]; } /** * Format errors for test output */ formatErrors(): string { if (this.errors.length === 0) { return 'No errors detected'; } const lines = ['Console errors detected during test:', '']; this.errors.forEach((error, index) => { lines.push(`${index + 1}. [${error.type}] ${error.message}`); if (error.location) { lines.push(` Location: ${error.location}`); } lines.push(''); }); return lines.join('\n'); } /** * Check for specific browser context errors */ hasBrowserContextErrors(): boolean { const contextErrorPatterns = [ /has been externalized for browser compatibility/i, /__dirname is not defined/i, /require is not defined/i, /Cannot access .* in client code/i, ]; return this.errors.some(error => contextErrorPatterns.some(pattern => pattern.test(error.message)) ); } /** * Reset monitoring state */ reset(): void { this.errors = []; this.warnings = []; } }