playwright-custom-reporter
Version:
A custom Playwright reporter.
140 lines (116 loc) • 4.78 kB
JavaScript
const { Reporter } = require('@playwright/test');
const fs = require('fs');
class CustomReporter {
constructor() {
this.results = { tests: [] }; // Store test results
}
onBegin(config, suite) {
console.log(`Starting the test run with ${suite.allTests().length} tests.`);
}
onTestBegin(test) {
console.log(`Running: ${test.title}`);
// Initialize step tracking for this test
test.stepDetails = [];
}
async onStepBegin(test, step) {
if (!test.stepDetails) test.stepDetails = [];
test.stepDetails.push({ title: step.title });
}
onTestEnd(test, result) {
const testResult = {
name: test.title,
status: result.status,
steps: test.stepDetails || [],
errors: result.errors.map(error => ({
message: this.extractErrorMessage(error.message),
stack: this.formatStackTrace(error.stack || 'No stack trace available.')
}))
};
this.results.tests.push(testResult);
if (result.status === 'passed') {
console.log(`✅ Passed: ${test.title}`);
} else if (result.status === 'failed') {
console.log(`❌ Failed: ${test.title}`);
console.log('\nError Details:');
for (const error of result.errors) {
// Extract the base error message
console.log(`Message: ${this.extractErrorMessage(error.message)}`);
// Extract and log the locator information
const locatorMatch = error.message.match(/expect\((.*?)\)\.toBeVisible/);
if (locatorMatch) {
console.log(`Locator: ${locatorMatch[1]}`);
}
// Log expected and received values
if (error.message.includes('expect(')) {
console.log(`Expected: visible`);
console.log(`Received: <element(s) not found>`);
}
// Add call log information
console.log('\nCall log:');
console.log(` - expect.toBeVisible with timeout 5000ms`);
console.log(` - waiting for ${locatorMatch ? locatorMatch[1] : 'element'}`);
// Log stack trace
const stack = this.formatStackTrace(error.stack || 'No stack trace available.');
if (stack) {
console.log(`\nStack Trace:\n${stack}`);
}
}
} else if (result.status === 'skipped') {
console.log(`⏭️ Skipped: ${test.title}`);
}
}
onEnd(result) {
console.log(`Finished the test run. Status: ${result.status}`);
// Save test results with steps to a JSON file
const reportPath = 'test-results/results.json';
fs.writeFileSync(reportPath, JSON.stringify(this.results, null, 2));
console.log(`✅ Test report saved to ${reportPath}`);
}
// Helper method to extract and refine error messages
extractErrorMessage(message) {
return message.split('\n')[0];
}
// Helper method to format stack traces
formatStackTrace(stack) {
return stack
.split('\n')
.filter(line => !line.includes('node_modules')) // Exclude lines from node_modules
.map(line => line.replace(/^\s*at\s+/, '')) // Remove "at" prefixes
.map(line => line.replace(process.cwd(), '.')) // Replace absolute paths with relative paths
.join('\n');
}
// Helper method to extract locator, expected, and received details
extractErrorDetails(message) {
const details = {
locator: null,
expected: null,
received: null,
callLog: []
};
// Extract locator information
const locatorMatch = message.match(/Locator: ([^\n]+)/);
if (locatorMatch) {
details.locator = locatorMatch[1];
}
// Extract expected value
const expectedMatch = message.match(/Expected: ([^\n]+)/);
if (expectedMatch) {
details.expected = expectedMatch[1];
}
// Extract received value
const receivedMatch = message.match(/Received: ([^\n]+)/);
if (receivedMatch) {
details.received = receivedMatch[1];
}
// Extract call log
const callLogSection = message.match(/Call log:\n((?: - .*\n?)*)/);
if (callLogSection) {
details.callLog = callLogSection[1]
.split('\n')
.filter(line => line.trim())
.map(line => line.replace(' - ', ''));
}
return details;
}
}
module.exports = CustomReporter;