Reputation: 573
I am very new to playwright and i have a problem. I am trying to implement comparing two screenshots (before and after) in one test. this is what i want to achieve:
something like this:
test('compare screenshots', async ({ page }) => {
await page.goto('my website here');
const beforeImage = await page.screenshot({
path: `./screenshots/before.png`
})
//
// some state changes implemented here
//
const afterImage = await page.screenshot({
path: `./screenshots/after.png`
})
expect(beforeImage).toMatchSnapshot(afterImage)
});
but it does not work like this. Any ideas/suggestions how can i achieve this? Help would be greatly appreciated
Upvotes: 5
Views: 8757
Reputation: 890
Another potential solution is to create a fixture that manages the file comparison, as inspired by @d2vid's answer.
The idea is to expose (via a Playwright fixture) a function to create a screenshot, and a function to compare them.
If the test succeeds, the images are deleted.
It would look something like this:
export const compareSnapshotsFixture: TFixture<'compareSnapshots'> = async (
{ page },
use,
testConfig,
) => {
const testName = testConfig.title
// Unique -- in case we rerun the test
const now = ts()
// Different folders for each test
const imageFolder = CONFIG.paths.inTestSnapshot(testName)
// Generate image names on the fly
const imageName = (prefix: string = '') =>
`${imageFolder}/${now}---${prefix}.png` as const
// import from `playwright-core/lib/utils`
const compare = getComparator('img/png')
// The test that uses this fixture
await use({
compare,
page,
snapImg: async (opts = {}) => {
await page.mouse.move(0, 0)
await page.waitForTimeout(300) // wait for any async/animations
return page.screenshot({ ...opts, path: imageName(opts.prefix) })
},
expectDifferent: (buffer1, buffer2, minDiffPixels, message) =>
expect(
compare(buffer1, buffer2, { maxDiffPixels: minDiffPixels }),
message,
).not.toBeNull(),
expectSimilar: (buffer1, buffer2, maxDiffPixels, message) =>
expect(compare(buffer1, buffer2, { maxDiffPixels }), message).toBeNull(),
})
// //////////////
if (testConfig.status === 'passed') {
await test.step('[fixture] Remove screenshots (test succeeded)', async () => {
await fs.rm(imageFolder, { recursive: true, force: true })
})
} else {
await test.step('[fixture] Not removing screenshots (test did not succeed)', () => {
expect(1).toEqual(1)
})
}
}
This exposes snapImg
, expectDifferent(img1, img2, 100)
and expectSimilar
.
In my test I can use it like this:
test('Test before and after', async ({ compareSnapshots }) => {
const { expectDifferent, expectSimilar, snapImg, page } = compareSnapshots
const one = await snapImg({ prefix: 'before' })
// DO STUFF
// page.locator(...).click()
const two = await snapImg({ prefix: 'after' })
expectDifferent(one, two, 400)
})
Upvotes: 0
Reputation: 2322
The problem with Playwright's toHaveScreenshot
and toMatchSnapshot
is that they're a bit over-engineered and will only compare a current screenshot to a screenshot from a previous test run. If you want to compare two screenshots that you have as Buffers in memory, you can use the getComparator
method that Playwright uses behind the scenes:
import { getComparator } from 'playwright-core/lib/utils';
await page.goto('my website here');
const beforeImage = await page.screenshot({
path: `./screenshots/before.png`
});
//
// some state changes implemented here
//
const afterImage = await page.screenshot({
path: `./screenshots/after.png`
});
const comparator = getComparator('image/png');
expect(comparator(beforeImage, afterImage)).toBeNull();
The advantage of using getComparator
is that it fuzzy matches, and you can set the threshold of how many pixels are allowed to be different. If you just want to check that the PNGs are exactly identical, a dead simple method to check for equality between the two screenshots is:
expect(Buffer.compare(beforeImage, afterImage)).toEqual(0)
Beware though - this simpler method is flakey and sensitive to a single pixel difference in rendering (such as if any animations/transitions are not completed or if there are differences in anti-aliasing).
Upvotes: 4
Reputation: 917
You can do something like this:
test('compare screenshots', async ({ page }, testInfo)=>{
await page.goto(pageUrl);
const screenshotTarget = page.locator(scTarget);
await expect(screenshotTarget).toHaveScreenshot( `${testInfo.title}.png`);
//
// some state changes implemented here
//
await expect(screenshotTarget).toHaveScreenshot( `${testInfo.title}.png`);
});
I prefer to use the test titel for naming my screenshots but it should also work if you just enter the same name twice. Then if you run your tests without --update-snapshots
they should fail if some visual changes happened.
Upvotes: 3