If a Puppeteer visual test started failing after a screenshot resize, first check the rendering contract—not the pixel-diff threshold. Match the baseline’s viewport width and height, deviceScaleFactor, browser and font environment, page state, and capture timing. Save the received image and diff, then fix any dimension or layout mismatch before considering comparator tolerance.
Why resizing can make a visual test flaky
A screenshot baseline is a rendering contract: it represents a page rendered under a particular set of conditions. Changing the screenshot’s dimensions can change more than the output file’s size. It can alter the viewport, device scale, responsive layout, text wrapping, lazy-loaded content, and the moment at which the page is captured. If any of those differ between the baseline run and the test run, the images may differ even when the application code has not.
Puppeteer’s page.setViewport() changes the page viewport. The Puppeteer API documentation recommends setting it before navigation; it also notes that changing it can reload the page in some cases. Resizing halfway through a test can therefore change the page state as well as its geometry. Keep the dimensions consistent from the start unless responsive behavior is specifically what the test is checking.
Separate geometry changes from rendering noise
Begin by comparing the PNG dimensions of the stored baseline and the received screenshot. Different dimensions are a strong sign that the test setup or capture options do not match. If dimensions match, inspect the diff: shifted content, changed line breaks, or broad regions of difference point toward layout, fonts, data, or readiness; small, speckled changes around edges may be rasterization noise. A moving timestamp, banner, ad, or widget is a source of uncontrolled page data.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Reproduce and classify the failure
Keep the baseline, the received screenshot, and the comparator’s diff image as CI artifacts. A failure message alone rarely tells you whether the cause is a size mismatch, a shifted layout, or a few noisy edge pixels.
#1 Best Overall
- Dimensions differ: compare the viewport, device scale factor, full-page setting, and target element or page used for each capture.
- The whole page is shifted or reflowed: check viewport dimensions, scale factor, fonts, and whether the test captured before the intended layout settled.
- Only a small number of edge pixels differ: inspect whether the difference is consistent rasterization noise before changing per-pixel sensitivity or blur.
- A region appears, disappears, or changes between runs: identify dynamic content such as a timestamp, rotating banner, third-party widget, ad, or live data feed.
Do not update the baseline just to make a failing run green. First establish whether the new image reflects an intentional product change or an accidental change in test conditions.
Set the exact viewport before navigation
Use the same width, height, and device scale factor that were used to create the intended baseline. Create the page, set its viewport, and only then navigate:
const page = await browser.newPage();
await page.setViewport({
width: 1280,
height: 720,
deviceScaleFactor: 1,
});
await page.goto(url, { waitUntil: 'networkidle2' });
The example’s dimensions are illustrative; use your test’s deliberate baseline dimensions instead. Device scale factor matters because CSS pixels and screenshot pixels are not interchangeable: changing the factor can change the output raster dimensions and pixel sampling. A viewport that appears visually similar can still produce a different PNG if its scale factor or capture mode differs.
Recommended Free Tools
For a responsive test, treat each viewport and scale factor as a separate rendering contract with its own expected image. Do not resize a page after navigation and compare it to a baseline made at a different size. If the test is specifically about resizing behavior, make that transition an explicit test step and capture the appropriate state for that step.
Wait for the page state that matters
Navigation completion is not always application readiness. Puppeteer’s screenshot guide demonstrates navigation using waitUntil: 'networkidle2' before taking a screenshot. Network idle is useful when the application settles network activity, but it is not proof that fonts, animations, client-side rendering, or polling have finished. Puppeteer’s Page.waitForNetworkIdle() documentation also specifies that it waits at least the configured idle time.
Rank #2
Prefer a meaningful application-ready signal, then wait for fonts. For example:
await page.waitForSelector('[data-test="page-ready"]');
await page.evaluate(async () => {
if (document.fonts?.ready) await document.fonts.ready;
});
Choose a selector that signals the state the screenshot actually needs—for example, a test hook rendered after the main content is ready. Network idle can follow or complement that signal where it makes sense for the app. Avoid using a fixed sleep as the only readiness condition: the same delay can be unnecessarily long on one run and too short on another.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsMake motion and data deterministic
Once size and readiness are controlled, remove sources of intentional change that are irrelevant to the visual assertion. Inject a test stylesheet to disable CSS animations and transitions and suppress blinking carets. Where your application permits it, stub the clock, random values, and network responses so the same data appears on each run. Block or mock third-party content when it is not part of what the test is meant to verify.
For a region that must not affect the comparison, hide or replace it while preserving its layout footprint. The jest-image-snapshot README includes a Puppeteer example that removes .banner nodes, and warns that removing an element can change layout. If the banner takes up space, removal may move everything below it; visibility: hidden or a fixed-size placeholder can avoid that reflow. Use masking only for genuinely dynamic regions, not to conceal a layout regression.
Apply the same preparation to baseline generation and comparison runs. A baseline captured with animations disabled or a mocked clock is not a fair comparison to a run using live motion or current time.
Use the same capture target and options
Capture the same thing in both runs: a viewport screenshot should be compared with a viewport screenshot, and a full-page screenshot with a full-page screenshot. Keep screenshot options explicit in the test so that a later change to defaults or a helper does not silently alter the contract.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Rank #3
Puppeteer supports page and element screenshots. Its screenshot guide notes that taking an element screenshot scrolls the element into view if it is hidden. That behavior can matter when sticky headers, lazy loading, or scroll-triggered effects are present. If the test targets an element, ensure that both baseline and received captures use the same selector and that the element is in the intended state before capture.
A repeatable Jest and Puppeteer pattern
The following is a minimal pattern for a Jest test using Puppeteer and jest-image-snapshot. Install and configure those packages in the project first; provide a URL reachable by the test runner and a stable ready selector in the app. Keep your existing baseline workflow and browser installation consistent across local and CI runs.
const { toMatchImageSnapshot } = require('jest-image-snapshot');
expect.extend({ toMatchImageSnapshot });
test('product page matches its visual baseline', async () => {
const page = await browser.newPage();
await page.setViewport({
width: 1280,
height: 720,
deviceScaleFactor: 1,
});
await page.goto(process.env.TEST_URL, {
waitUntil: 'networkidle2',
});
await page.waitForSelector('[data-test="page-ready"]');
await page.evaluate(async () => {
if (document.fonts?.ready) await document.fonts.ready;
});
await page.addStyleTag({
content: `
*, *::before, *::after {
animation-duration: 0s !important;
animation-delay: 0s !important;
transition-duration: 0s !important;
caret-color: transparent !important;
}
`,
});
const image = await page.screenshot({
type: 'png',
fullPage: false,
});
expect(image).toMatchImageSnapshot();
await page.close();
});
Adapt the order of injected styles and readiness checks if your app’s ready state depends on them. For example, if disabling motion changes when an element becomes visible, prepare the page before waiting for the relevant state. Close pages in a finally block in a larger suite so a failed assertion does not leave resources open.
Choose comparator tolerance only after fixing setup
jest-image-snapshot compares a received PNG buffer with a stored baseline. Its documented options include pixelmatch or SSIM, per-pixel sensitivity, whole-image failure thresholds, blur, diff output, and allowSizeMismatch. These controls address different kinds of variation; they should not be used interchangeably.
| Need | Approach | Use it when |
|---|---|---|
| Exact pixel comparison | pixelmatch with strict per-pixel settings | Small pixel-level changes matter to the test. |
| Ignore proven scale-related edge noise | A small Gaussian blur; the matcher README describes radius 1–2 as a usual range | Diff inspection shows edge noise rather than layout or content changes. |
| Compare structural similarity | SSIM with an explicit failure threshold | The requirement is perceptual structure rather than exact pixel identity. |
| Permit different image dimensions | allowSizeMismatch |
The test deliberately compares captures of different dimensions. |
Keep tolerance as strict as the product’s rendering variability allows. Start by reviewing the diff image, then make the smallest justified adjustment. A whole-image threshold can hide a meaningful local defect; a per-pixel setting has a different scope. allowSizeMismatch is not a general fix for flaky captures: if the test expects the same viewport, unequal dimensions are a setup error to resolve first.
Handle retries and baseline updates deliberately
The jest-image-snapshot README notes that browser screenshot tests can produce false positives and documents Jest’s jest.retryTimes() for retries. When using retries, it requires a unique customSnapshotIdentifier. A retry that passes once does not establish that the image is deterministic; it may simply have sampled a different moment or a transiently favorable render.
Use retries to reduce the impact of occasional browser noise only after stabilizing the test. Keep the images from failed attempts where possible, and do not automatically bless a passing retry as a new baseline. Update a snapshot after reviewing the diff and confirming the viewport, fonts, data, browser environment, and intended application changes.
Keep CI rendering consistent
To make screenshots comparable across CI runs, pin the relevant environment rather than relying on whatever happens to be installed on a worker. Use the same browser build, operating-system image, font packages, locale, timezone, viewport, scale factor, and app fixtures for baseline creation and comparison. If a browser or font change is intentional, treat it as a baseline migration and review the resulting image differences.
- Use the same capture helper and explicit screenshot options in local and CI runs.
- Keep fonts available before capture and avoid relying on fallback fonts during startup.
- Mock unstable APIs and third-party responses when they are not under test.
- Preserve received images and diffs as CI artifacts to make failures diagnosable.
- Separate visual tests by viewport when checking responsive states instead of changing size unpredictably within one capture.
Visual comparisons add browser startup, page loading, rendering, and image comparison work to a test suite. Waiting for a real ready condition is usually more useful than adding a long global sleep: it avoids delaying every run while still making the capture dependent on application state. Network idle can also delay tests on pages with persistent connections or polling, so use it only when it reflects the page’s behavior.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Troubleshooting common failures
“Image dimensions do not match”
Compare baseline and received PNG width and height, then verify setViewport(), deviceScaleFactor, full-page mode, and target selection. Set the viewport before goto(). Do not enable allowSizeMismatch unless different sizes are an intentional part of the assertion.
Text wraps differently even though the viewport looks right
Check the actual viewport values and device scale factor, then confirm the same font files loaded before capture. Wait for document.fonts.ready and compare browser and operating-system environments between baseline and CI. A fallback font can have different glyph widths and change wrapping.
Best Value
The page sometimes captures an empty or partly rendered region
Wait for the application’s ready selector and required data state. Use network idle as an additional signal only if the application can become idle. A screenshot taken after navigation does not necessarily mean a client-rendered page has finished producing its main content.
Only banners, timestamps, or widgets differ
Stub the relevant data or hide the unstable region while preserving its geometry. If the changing region is a third-party widget, block or mock it if the test is not intended to cover it. Avoid removing a space-consuming node if doing so shifts the rest of the page.
Many tiny differences remain around edges
First rule out a different font, browser build, scale factor, or capture size. If the remaining differences are demonstrably rasterization noise, inspect the matcher’s blur or per-pixel settings and choose the smallest tolerance that addresses that noise. Do not raise the whole-image failure threshold as a shortcut.
Or skip the browser setup
If your goal is to capture a URL rather than control a local Puppeteer test session, ScreenshotNeo can return a screenshot or PDF with one GET request. This does not replace Puppeteer when a test needs application-specific setup, browser interaction, or precise control of a CI environment. The API accepts screenshot parameters, and its docs are at ScreenshotNeo’s API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, with the response identifying the page verdict and billing status in headers. An MCP server exposes screenshot and page-information tools to AI agents. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Sign up for ScreenshotNeo’s free plan to try 1,000 screenshots a month without a card.
Frequently Asked Questions
Can I compare screenshots captured on different operating systems?
You can, but font rendering and available fonts may differ by environment. For pixel-sensitive baselines, create and compare images in the same pinned CI environment; otherwise treat the environment change as a deliberate baseline migration.
Does a successful retry mean I should update the snapshot?
No. A passing retry can reflect a transient render rather than a corrected test. Review the failed and passing images and identify the source of variation before accepting a new baseline.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Free tools Windows power users keep installed
One-click scans. No signup required.




