import { TestCase, TestResult } from '@playwright/test/reporter';
import { BaseReporter, ReportMetrics } from './BaseReporter';
import fs from 'fs/promises';
import path from 'path';
export class HtmlReporter extends BaseReporter {
private outputDir: string;
private template: string;
constructor(options: { outputDir: string }) {
super();
this.outputDir = options.outputDir;
this.template = `
Test Report
`;
}
async onEnd() {
const counts = this.getStatusCounts();
const content = [];
// Summary section
content.push(`
Test Summary
Total Tests: ${counts.passed + counts.failed + counts.skipped}
Passed: ${counts.passed}
Failed: ${counts.failed}
Skipped: ${counts.skipped}
`);
// Test cases
this.testResults.forEach((results, title) => {
results.forEach(result => {
const metrics = this.metrics.get(title);
const attachments = this.getAttachments(result);
content.push(`
${title}
Duration: ${this.formatDuration(metrics?.duration || 0)}
Memory: ${Math.round(metrics?.memory || 0)}MB
Network Requests: ${metrics?.networkRequests || 0}
${result.error ? `
Error:
${result.error.message}
` : ''}
${this.renderAttachments(attachments)}
`);
});
});
// Generate the final HTML
const html = this.template.replace(
'',
`${content.join('')}
`
);
// Ensure output directory exists
await fs.mkdir(this.outputDir, { recursive: true });
// Write the report
await fs.writeFile(
path.join(this.outputDir, 'report.html'),
html
);
// Copy attachments to the output directory
await this.copyAttachments();
}
private async copyAttachments() {
const attachmentsDir = path.join(this.outputDir, 'attachments');
await fs.mkdir(attachmentsDir, { recursive: true });
for (const results of this.testResults.values()) {
for (const result of results) {
for (const attachment of result.attachments) {
if (attachment.path) {
const destPath = path.join(attachmentsDir, path.basename(attachment.path));
await fs.copyFile(attachment.path, destPath);
}
}
}
}
}
private renderAttachments(attachments: ReturnType) {
const elements = [];
if (attachments.screenshots.length) {
elements.push('Screenshots:
');
attachments.screenshots.forEach(screenshot => {
if (screenshot.path) {
const filename = path.basename(screenshot.path);
elements.push(`
`);
}
});
elements.push('
');
}
if (attachments.videos.length) {
elements.push('Videos:
');
attachments.videos.forEach(video => {
if (video.path) {
const filename = path.basename(video.path);
elements.push(`
`);
}
});
elements.push('
');
}
if (attachments.traces.length) {
elements.push('Traces:
');
attachments.traces.forEach(trace => {
if (trace.path) {
const filename = path.basename(trace.path);
elements.push(`
`);
}
});
elements.push('
');
}
return elements.join('');
}
}