import { Reporter, TestCase, TestResult } from '@playwright/test/reporter'; import * as fs from 'fs'; interface TestMetrics { total: number; passed: number; failed: number; skipped: number; duration: number; passRate: number; avgDuration: number; flakyTests: string[]; } class MetricsReporter implements Reporter { private tests: Array<{ test: TestCase; result: TestResult }> = []; onTestEnd(test: TestCase, result: TestResult) { this.tests.push({ test, result }); } onEnd() { const metrics: TestMetrics = { total: this.tests.length, passed: this.tests.filter(t => t.result.status === 'passed').length, failed: this.tests.filter(t => t.result.status === 'failed').length, skipped: this.tests.filter(t => t.result.status === 'skipped').length, duration: this.tests.reduce((sum, t) => sum + t.result.duration, 0), passRate: 0, avgDuration: 0, flakyTests: [], }; metrics.passRate = metrics.total > 0 ? (metrics.passed / metrics.total) * 100 : 0; metrics.avgDuration = metrics.total > 0 ? metrics.duration / metrics.total : 0; const flakyTests = this.tests.filter( t => t.result.status === 'passed' && t.result.retryCount > 0 ); metrics.flakyTests = flakyTests.map(t => t.test.title); if (!fs.existsSync('reports')) { fs.mkdirSync('reports', { recursive: true }); } fs.writeFileSync( 'reports/test-metrics.json', JSON.stringify(metrics, null, 2) ); console.log('\n=== 测试质量指标 ==='); console.log(`总测试数: ${metrics.total}`); console.log(`通过: ${metrics.passed}`); console.log(`失败: ${metrics.failed}`); console.log(`跳过: ${metrics.skipped}`); console.log(`通过率: ${metrics.passRate.toFixed(2)}%`); console.log(`平均执行时间: ${(metrics.avgDuration / 1000).toFixed(2)}秒`); console.log(`总执行时间: ${(metrics.duration / 1000).toFixed(2)}秒`); if (metrics.flakyTests.length > 0) { console.log(`\n⚠️ Flaky 测试 (${metrics.flakyTests.length}):`); metrics.flakyTests.forEach(title => console.log(` - ${title}`)); } } } export default MetricsReporter;