import { Reporter, TestCase, TestResult, TestStep } from '@playwright/test/reporter'; export interface ReportMetrics { duration: number; memory: number; networkRequests: number; } export abstract class BaseReporter implements Reporter { protected testResults: Map = new Map(); protected metrics: Map = new Map(); onBegin(config: any, suite: any) { this.testResults.clear(); this.metrics.clear(); } onTestEnd(test: TestCase, result: TestResult) { if (!this.testResults.has(test.title)) { this.testResults.set(test.title, []); } this.testResults.get(test.title)?.push(result); // Collect metrics this.metrics.set(test.title, { duration: result.duration, memory: this.calculateMemoryUsage(result), networkRequests: this.countNetworkRequests(result) }); } protected getStatusCounts() { let passed = 0, failed = 0, skipped = 0; this.testResults.forEach(results => { results.forEach(result => { if (result.status === 'passed') passed++; else if (result.status === 'failed') failed++; else if (result.status === 'skipped') skipped++; }); }); return { passed, failed, skipped }; } protected calculateMemoryUsage(result: TestResult): number { // Extract memory info from test result attachments or metadata return result.attachments .filter(a => a.name === 'memory-snapshot') .reduce((total, snapshot) => total + (snapshot.body ? JSON.parse(snapshot.body.toString()).memoryUsage : 0), 0); } protected countNetworkRequests(result: TestResult): number { return result.attachments .filter(a => a.name === 'network-requests') .reduce((total, reqs) => total + (reqs.body ? JSON.parse(reqs.body.toString()).length : 0), 0); } protected getAttachments(result: TestResult) { return { screenshots: result.attachments.filter(a => a.contentType?.startsWith('image/')), videos: result.attachments.filter(a => a.contentType?.startsWith('video/')), traces: result.attachments.filter(a => a.name === 'trace') }; } protected formatDuration(ms: number): string { if (ms < 1000) return `${ms}ms`; return `${(ms / 1000).toFixed(2)}s`; } }