americanexpress / jest-image-snapshot

✨ Jest matcher for image comparisons. Most commonly used for visual regression testing.
Apache License 2.0
3.85k stars 200 forks source link

Feature: take in a screenshot function for retries. #268

Closed arturohernandez10 closed 3 years ago

arturohernandez10 commented 3 years ago

It's often hard to account for every single transition to get the page ready for an image snapshot. Since we do have a baseline. It would be useful to have an option to retry after some time has passed. That is a better alternative to chasing all events in the application. There could even be a longer wait time when snapshots are not available to get a good baseline. With puppeteer It would look like this:

expect(page.screenshot).toMatchImageSnapshot({retries:3, firstSnapshotWaitTime:3000});

This would be really handy.

arturohernandez10 commented 3 years ago

I retrofitted this feature. And it worked great!! I am using the snapshot as a kind of polling, I still make use of testing libraries to do most of the synchronization work. But where they fall short is usually not by a lot. For other users here is what I did.

I added this line in index.js. Right before toMatchImageSnapshot returns. And I used patch-package to apply the patch after npm install.

global.imageSnapshotsMr[snapshotIdentifier] = { diffRatio : result.diffRatio };

In my jest test environment I initialize the global object.

  async setup() {
    await super.setup();

    ...

    this.global.imageSnapshotsMr = {};
  }

And then I just call snapshot with a high error tolerance (30%), if it fails I keep comparing. The last comparison it's done at 1%.

                let lastScreenShot = await page.screenshot({path: tempFilename, fullPage: true });
                let j = 0;
                let snapshotIdentifier = ''
                for (j = 0; j < 3; j++) {
                    expect(lastScreenShot).toMatchImageSnapshot({
                        customSnapshotIdentifier: ({testPath, currentTestName}: any) => {
                            failureThreshold: j === 2 ? 0.01, 0.3,
                            snapshotIdentifier = `${path.parse(testPath).name}-${currentTestName}-${i+1}-${transitionName}`
                            return snapshotIdentifier;
                        }
                    });                    
                    const matchResult = global.imageSnapshotsMr[snapshotIdentifier] || {};
                    if ((matchResult.diffRatio || 0) <= 0.01)
                        break;
                    console.log(`Failed comparison attempt ${j+1}, ${(matchResult.diffRatio || 0)}`);
                    fs.unlinkSync(tempFilename);
                    lastScreenShot = await page.screenshot({path: tempFilename, fullPage: true });
                }

This code is raw at this point. For our codebase, all images are compared by a in a function outside of the main test code. So it does not affect the test code itself. I still need to add an initial delay for the case where there is no snapshot. But this has allowed me to remove ugly meaningless waits from the test code.

Please let the maintainers know if you would like support for a similar feature...

github-actions[bot] commented 3 years ago

This issue is stale because it has been open 30 days with no activity.