Migrate from jest-image-snapshot
Move a jest-image-snapshot setup to Snapvisor - how the toMatchImageSnapshot matcher, the __image_snapshots__ and __diff_output__ folders, failureThreshold and snapshot updates translate to the Puppeteer SDK, the Playwright SDK or a CLI upload.
This guide moves a jest-image-snapshot setup (the toMatchImageSnapshot matcher from jest-image-snapshot, usually with Jest and Puppeteer or Playwright) to Snapvisor. You keep your pages and your test runner. What changes is the capture call, where baselines live, and how a visual difference is reviewed.
jest-image-snapshot facts come from the README of the jest-image-snapshot 6.5.2 package and its source. The Puppeteer path was run with Jest 29, Puppeteer 24.43.1 and the published @snapvisor/puppeteer 7.0.0. The CLI options come from @snapvisor/cli 7.0.0 and the Playwright setup from the SDKs page. Check the versions you run.
The one change that matters
With jest-image-snapshot, the first run of toMatchImageSnapshot() writes a baseline PNG into a __image_snapshots__ folder next to your test. Later runs compare the new image with that file in the same test process, and a difference fails the test. On failure the matcher writes a diff image, by default into a __diff_output__ folder. You update baselines by running Jest with --updateSnapshot or -u and committing the new files.
Snapvisor does not compare inside your test. The capture call stabilizes the page and takes the picture, your pipeline uploads the images with a project token, and Snapvisor compares them with the approved screenshots for the branch. Your test does not fail on a visual difference. The gate is the commit status check on the pull request, which asks for review until someone approves or rejects. See Baseline builds and Review workflow.
Your __image_snapshots__ files do not carry over, and there is no import path. Your first Snapvisor build on your base branch becomes the baseline. Plan the switch for a moment when your base branch is visually correct.
Concept mapping
| jest-image-snapshot | Snapvisor equivalent |
|---|---|
expect(image).toMatchImageSnapshot() | argosScreenshot(page, name). See Puppeteer and Playwright. |
expect.extend({ toMatchImageSnapshot }) | Not needed. There is no matcher to register. |
configureToMatchImageSnapshot({ ... }) shared defaults | Not needed. Set threshold per screenshot, or with --threshold on the CLI. |
__image_snapshots__ (baselines in your repository) | Baselines stored by Snapvisor. Nothing to commit. |
__diff_output__ (diff images on failure) | The build in the app, which shows each changed screenshot against its baseline. |
customSnapshotsDir, customDiffDir, customReceivedDir, customReceivedPostfix, storeReceivedOnFailure | Not needed. Nothing is stored in your repository. |
customSnapshotIdentifier, and the name Jest derives from the test file and test name | The screenshot name, the second argument of argosScreenshot. It must be unique within a run. |
failureThreshold and failureThresholdType (pixel or percent, default 0) | Snapvisor has no per-test option for this. See Tolerances. |
customDiffConfig.threshold (per-pixel sensitivity, default 0.01) | threshold. See Tolerances. |
comparisonMethod (pixelmatch or ssim) | Snapvisor has no per-test option for this. The only comparison setting is threshold. |
blur | Snapvisor has no per-test option for this. |
allowSizeMismatch | Snapvisor has no per-test option for this. |
diffDirection, onlyDiff, noColors, dumpDiffToConsole, dumpInlineDiffToConsole | Not needed. The comparison and the diff view are in the app. |
runInProcess, maxChildProcessBufferSizeInBytes, runtimeHooksPath | Not needed. Snapvisor does not run the diff in your test process. |
updatePassedSnapshot, jest -u | Approving in the review UI. |
jest.retryTimes() with a unique customSnapshotIdentifier | Not needed. Snapvisor does not compare during the test, so there is no "difference found" failure to retry. |
outdated-snapshot-reporter and JEST_IMAGE_SNAPSHOT_TRACK_OBSOLETE | Not needed. There are no stale files to prune from your repository. |
| An image-upload reporter for failed diffs (the README's S3 recipe) | Not needed. Screenshots and diffs are in the build. |
Choose a path
Puppeteer. Jest with Puppeteer is the most common jest-image-snapshot setup, and @snapvisor/puppeteer replaces the page.screenshot() call. Your tests stay in Jest, with one Node flag. See Puppeteer.
Playwright. The documented Snapvisor path is the @playwright/test runner with the Snapvisor reporter. If your Jest tests drive Playwright, move them to @playwright/test. See Playwright.
Anything else that writes images. Snapvisor accepts any folder of PNG or JPEG files. Keep your capture code, write the files, and upload the folder with the CLI.
Puppeteer
Install the SDK next to the CLI, which uploads the screenshots:
npm install --save-dev @snapvisor/puppeteer @snapvisor/cliThen change the capture. The matcher and the expect.extend call go away:
-const { toMatchImageSnapshot } = require("jest-image-snapshot");
-expect.extend({ toMatchImageSnapshot });
+const { argosScreenshot } = require("@snapvisor/puppeteer");
it("renders correctly", async () => {
const page = await browser.newPage();
await page.goto("http://localhost:3000");
- const image = await page.screenshot();
- expect(image).toMatchImageSnapshot();
+ await argosScreenshot(page, "home page");
});Run Jest with --experimental-vm-modules. The CommonJS build of @snapvisor/puppeteer loads its code with a dynamic import(), and without the flag Jest stops with A dynamic import callback was invoked without --experimental-vm-modules:
NODE_OPTIONS=--experimental-vm-modules npx jestThis was run with Jest 29, Puppeteer 24.43.1 and @snapvisor/puppeteer 7.0.0, against a data: URL. The test passed and wrote screenshots/argos/home.png and screenshots/argos/home.png.argos.json in the project directory. The screenshot name home became the file name home.png. Puppeteer 25.14.0 failed under Jest 29 with SyntaxError: Unexpected token 'export', because that release ships as an ES module, so use Puppeteer 24 or a Jest setup that loads ES modules.
The call only saves the files. Upload that folder at the end of your CI job, with the project token in ARGOS_TOKEN:
npx @snapvisor/cli upload ./screenshots/argosThe .argos.json file next to each PNG holds the page URL, viewport and browser details of the capture. Leave it in the folder. The other argosScreenshot options are in SDKs.
Playwright
The documented Playwright path is the @playwright/test runner with the Snapvisor reporter. If your Jest tests drive Playwright, move them to @playwright/test and drop the matcher. There is no documented way to register the reporter inside Jest. For moving existing Playwright snapshot tests, see Migrate from Playwright snapshots. Install the SDK and register the reporter, which uploads screenshots when the run finishes:
npm install --save-dev @snapvisor/playwright// playwright.config.ts
import { defineConfig } from "@playwright/test";
import { createArgosReporterOptions } from "@snapvisor/playwright/reporter";
export default defineConfig({
reporter: [
process.env.CI ? ["dot"] : ["list"],
[
"@snapvisor/playwright/reporter",
createArgosReporterOptions({ uploadToArgos: !!process.env.CI }),
],
],
});Then capture in your test (this is the example from SDKs):
// tests/example.spec.ts
import { test } from "@playwright/test";
import { argosScreenshot } from "@snapvisor/playwright";
test("screenshot homepage", async ({ page }) => {
await page.goto("http://localhost:3000");
await argosScreenshot(page, "homepage");
});See SDKs for the options.
Keep your capture and upload the folder
If your tests already write images to disk, you can leave them alone and upload the folder. Install the CLI:
npm install --save-dev @snapvisor/clinpx @snapvisor/cli upload ./screenshotsWhat the upload command does, read from npx @snapvisor/cli upload --help:
- It takes one
<directory>argument. - It uploads files matching
**/*.{png,jpg,jpeg}by default. Use--filesto choose other globs and--ignoreto leave files out. - The token comes from
--token, or from theARGOS_TOKENenvironment variable. --build-namenames the build when one CI job creates several.--parallel,--parallel-total,--parallel-indexand--parallel-noncesplit a run across machines. See Parallel testing.--reference-branchand--reference-commitoverride the baseline Snapvisor would resolve for you.--thresholdsets the sensitivity. See Tolerances.
A screenshot's name is its path relative to the uploaded directory, so files in different folders never collide. If you rename or move files between runs, Snapvisor sees a different screenshot. The upload sends every matching file in the folder, so write only the images you want reviewed.
Ignore regions
jest-image-snapshot has no ignore option. Its README suggests removing or hiding the element in the page with Puppeteer before the screenshot. That works the same way with Snapvisor, because the page is already hidden or changed when argosScreenshot runs:
await page.evaluate(() => {
document.querySelectorAll(".banner").forEach((el) => el.remove());
});
await argosScreenshot(page, "home page");Setting visibility: hidden instead keeps the space, so the layout does not shift. For the Snapvisor-side options (argosCSS and mask), see Hide, remove and mask.
Tolerances
jest-image-snapshot has two thresholds. customDiffConfig.threshold is the per-pixel sensitivity of the comparison, and failureThreshold with failureThresholdType is how much of the whole image may differ, in pixels or in percent, before the test fails. Both decide whether a test fails.
Snapvisor has one setting, threshold, a sensitivity between 0 and 1 that defaults to 0.5. Higher means less sensitive. Snapvisor has no per-test option that limits the number or percentage of changed pixels, so failureThreshold has no equivalent, and the scales are different, so do not copy a number across. Set it per screenshot, or with --threshold on the CLI:
await argosScreenshot(page, "chart", { threshold: 0.7 });Start from the default and adjust only when a specific screenshot is noisy. A change is flagged for review, not failed, so a small difference is a decision for a reviewer and not a red build.
The CI change
Before, the Jest run was the whole check, and the baselines came from your repository:
- run: npm ci
- run: npx jestAfter, the job runs your tests, then uploads the screenshots with the project token:
- run: npm ci
- run: npx jest # captures via argosScreenshot, writes ./screenshots/argos
env:
NODE_OPTIONS: --experimental-vm-modules
- run: npx @snapvisor/cli upload ./screenshots/argos
env:
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}With the Playwright test runner, the reporter uploads for you, so the job is npx playwright test with ARGOS_TOKEN set.
Copy the project token from your project's settings in the app, and store it as a CI secret, never in the repository. See Access tokens. On GitHub Actions you can drop the secret with tokenless authentication. More workflows, including merge queues and fork pull requests, are in GitHub Actions recipes.
Run both side by side
You do not have to switch in one pull request. Keep toMatchImageSnapshot() and add the Snapvisor capture next to it:
- Add the Snapvisor capture and upload, and merge them to your base branch so the first Snapvisor build becomes the baseline.
- Keep jest-image-snapshot as your gate, and the Snapvisor check optional, while you compare results.
- When the Snapvisor builds match your expectations on a few pull requests, make the Snapvisor check required, then remove jest-image-snapshot.
Make the Snapvisor check the gate
The upload finishes once the build has been created, whether or not it contains changes. A failed upload (bad token, unresolvable commit, network) is an error. The review gate is the commit status check Snapvisor posts to the pull request, which asks for review until someone approves or rejects the changes.
What you can delete
Once builds are landing in Snapvisor and the check is required:
- The
jest-image-snapshotdependency, theexpect.extendorconfigureToMatchImageSnapshotsetup, and any@types/jest-image-snapshotpackage. - The
__image_snapshots__and__diff_output__folders, and their entries in.gitignoreor CI artifact steps. - The
outdated-snapshot-reporterentry in your Jestreporters, and theJEST_IMAGE_SNAPSHOT_TRACK_OBSOLETEvariable. - Any reporter that uploads diff images, and the
jest -ustep in your contributor docs. - Any
jest.retryTimes()call that existed only for screenshot tests.
First-build checklist
After the first Snapvisor build on your base branch:
- Open the build and confirm it has a screenshot for each snapshot jest-image-snapshot compared.
- Look through the screenshots for fonts, images and animations that did not settle. Fix those first, before any threshold change.
- Run the same build twice on one commit and check that nothing shows as changed.
- Open a pull request with one small visual change. Confirm the Snapvisor status check appears, asks for review, and clears after you approve.
- Make the Snapvisor check required in branch protection.
Frequently asked questions
- Do I still commit __image_snapshots__?
- No. jest-image-snapshot keeps its baselines as PNG files in your repository. Snapvisor keeps baselines for you, per reference branch, so there is nothing to commit and nothing to review as a binary diff in git. Delete the folder once your base branch has a Snapvisor baseline.
- Can I import my existing snapshots as baselines?
- No. There is no import path. Your first Snapvisor build on your base branch becomes the new baseline: it has nothing to compare against, so it is approved automatically. Run it when your base branch is visually correct.
- What replaces jest -u?
- Approving the change in the review UI. Running Jest with --updateSnapshot (or -u) rewrites the files in __image_snapshots__. With Snapvisor you do not rewrite anything in your repository. You approve the changed screenshots in the app, and they become the new baseline for that branch.
- Can I run jest-image-snapshot and Snapvisor side by side while I switch?
- Yes. They are independent. Keep the matcher, add a Snapvisor capture next to it, and remove jest-image-snapshot once the Snapvisor check is your gate.
Migrate from Visual Regression Tracker
Move a Visual Regression Tracker (VRT) setup to Snapvisor - how the self-hosted server, vrt.json config, vrtTrack and trackPage calls, ignore areas, tolerance and baseline branches translate to the Playwright and Cypress SDKs.
Baseline builds
The baseline is the last approved build on your base branch. Learn how Snapvisor picks the reference build and resolves the base branch for every comparison.