Visual regression testing with WebdriverIO, Jasmine and Allure

Search for a command to run...

No comments yet. Be the first to comment.
When performing end to end journey test automation on an eccommerce style website, it’s common for security reasons for the credit card fields to be loaded in an iframe to allow payment forms from a payment service provider to added to a website in a...

After 20 years of working with test automation, I like to think I've become quite good at it. Recently, I was advocating for a change in approach at a company I had just joined. I knew it was crucial to clearly explain my method for writing maintaina...

At Glofox, we regularly perform whole-team exploratory testing sessions. These are very effective at discovering problems that are not found when testing around acceptance criteria, as we get multiple different perspectives, and it gives developers t...

Lighthouse has a new user-flow API that allows lab testing at any point within a page's lifespan. There is support for generating a lighthouse report from a puppeteer script, but I wanted to explore using WebdriverIO to do the same! Why would you wan...

Much has been written about flaky tests, the perils of ignoring the signal they may be giving you and how to deal with them from a tools perspective, and here but I've never really read anything about how to stop flaky tests getting added in the firs...

We have recently started using the Visual Regression Testing Service, and I thought I would share how we incorporate it in our test automation.
It's quite straightforward to capture an image, then run the test again and perform an assertion, checking there are no differences between the captured baseline image and the newly captured image, for example:
// check screen
expect(browser.checkScreen('example page')).toEqual(0);
// Check an element
expect(browser.checkElement($('#element-id'), 'example element')).toEqual(0);
Having previously had some experience with writing custom matchers for Jasmine, I thought it would make sense to wrap some of the functionality inside a matcher, for the following reasons:
Firstly, we could set a consistent screen size before we do the comparison
Secondly, by setting the plugin option returnAllCompareData: true, it would allow us to return more information that would be useful when a test fails:
const checkResult = {
// The formatted filename, this depends on the options `formatImageName`
fileName: 'examplePage-chrome-headless-latest-1366x768.png',
folders: {
// The actual folder and the file name
actual: '/Users/wswebcreation/Git/wdio-image-comparison-service/.tmp/actual/desktop_chrome/examplePage-chrome-headless-latest-1366x768.png',
// The baseline folder and the file name
baseline: '/Users/wswebcreation/Git/wdio-image-comparison-service/localBaseline/desktop_chrome/examplePage-chrome-headless-latest-1366x768.png',
// This following folder is optional and only if there is a mismatch
// The folder that holds the diffs and the file name
diff: '/Users/wswebcreation/Git/wdio-image-comparison-service/.tmp/diff/desktop_chrome/examplePage-chrome-headless-latest-1366x768.png',
},
// The mismatch percentage
misMatchPercentage: 2.34
};


Bringing this all together we end up with:
jasmine.addMatchers({
toMatchImageSnapshot: function(util, customEqualityTesters) {
"use strict";
return {
compare: function(actual, expected) {
browser.setWindowSize(1200, 900);
let checkResult;
if(actual.hasOwnProperty('element')) {
actual.element.waitForDisplayed();
checkResult = browser.checkElement(actual.element, actual.tag);
}
else {
checkResult = browser.checkScreen(actual.tag);
}
const pass = checkResult.misMatchPercentage === 0;
if (!pass) {
const actualImage = fs.readFileSync(checkResult.folders.actual);
const expectedImage = fs.readFileSync(checkResult.folders.baseline);
const differenceImage = fs.readFileSync(checkResult.folders.diff);
allure.addLabel('testType', 'screenshotDiff');
allure.addAttachment('diff', differenceImage, 'image/png');
allure.addAttachment('actual', actualImage, 'image/png');
allure.addAttachment('expected', expectedImage, 'image/png');
}
return {
pass,
message: `Expected to have matched image at ${checkResult.folders.actual}, but there was a difference of ${checkResult.misMatchPercentage}%.
Difference can be viewed at ${checkResult.folders.diff}. Original can be viewed at ${checkResult.folders.baseline}`
};
}
};
}
});
This allows us to re-write the example assertions above as:
// check screen
expect({ tag: 'example page' }).toMatchImageSnapshot();
// Check an element
expect({ element: $('#element-id'), tag: 'example element' }).toMatchImageSnapshot();
We plan to add functionality to allow for updating the baseline, rather than having to manually update the baselines.