import * as fs from 'fs'; import * as path from 'path'; interface TestResult { testName: string; status: 'passed' | 'failed' | 'skipped'; duration: number; error?: string; screenshot?: string; video?: string; } interface TestSuite { suiteName: string; tests: TestResult[]; totalTests: number; passedTests: number; failedTests: number; skippedTests: number; totalDuration: number; } interface TestReport { timestamp: string; testSuites: TestSuite[]; summary: { totalSuites: number; totalTests: number; totalPassed: number; totalFailed: number; totalSkipped: number; totalDuration: number; passRate: number; }; } export class UniappTestReporter { private results: TestSuite[] = []; private startTime: number = Date.now(); addTestSuite(suiteName: string, tests: TestResult[]): void { const passedTests = tests.filter(t => t.status === 'passed').length; const failedTests = tests.filter(t => t.status === 'failed').length; const skippedTests = tests.filter(t => t.status === 'skipped').length; const totalDuration = tests.reduce((sum, t) => sum + t.duration, 0); this.results.push({ suiteName, tests, totalTests: tests.length, passedTests, failedTests, skippedTests, totalDuration, }); } generateReport(): TestReport { const totalSuites = this.results.length; const totalTests = this.results.reduce((sum, s) => sum + s.totalTests, 0); const totalPassed = this.results.reduce((sum, s) => sum + s.passedTests, 0); const totalFailed = this.results.reduce((sum, s) => sum + s.failedTests, 0); const totalSkipped = this.results.reduce((sum, s) => sum + s.skippedTests, 0); const totalDuration = this.results.reduce((sum, s) => sum + s.totalDuration, 0); const passRate = totalTests > 0 ? (totalPassed / totalTests) * 100 : 0; return { timestamp: new Date().toISOString(), testSuites: this.results, summary: { totalSuites, totalTests, totalPassed, totalFailed, totalSkipped, totalDuration, passRate, }, }; } async generateJSONReport(outputPath: string): Promise { const report = this.generateReport(); const dir = path.dirname(outputPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } fs.writeFileSync(outputPath, JSON.stringify(report, null, 2), 'utf-8'); console.log(`JSON report generated: ${outputPath}`); } async generateHTMLReport(outputPath: string): Promise { const report = this.generateReport(); const dir = path.dirname(outputPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } const html = this.generateHTMLContent(report); fs.writeFileSync(outputPath, html, 'utf-8'); console.log(`HTML report generated: ${outputPath}`); } private generateHTMLContent(report: TestReport): string { const { summary, testSuites } = report; return ` Uniapp E2E测试报告

Uniapp E2E测试报告

生成时间: ${report.timestamp}
总测试数
${summary.totalTests}
通过
${summary.totalPassed}
失败
${summary.totalFailed}
跳过
${summary.totalSkipped}
通过率
${summary.passRate.toFixed(1)}%
总耗时
${(summary.totalDuration / 1000).toFixed(2)}s
${testSuites.map(suite => `

${suite.suiteName}

✓ 通过: ${suite.passedTests} ✗ 失败: ${suite.failedTests} ○ 跳过: ${suite.skippedTests} ⏱ 耗时: ${(suite.totalDuration / 1000).toFixed(2)}s
    ${suite.tests.map(test => `
  • ${test.status.toUpperCase()}
    ${test.testName}
    ${(test.duration / 1000).toFixed(2)}s
    ${test.error ? `
    ${test.error}
    ` : ''} ${test.screenshot || test.video ? ` ` : ''}
  • `).join('')}
`).join('')}
`; } async generateMarkdownReport(outputPath: string): Promise { const report = this.generateReport(); const dir = path.dirname(outputPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } const markdown = this.generateMarkdownContent(report); fs.writeFileSync(outputPath, markdown, 'utf-8'); console.log(`Markdown report generated: ${outputPath}`); } private generateMarkdownContent(report: TestReport): string { const { summary, testSuites } = report; let markdown = `# Uniapp E2E测试报告\n\n`; markdown += `**生成时间**: ${report.timestamp}\n\n`; markdown += `## 测试摘要\n\n`; markdown += `| 指标 | 数值 |\n`; markdown += `|------|------|\n`; markdown += `| 总测试数 | ${summary.totalTests} |\n`; markdown += `| 通过 | ${summary.totalPassed} |\n`; markdown += `| 失败 | ${summary.totalFailed} |\n`; markdown += `| 跳过 | ${summary.totalSkipped} |\n`; markdown += `| 通过率 | ${summary.passRate.toFixed(1)}% |\n`; markdown += `| 总耗时 | ${(summary.totalDuration / 1000).toFixed(2)}s |\n\n`; for (const suite of testSuites) { markdown += `## ${suite.suiteName}\n\n`; markdown += `**测试数**: ${suite.totalTests} | **通过**: ${suite.passedTests} | **失败**: ${suite.failedTests} | **跳过**: ${suite.skippedTests} | **耗时**: ${(suite.totalDuration / 1000).toFixed(2)}s\n\n`; for (const test of suite.tests) { markdown += `### ${test.testName}\n\n`; markdown += `- **状态**: ${test.status.toUpperCase()}\n`; markdown += `- **耗时**: ${(test.duration / 1000).toFixed(2)}s\n`; if (test.error) { markdown += `- **错误**: ${test.error}\n`; } if (test.screenshot) { markdown += `- **截图**: [查看](${test.screenshot})\n`; } if (test.video) { markdown += `- **视频**: [查看](${test.video})\n`; } markdown += `\n`; } } return markdown; } reset(): void { this.results = []; this.startTime = Date.now(); } }