Visual Testing Using pixelmatch and Playwright with CucumberJS
Scenario
I was recently working on testing a feature with Playwright Node.js library which required image comparison. Playwright already have many inbuilt functions for screenshot testing, like expect(locator).toHaveScreenshot()
. But here’s the catch, they have clearly mentioned in their documentation that toHaveScreenshot()
works only with Playwright test runner. Since I am using CucumberJS, so it did not make any sense to use this function.
Solution
So, here’s an alternative approach I came up with to validate the image comparison with CucumberJS and Playwright.
- Take the base screenshot image of element
In order to perform the image comparison, we will first require a base image (expected image) to compare with the actual screenshot. So, I used screenshot()
to get the screenshot of the target element and stored it in my test data folder.
await elementLocator.screenshot({ path: './test-data/expected-image.png' });
This image will be my base image and I shall keep this unchanged until there’s an update on the UI.
2. Take the screenshot of actual element under test
Now that we have got the base image of our element under test, we can now go ahead to capture the screenshot of actual element during test run. This can be done using location.screenshot()
, this method returns Promise<Buffer>
. We don’t need to save the actual image file as we are interested in getting the Buffer of actual image.
async takeLocatorScreenshot(dataTestId: string): Promise<Buffer> {
const screenshot = await page.getByTestId(dataTestId).screenshot();
return screenshot
}
3. Use pixelmatch
library to compare the expected and actual image.
Now, let’s use pixelmatch library to compare the expected and actual screenshot. It is a JS library for pixel-level image comparison.
We will also require pngjs library to decode/encode PNG.
Here’s the code snippet where we have created a function which returns the pixel difference of two images in number.
async getPixelDiff(expectedImagePath: string, dataTestId: string): Promise<number> {
const actualImage = PNG.sync.read(await this.takeLocatorScreenshot(dataTestId))
const expectedImage = PNG.sync.read(fs.readFileSync(expectedImagePath))
const { width, height } = actualImage
const diff = new PNG({ height, width })
return pixelmatch(actualImage.data, expectedImage.data, diff.data, width, height)
}
If two images are exact identical, then the getPixelDiff()
will return 0, or else any other number based on the pixel difference.
Conclusion
In case you have a requirement of image comparison tests with CucumberJS runner, then the above discussed libraries can come to your rescue. With Playwright test runner, it would be a straight forward implementation using await expect(locator).toHaveScreenshot();
, for more detailed documentation, click here.
Shiv Jirwankar
LinkedIn