import { promises as fs } from 'fs'; import { join } from 'path'; export class TestReporter { private results: TestResult[] = []; private startTime: number = 0; startReport(): void { this.startTime = Date.now(); console.log('📊 开始生成测试报告'); } recordResult(result: TestResult): void { this.results.push(result); } async generateAllReports(outputDir: string): Promise { await this.generateJSONReport(join(outputDir, 'test-results.json')); await this.generateHTMLReport(join(outputDir, 'test-results.html')); await this.generateJUnitReport(join(outputDir, 'junit-report.xml')); await this.generateSummaryReport(outputDir); } async generateJSONReport(outputPath: string): Promise { const report = { summary: this.getSummary(), results: this.results, metadata: { generatedAt: new Date().toISOString(), duration: Date.now() - this.startTime } }; await fs.writeFile(outputPath, JSON.stringify(report, null, 2)); console.log(`✅ JSON报告已生成: ${outputPath}`); } async generateHTMLReport(outputPath: string): Promise { const summary = this.getSummary(); const html = this.generateHTML(summary, this.results); await fs.writeFile(outputPath, html); console.log(`✅ HTML报告已生成: ${outputPath}`); } async generateJUnitReport(outputPath: string): Promise { const summary = this.getSummary(); const xml = this.generateJUnitXML(summary, this.results); await fs.writeFile(outputPath, xml); console.log(`✅ JUnit报告已生成: ${outputPath}`); } async generateSummaryReport(outputDir: string): Promise { const summary = this.getSummary(); const summaryPath = join(outputDir, 'summary.txt'); const summaryText = this.generateSummaryText(summary); await fs.writeFile(summaryPath, summaryText); console.log(`✅ 摘要报告已生成: ${summaryPath}`); } private getSummary(): TestSummary { const passed = this.results.filter(r => r.status === 'passed').length; const failed = this.results.filter(r => r.status === 'failed').length; const skipped = this.results.filter(r => r.status === 'skipped').length; const total = this.results.length; const duration = this.results.reduce((sum, r) => sum + r.duration, 0); return { total, passed, failed, skipped, passRate: total > 0 ? (passed / total * 100).toFixed(2) : '0', duration, startTime: new Date(this.startTime).toISOString(), endTime: new Date().toISOString() }; } private generateHTML(summary: TestSummary, results: TestResult[]): string { return ` E2E测试报告

E2E测试报告

测试摘要

总测试数: ${summary.total}

通过: ${summary.passed}

失败: ${summary.failed}

跳过: ${summary.skipped}

通过率: ${summary.passRate}%

总耗时: ${summary.duration}ms

测试结果

${results.map(result => `

${result.testName}

状态: ${result.status}

耗时: ${result.duration}ms

${result.error ? `

错误: ${result.error.message}

` : ''} ${result.steps.length > 0 ? `

测试步骤

    ${result.steps.map(step => `
  • ${step.name} - ${step.status}
  • `).join('')}
` : ''}
`).join('')} `; } private generateJUnitXML(summary: TestSummary, results: TestResult[]): string { const testCases = results.map(result => { const testCase = ` ${result.status === 'failed' ? ` ${result.error?.stack || ''} ` : ''} ${result.status === 'skipped' ? '' : ''} `; return testCase; }).join('\n'); return ` ${testCases} `; } private generateSummaryText(summary: TestSummary): string { return ` ======================================== E2E测试摘要报告 ======================================== 测试时间: ${summary.startTime} - ${summary.endTime} 总耗时: ${summary.duration}ms 测试统计: ---------------------------------------- 总测试数: ${summary.total} 通过: ${summary.passed} 失败: ${summary.failed} 跳过: ${summary.skipped} 通过率: ${summary.passRate}% ======================================== `; } } export interface TestResult { testName: string; status: 'passed' | 'failed' | 'skipped'; duration: number; steps: TestStep[]; logs: string[]; screenshots: string[]; error?: Error; } export interface TestStep { name: string; status: 'passed' | 'failed'; duration: number; } export interface TestSummary { total: number; passed: number; failed: number; skipped: number; passRate: string; duration: number; startTime: string; endTime: string; } export const testReporter = new TestReporter();