UNPKG

mocha-multiple-sessions-detailed-runner

Version:
363 lines (312 loc) 11.5 kB
import type { IInitializeRunnerUseCase, IDefineSessionUseCase, IRunSingleSessionUseCase, IRunAllSessionsUseCase, IClearAllSessionsUseCase, IDestroyRunnerUseCase } from '../../interfaces/use-cases/runner-use-cases'; import type { MultiSessionConfig, SessionResult, SessionMeta } from '../../interfaces/core/types'; import type { BatchExecutionOptions } from '../../interfaces/core/batch-execution'; import type { ISessionMetaRepository } from '../../interfaces/repositories/session-meta-repository'; import type { IReportRepository } from '../../interfaces/repositories/report-repository'; import type { IUnifiedEventBus } from '../../interfaces/events/unified-event-bus'; import type { IMochaIntegration } from '../../interfaces/infrastructure/mocha-integration'; // Use the global interface from mocha-integration.ts declare global { interface Window { MochaMultipleSessions?: { testSessionSetup: (config: any) => void; onAnySessionEvent: (callback: (event: any) => void) => () => void; testSession: (label: string, setupFn: () => void, options?: any) => Promise<string>; runSession: (label: string) => Promise<any>; runAllSessions: () => Promise<any[]>; }; } } export class InitializeRunnerUseCase implements IInitializeRunnerUseCase { constructor( private mochaIntegration: IMochaIntegration, private eventBus: IUnifiedEventBus ) {} execute(config?: Partial<MultiSessionConfig>): void { const finalConfig: MultiSessionConfig = { windowVariablePrefix: 'mochaSession__', autoInit: true, captureConsoleLog: true, sourceCode: true, attachStats: true, ...config }; try { this.mochaIntegration.setupTestEnvironment(finalConfig); this.eventBus.emitRunnerEvent({ type: 'runner:initialized', timestamp: new Date(), data: { configUsed: finalConfig, sessionCount: 0, timestamp: new Date() } }); } catch (error) { this.eventBus.emitRunnerEvent({ type: 'runner:config-updated', timestamp: new Date(), data: { oldConfig: {}, newConfig: finalConfig, changes: ['initialization_failed'] } }); throw error; } } } export class DefineSessionUseCase implements IDefineSessionUseCase { constructor( private sessionRepository: ISessionMetaRepository, private reportRepository: IReportRepository, private eventBus: IUnifiedEventBus ) {} execute(label: string, setupFn: () => void): void { const resultKey = this.reportRepository.generateResultKey(label); const sessionMeta: SessionMeta = { label, resultKey, setupFn, status: 'ready', timestamp: new Date() }; this.sessionRepository.save(sessionMeta); this.eventBus.emitRunnerEvent({ type: 'runner:session-defined', timestamp: new Date(), data: { sessionLabel: label, sessionMeta, totalSessions: this.sessionRepository.findAll().length } }); } } export class RunSingleSessionUseCase implements IRunSingleSessionUseCase { constructor( private sessionRepository: ISessionMetaRepository, private reportRepository: IReportRepository, private eventBus: IUnifiedEventBus ) {} async execute(label: string): Promise<SessionResult> { const sessionMeta = this.sessionRepository.findByLabel(label); if (!sessionMeta) { throw new Error(`Session '${label}' not found`); } try { // Mark as running this.sessionRepository.markAsRunning(label); // Use the existing mocha-multiple-sessions library if (!window.MochaMultipleSessions) { throw new Error('MochaMultipleSessions library not available'); } // Create the session first with better error handling try { await window.MochaMultipleSessions.testSession(label, sessionMeta.setupFn); console.log(`Session '${label}' created successfully`); } catch (sessionCreationError) { console.error(`Failed to create session '${label}':`, sessionCreationError); throw new Error(`Session creation failed for '${label}': ${sessionCreationError}`); } // Run the session with better error handling let result; try { result = await window.MochaMultipleSessions.runSession(label); console.log(`Session '${label}' execution completed:`, result); } catch (sessionRunError) { console.error(`Failed to run session '${label}':`, sessionRunError); throw new Error(`Session execution failed for '${label}': ${sessionRunError}`); } // Wait for detailed report to be available try { await this.reportRepository.waitForReport(sessionMeta.resultKey, 5000); console.log(`Detailed report available for '${label}'`); } catch (waitError) { console.warn(`Timeout waiting for detailed report for ${label}:`, waitError); // Don't throw here, continue with basic result } // Mark as completed this.sessionRepository.markAsCompleted(label, result); const sessionResult: SessionResult = { label, status: result.success ? 'passed' : 'failed', success: result.success, timestamp: new Date(), duration: result.duration, stats: result.stats }; console.log(`Session '${label}' result:`, sessionResult); return sessionResult; } catch (error) { console.error(`Error in session '${label}':`, error); this.sessionRepository.markAsFailed(label, error as Error); const sessionResult: SessionResult = { label, status: 'failed', success: false, timestamp: new Date(), error: error as Error }; return sessionResult; } } } export class RunAllSessionsUseCase implements IRunAllSessionsUseCase { constructor( private sessionRepository: ISessionMetaRepository, private runSingleSessionUseCase: IRunSingleSessionUseCase, private eventBus: IUnifiedEventBus ) {} async execute(options?: BatchExecutionOptions): Promise<Record<string, SessionResult>> { const allSessions = this.sessionRepository.findAll(); if (allSessions.length === 0) { throw new Error('No sessions defined'); } const sessionLabels = allSessions.map(s => s.label); const startTime = Date.now(); // Emit batch start event this.eventBus.emitRunnerEvent({ type: 'runner:batch-started', timestamp: new Date(), data: { sessionLabels, totalSessions: sessionLabels.length, executionMode: options?.mode || 'sequential', options } }); const results: Record<string, SessionResult> = {}; let completed = 0; try { if (options?.mode === 'parallel') { // Run in parallel const promises = sessionLabels.map(async (label: string) => { const result = await this.runSingleSessionUseCase.execute(label); results[label] = result; completed++; this.eventBus.emitRunnerEvent({ type: 'runner:batch-progress', timestamp: new Date(), data: { completed, total: sessionLabels.length, currentSession: label, completedSessions: Object.keys(results), failedSessions: Object.keys(results).filter((l: string) => !results[l]?.success), progress: Math.round((completed / sessionLabels.length) * 100) } }); return result; }); await Promise.all(promises); } else { // Run sequentially for (const label of sessionLabels) { if (options?.stopOnFirstFailure && completed > 0) { const hasFailures = Object.values(results).some((r: SessionResult) => !r.success); if (hasFailures) break; } const result = await this.runSingleSessionUseCase.execute(label); results[label] = result; completed++; this.eventBus.emitRunnerEvent({ type: 'runner:batch-progress', timestamp: new Date(), data: { completed, total: sessionLabels.length, currentSession: label, completedSessions: Object.keys(results), failedSessions: Object.keys(results).filter((l: string) => !results[l]?.success), progress: Math.round((completed / sessionLabels.length) * 100) } }); } } const duration = Date.now() - startTime; const successfulSessions = Object.values(results).filter(r => r.success).length; const failedSessions = Object.values(results).filter(r => !r.success).length; // Emit completion event this.eventBus.emitRunnerEvent({ type: 'runner:batch-completed', timestamp: new Date(), data: { totalSessions: sessionLabels.length, successfulSessions, failedSessions, duration, sessionResults: results, overallSuccess: failedSessions === 0 } }); return results; } catch (error) { // Emit failure event this.eventBus.emitRunnerEvent({ type: 'runner:batch-failed', timestamp: new Date(), data: { error: error as Error, completedSessions: Object.keys(results), partialResults: results } }); throw error; } } } export class ClearAllSessionsUseCase implements IClearAllSessionsUseCase { constructor( private sessionRepository: ISessionMetaRepository, private reportRepository: IReportRepository, private eventBus: IUnifiedEventBus ) {} async execute(): Promise<void> { const allSessions = this.sessionRepository.findAll(); const clearedSessions = allSessions.map(s => s.label); const clearedReports: string[] = []; // Clear all reports for (const session of allSessions) { if (this.reportRepository.hasReport(session.resultKey)) { this.reportRepository.clearReport(session.resultKey); clearedReports.push(session.resultKey); } } // Clear session repository this.sessionRepository.clear(); // Emit cleared event this.eventBus.emitRunnerEvent({ type: 'runner:cleared', timestamp: new Date(), data: { clearedSessions, clearedReports } }); } } export class DestroyRunnerUseCase implements IDestroyRunnerUseCase { constructor( private clearAllSessionsUseCase: IClearAllSessionsUseCase, private eventBus: IUnifiedEventBus ) {} async execute(): Promise<void> { // Clear all sessions first await this.clearAllSessionsUseCase.execute(); // Emit destroy event this.eventBus.emitRunnerEvent({ type: 'runner:destroyed', timestamp: new Date() }); // Destroy event bus this.eventBus.destroy(); } }