UNPKG

mocha-multiple-sessions-detailed-runner

Version:
267 lines (225 loc) 8.71 kB
import type { IReportService } from '../../interfaces/services/report-service'; import type { DetailedReport, CombinedReport } from '../../interfaces/core/types'; import type { ReportSummary } from '../../interfaces/use-cases/report-use-cases'; import type { IReportRepository } from '../../interfaces/repositories/report-repository'; import type { ISessionMetaRepository } from '../../interfaces/repositories/session-meta-repository'; import type { IUnifiedEventBus } from '../../interfaces/events/unified-event-bus'; export class ReportService implements IReportService { constructor( private reportRepository: IReportRepository, private sessionRepository: ISessionMetaRepository, private eventBus: IUnifiedEventBus ) {} getSessionReport(label: string): DetailedReport | null { const sessionMeta = this.sessionRepository.findByLabel(label); if (!sessionMeta) return null; const reportData = this.reportRepository.getReportData(sessionMeta.resultKey); if (!reportData) return null; const detailedReport: DetailedReport = { data: reportData, stats: reportData.stats || { tests: 0, passes: 0, failures: 0, pending: 0 }, timestamp: reportData.timestamp ? new Date(reportData.timestamp) : new Date(), sessionLabel: label, size: JSON.stringify(reportData).length }; return detailedReport; } async waitForSessionReport(label: string, timeoutMs: number = 30000): Promise<DetailedReport> { const sessionMeta = this.sessionRepository.findByLabel(label); if (!sessionMeta) { throw new Error(`Session '${label}' not found`); } const reportData = await this.reportRepository.waitForReport(sessionMeta.resultKey, timeoutMs); return { data: reportData, stats: reportData.stats || { tests: 0, passes: 0, failures: 0, pending: 0 }, timestamp: reportData.timestamp ? new Date(reportData.timestamp) : new Date(), sessionLabel: label, size: JSON.stringify(reportData).length }; } hasSessionReport(label: string): boolean { const sessionMeta = this.sessionRepository.findByLabel(label); if (!sessionMeta) return false; return this.reportRepository.hasReport(sessionMeta.resultKey); } generateCombinedReport(): CombinedReport { const allSessions = this.sessionRepository.findAll(); const sessions: Record<string, DetailedReport> = {}; let totalTests = 0; let totalPasses = 0; let totalFailures = 0; let totalPending = 0; let successfulSessions = 0; let failedSessions = 0; for (const sessionMeta of allSessions) { const report = this.getSessionReport(sessionMeta.label); if (report) { sessions[sessionMeta.label] = report; totalTests += report.stats.tests; totalPasses += report.stats.passes; totalFailures += report.stats.failures; totalPending += report.stats.pending; if (report.stats.failures === 0) { successfulSessions++; } else { failedSessions++; } } } const combinedReport: CombinedReport = { generatedAt: new Date().toISOString(), summary: { totalTests, totalPasses, totalFailures, totalPending, totalSessions: allSessions.length, successfulSessions, failedSessions }, sessions }; // Emit report generated event this.eventBus.emitRunnerEvent({ type: 'runner:combined-report-generated', timestamp: new Date(), data: { reportType: 'combined', totalSessions: allSessions.length, reportSize: JSON.stringify(combinedReport).length, generatedAt: new Date(), summary: combinedReport.summary } }); return combinedReport; } async getCombinedReportAsync(): Promise<CombinedReport> { // For now, just return the sync version // In the future, this could wait for all reports to be ready return this.generateCombinedReport(); } watchSessionReport(label: string, callback: (report: DetailedReport) => void): () => void { const sessionMeta = this.sessionRepository.findByLabel(label); if (!sessionMeta) { throw new Error(`Session '${label}' not found`); } return this.reportRepository.watchReport(sessionMeta.resultKey, (reportData: any) => { const detailedReport: DetailedReport = { data: reportData, stats: reportData.stats || { tests: 0, passes: 0, failures: 0, pending: 0 }, timestamp: reportData.timestamp ? new Date(reportData.timestamp) : new Date(), sessionLabel: label, size: JSON.stringify(reportData).length }; callback(detailedReport); }); } watchAllReports(callback: (sessionLabel: string, report: DetailedReport) => void): () => void { const unsubscribeFunctions: (() => void)[] = []; const allSessions = this.sessionRepository.findAll(); for (const sessionMeta of allSessions) { const unsubscribe = this.watchSessionReport(sessionMeta.label, (report) => { callback(sessionMeta.label, report); }); unsubscribeFunctions.push(unsubscribe); } // Return function to unsubscribe from all return () => { unsubscribeFunctions.forEach(fn => fn()); }; } getReportSummary(): ReportSummary { const allSessions = this.sessionRepository.findAll(); const reportSizes: Record<string, number> = {}; let totalReports = 0; let totalTests = 0; let totalPasses = 0; let totalFailures = 0; let oldestReport: Date | undefined; let newestReport: Date | undefined; for (const sessionMeta of allSessions) { const report = this.getSessionReport(sessionMeta.label); if (report) { totalReports++; totalTests += report.stats.tests; totalPasses += report.stats.passes; totalFailures += report.stats.failures; reportSizes[sessionMeta.label] = report.size || 0; if (!oldestReport || report.timestamp < oldestReport) { oldestReport = report.timestamp; } if (!newestReport || report.timestamp > newestReport) { newestReport = report.timestamp; } } } return { totalReports, totalTests, totalPasses, totalFailures, reportSizes, ...(oldestReport && { oldestReport }), ...(newestReport && { newestReport }) }; } async exportReports(format: 'json' | 'html'): Promise<string> { const combinedReport = this.generateCombinedReport(); if (format === 'json') { return JSON.stringify(combinedReport, null, 2); } else { // Basic HTML export - could be enhanced return ` <html> <head><title>Test Report</title></head> <body> <h1>Combined Test Report</h1> <p>Generated: ${combinedReport.generatedAt}</p> <h2>Summary</h2> <pre>${JSON.stringify(combinedReport.summary, null, 2)}</pre> <h2>Sessions</h2> <pre>${JSON.stringify(combinedReport.sessions, null, 2)}</pre> </body> </html> `; } } clearAllReports(): void { const allSessions = this.sessionRepository.findAll(); for (const sessionMeta of allSessions) { if (this.reportRepository.hasReport(sessionMeta.resultKey)) { this.reportRepository.clearReport(sessionMeta.resultKey); } } } validateSessionReport(sessionLabel: string): boolean { const report = this.getSessionReport(sessionLabel); return report !== null && this.reportRepository.validateReport(report.data); } getReportValidationErrors(sessionLabel: string): string[] { const errors: string[] = []; const report = this.getSessionReport(sessionLabel); if (!report) { errors.push('Report not found'); return errors; } if (!report.data) { errors.push('Report data is missing'); } if (!report.stats) { errors.push('Report stats are missing'); } else { if (typeof report.stats.tests !== 'number') { errors.push('Invalid test count'); } if (typeof report.stats.passes !== 'number') { errors.push('Invalid pass count'); } if (typeof report.stats.failures !== 'number') { errors.push('Invalid failure count'); } } return errors; } }