Search code examples
javascriptjestjspuppeteerjest-image-snapshot

how to ignore a region or element within the body tag when using jest-image-snapshot to compare image shots?


I walked through several issues inside the jest-image-shot and puppeteer repos. Yet, I have not able to find a proper solution for this. Basically, what I want to do is, to allow jest to run its toMatchImageSnapshot with ignore certain elements or regions within the executed page of puppeteer chrome instance.

Any pointer or existing solution for this within the jest-image-shot itself, or removing the element during execution of puppeteer is the only way to work around it?


Solution

  • No, what you want is not easily possible like that. The library jest-image-snapshot uses the library pixelmatch to compare two images. That means it actually compares the pixels of the image without executing any logic on the document itself.

    You have three options:

    1. Use failureThreshold to define how much can differ
    2. Remove the element from the page before taking the screenshot (as you already proposed)
    3. Remove/Black the element in the image

    Option 1: Use failureThreshold

    The library has an option called failureThreshold built-in, which allows to define a threshold when comparing the images. This threshold is for the whole image and not specific to elements or parts of the image. Depending on your use case, it might be useful if the part of the page you want to ignore is small and other minor changes might be tolerable.

    Code sample

    it('...', async () => {
        // ...
        expect(image).toMatchImageSnapshot({
            failureThreshold: '0.01',
            failureThresholdType: 'percent'
        });
    });
    

    Option 2: Remove the element from the page

    This would be the easiest way to do it. Remove the element that you want to ignore. If the remaining page does not completely change by this, this might be the easiest and best way.

    Code sample

    it('...', async () => {
        const page = await browser.newPage();
        await page.goto('...');
        await page.execute(() => {
            // remove element from the DOM
            const el = document.querySelector('#element-selector');
            el.parentElement.removeChild(el);
        });
        const image = await page.screenshot();
    
        expect(image).toMatchImageSnapshot();
    });
    

    Option 3: Remove/Black the element in the image

    If option 2 is not viable for some reason, you could also manipulate the image itself. You first, check the position of the element inside the page and its height and weight and then black the part of the image at that position.

    To get the position and size of the element you can use the following code:

    const [top, left, weight, height] = await page.execute(() => {
        const el = document.querySelector('#element-selector');
        const rect = element.getBoundingClientRect();
        return [rect.top, rect.left, rect.width, rect.height];
    });
    

    After that you could use a library like lwip or jimp to directly manipulate the image and paint that part of the image black (or pixelate) it before comparing them.