Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagejs
import {exec} from "child_process";
 
exec("npx playwright test --reporter="./myreporter/PlaywrightCustomReporter.js"")

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

Code Block
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

Code Block
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

Code Block
languagejson
 [
  {
    "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
  }
]