/
Writing Own Custom Reporter with custom execution of Playwright Script

Writing Own Custom Reporter with custom execution of Playwright Script

Custom execution of Playwright script can also be done one step at a time using async handler of express by passing one step at a time in a loop. This allows to use a completely independent custom reporter. Below is an example:

CustomReporter.js

class CustomReporter { constructor() { this.results = []; } logStep(stepIndex, command, status, duration, errorMessage = null) { const stepResult = { stepNumber: stepIndex + 1, stepCommand: command, status, duration: `${duration}ms`, timestamp: new Date().toISOString(), errorMessage: errorMessage, }; this.results.push(stepResult); console.log(`[Step ${stepIndex + 1}] ${status.toUpperCase()}: ${command} (Duration: ${duration}ms)`); } saveReport() { // Save or send the report as needed console.log("Final Test Report:", JSON.stringify(this.results, null, 2)); } } export default CustomReporter;

 

playwrightCustomExecution.js

import asyncHandler from "express-async-handler"; import {chromium} from "playwright"; import CustomReporter from "./customReporter.js"; // Custom Reporter // @route POST /api/pw/exe // @access Public const execution = asyncHandler(async (req, res) => { const {playwright_test_steps} = req.body; if (playwright_test_steps) { const reporter = new CustomReporter(); // Initialize reporter let browser; try { (async () => { browser = await chromium.launch({ headless: false, }); const context = await browser.newContext(); const page = await context.newPage(); for (let i = 0; i < playwright_test_steps.length; i++) { const command = playwright_test_steps[i]; const startTime = Date.now(); // Start time for execution try { await eval(command.replace("page", "page").replace("await ", "")); const duration = Date.now() - startTime; // Calculate duration reporter.logStep(i, command, "passed", duration); } catch (error) { const duration = Date.now() - startTime; // Calculate duration reporter.logStep(i, command, "failed", duration, error.message); await page.screenshot({path: `error-${i}.png`}); reporter.saveReport(); throw new Error(`Error executing test step ${i}: ${error.message}`); } } await context.close(); await browser.close(); // Save the final report reporter.saveReport(); res.json({ message: "Test steps executed successfully", report: reporter.results, }); })(); } catch (error) { console.log("Error executing test steps", error.message); if (browser) await browser.close(); // res.status(500).json({ // message: "Error executing test steps", // error: error.message, // report: reporter.results, // }); } } else { res.status(401); throw new Error("Invalid Script"); } }); export {execution};

 

testResults.json

[ { "stepNumber": 1, "stepCommand": "await page.goto('https://sandbox.mabl.com/');", "status": "passed", "duration": "4231ms", "timestamp": "2024-08-21T15:08:25.121Z", "errorMessage": null }, { "stepNumber": 2, "stepCommand": "await page.getByRole('button', { name: 'alert' }).click();", "status": "passed", "duration": "88ms", "timestamp": "2024-08-21T15:08:25.210Z", "errorMessage": null }, { "stepNumber": 3, "stepCommand": "page.once('dialog', (dialog) => { console.log(`Dialog message: ${dialog.message()}`); dialog.dismiss().catch(() => {}); });", "status": "passed", "duration": "1ms", "timestamp": "2024-08-21T15:08:25.211Z", "errorMessage": null }, { "stepNumber": 4, "stepCommand": "await page.getByRole('button', { name: 'open alert' }).click();", "status": "passed", "duration": "55ms", "timestamp": "2024-08-21T15:08:25.270Z", "errorMessage": null }, { "stepNumber": 5, "stepCommand": "await page.getByRole('link', { name: 'mabl' }).click();", "status": "passed", "duration": "198ms", "timestamp": "2024-08-21T15:08:25.468Z", "errorMessage": null }, { "stepNumber": 6, "stepCommand": "await page.getByRole('button', { name: 'dropdowns' }).click();", "status": "passed", "duration": "140ms", "timestamp": "2024-08-21T15:08:25.608Z", "errorMessage": null }, { "stepNumber": 7, "stepCommand": "await page.getByLabel('Dropdown select').locator('div').first().click();", "status": "passed", "duration": "160ms", "timestamp": "2024-08-21T15:08:25.768Z", "errorMessage": null }, { "stepNumber": 8, "stepCommand": "await page.getByLabel('george costanza').click();", "status": "passed", "duration": "95ms", "timestamp": "2024-08-21T15:08:25.864Z", "errorMessage": null }, { "stepNumber": 9, "stepCommand": "await page.getByRole('link', { name: 'mabl' }).click();", "status": "passed", "duration": "69ms", "timestamp": "2024-08-21T15:08:25.934Z", "errorMessage": null } ]

 

Advantages:

  • Full Flexibility: Complete control over every aspect of the report, from data collection to formatting, allowing you to meet highly specific requirements.

  • Tailored Solutions: You can create a reporting solution that perfectly aligns with your organization's needs, preferences, and standards.

  • Unique Features: Ability to add unique features, visualizations, or integrations that aren't available in built-in or third-party options.

Disadvantages:

  • Time-Consuming: Developing a custom reporting solution from scratch can be labor-intensive and time-consuming.

  • Maintenance Overhead: You'll be responsible for maintaining and updating the custom reporting tool, which can add to the project's technical debt.

  • Requires Expertise: Building a custom reporting API requires in-depth knowledge of both Playwright and general software development practices, potentially increasing the complexity for less experienced teams.