Take a Screenshot when a Test Fails in Playwright Mocha

Running Playwright with Mocha (the most widely used test syntax framework in JavaScript) is beneficial and has several advantages over running with Playwright Test runner. See this article for running Playwright with Mocha in a Continuous Testing server.

This article will cover setting up screenshots on test failure for viewing in a CT Server like BuildWise. The video below shows the screenshot functionality in action.

Video of AgileTravel sample Playwright tests showing screenshots on test failure on BuildWise CT Server.

Why Screenshots

Screenshots can benefit a Continuous Testing (CT) Server when tests fail. It can show what the screen was when the test failed. This is good in a CT situation as it can provide information at a glance (e.g. assertion failed because the message is incorrect).

Note that taking screenshots when tests pass is not valuable and likely won’t be looked at, so there is no need to store and display these.

BuildWise (a CT server) supports saving screenshots and displaying them alongside failing tests. However, for Playwright (Mocha), some additional statements in the afterEach are required to save the screenshots.

The Code

We want to take a screenshot after each failing test. So we will use the afterEach hook function to take the screenshot and store it.

The complete code is below:

// import path
var path = require("path");

// ...
afterEach(async function() {
if (this.currentTest.state != "passed") {
var screenshot_file_dir = __dirname + '/../reports/screenshots/' + path.basename(__filename).replace(".js", ".xml")
var screenhost_file_name = this.currentTest.fullTitle() + ".png"
var screenshot_file_path = screenshot_file_dir + "/" + screenhost_file_name
await page.screenshot({ path: screenshot_file_path });

First, we only want to take the screenshot on a failed test, then store it under the /reports/screenshots/<test file name>.xml/<test name>.png. To detect if a test fails, we check if the test’s state (this.currentTest.state) is not equal to “passed”. We do this instead of ‘equal to “failed”’ because the state is possibly undefined (i.e. test timed out). In my case, I consider timing out a test failure.

Note that at the top of the file, we need to import path for it to be used as part of the actual function.

This afterEach block (in Mocha) saves the screenshot to a file when a test case fails. Add this afterEach block into the test script files if you want this feature. BuildWise will recognize and show on its web interface.

However, it is a quite large code block; extracting it to a shared test helper is better.

Moving it to Test Helper

Looking at the previous afterEach function, the information we need from each test is:

  • this.currentTest.state: whether or not the test passed
  • this.currentTest.fullTitle(): used for the screenshot file’s name
  • path.baseName(__filename): test’s filename e.g. 01_login_spec. Used for the location of where to store the screenshot
  • page : required to take the screenshot of the page.

In your test helper file (e.g. test_helper.js), we will add our save_screenshot_after_test_failed method.

// test_helper.js
// imports
var path = require('path');
async function save_screenshot_after_test_failed(page, currentTest, testFileName) {
if (currentTest.state != "passed") {
console.log("Trying to take a screenshot of " + currentTest.fullTitle());
var screenshot_file_dir = __dirname + '/reports/screenshots/' + testFileName.replace(".js", ".xml");
var screenhost_file_name = currentTest.fullTitle() + ".png";
var screenshot_file_path = screenshot_file_dir + "/" + screenhost_file_name;
await page.screenshot({ path: screenshot_file_path });
// Module Exports
module.exports.save_screenshot_after_test_failed = save_screenshot_after_test_failed;

For our helper method, we will pass in page , currentTest (this.currentTest) and the test’s filename, testFileName. The method content remains the same.

You will need to export the function to be used.

In your test file, your afterEach block will look like the following:

// imports
var helper = require('../test_helper');
afterEach(async function() {
var testFileName = path.basename(__filename);
await helper.save_screenshot_after_test_failed(page, this.currentTest, testFileName);

Done! 😊



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store