Migrate from Cypress image snapshot plugins
Move a Cypress suite from cypress-image-snapshot or cypress-visual-regression to Snapvisor - how cy.matchImageSnapshot and cy.compareSnapshot, committed snapshot folders, thresholds and update flags translate to cy.argosScreenshot.
This guide moves a Cypress suite that uses cy.matchImageSnapshot() (from cypress-image-snapshot or its maintained fork @simonsmith/cypress-image-snapshot) or cy.compareSnapshot() (from cypress-visual-regression) to Snapvisor. You keep your tests and your CI. What changes is the capture command, and where baselines and review live.
It was written against @snapvisor/cypress 8.0.0, @simonsmith/cypress-image-snapshot 11.0.0, cypress-image-snapshot 4.0.1 and cypress-visual-regression 6.0.1. Option names and flags differ between plugin versions, so check the README of the version you run.
The one change that matters
Both plugins compare a screenshot with a baseline image kept in your repository, and a failed comparison fails the test. cypress-image-snapshot stores snapshots under cypress/snapshots by default, and the diff images next to them. cypress-visual-regression keeps base, actual and diff folders, and you choose between creating the base images and comparing against them with its visualRegressionType setting (base or regression).
Snapvisor stores baselines server-side, per reference branch. cy.argosScreenshot captures the screenshot, your pipeline uploads it, and Snapvisor compares it with the approved screenshot for the branch. A changed screenshot does not fail the test. You approve it in the review UI, and it becomes the new baseline for that branch. See Baseline builds.
Your committed snapshots 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
| Cypress plugins | Snapvisor equivalent |
|---|---|
cy.matchImageSnapshot("name") or cy.compareSnapshot("name") | cy.argosScreenshot("name") from @snapvisor/cypress. The name is required and must be unique within a run. |
cy.get(".card").matchImageSnapshot() | cy.get(".card").argosScreenshot("card") |
Snapshot folders in git (cypress/snapshots, or base and diff folders) | Nothing in your repository. Baselines live on the server. |
customSnapshotsDir, customDiffDir, visualRegressionBaseDirectory, visualRegressionDiffDirectory | Not needed. There are no baseline or diff files to place. |
--env updateSnapshots=true (older versions) or --expose updateSnapshots=true (current @simonsmith version) | Approve the change in the review UI. See Review workflow. |
A base run to create baselines and a regression run to compare (visualRegressionType) | One kind of run. The first build on your base branch is the baseline; later builds are compared with it. |
failureThreshold, failureThresholdType, errorThreshold | threshold. See Tolerances. |
capture: "viewport" or "fullPage" | The same Cypress capture option. Snapvisor captures the full page unless you pass capture: "viewport". |
blackout: [".selector"] | The same Cypress blackout option. Passing blackout replaces the default [data-visual-test="blackout"]; include it in your array to keep it. See Masks and animations. |
failOnSnapshotDiff=false, failSilently | Already the behavior. A changed screenshot does not fail the test. |
requireSnapshots=true | Not needed. A build with no baseline yet is simply the first one. |
| A failed assertion in the Cypress report | A change flagged in the Snapvisor review and on the pull request status check. |
Swap the command
Install the SDK:
npm install --save-dev @snapvisor/cypressRegister the task in your Cypress config, which uploads the screenshots when the run ends:
// cypress.config.ts
import { defineConfig } from "cypress";
import { registerArgosTask } from "@snapvisor/cypress/task";
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
registerArgosTask(on, config, {
// Upload only on CI. The default is to upload.
uploadToArgos: !!process.env.CI,
});
return config;
},
},
});Import the command in your support file, in place of the plugin's command registration:
// cypress/support/e2e.ts
-import { addMatchImageSnapshotCommand } from "@simonsmith/cypress-image-snapshot/command";
-addMatchImageSnapshotCommand({ failureThreshold: 0.2 });
+import "@snapvisor/cypress/support";Then change the calls in your specs:
// cypress/e2e/login.cy.ts
describe("Login", () => {
it("is publicly accessible", () => {
cy.visit("/login");
- cy.matchImageSnapshot("login", { blackout: [".timestamp"] });
+ cy.argosScreenshot("login", { blackout: [".timestamp"] });
});
});Remove the plugin's own setupNodeEvents registration (addMatchImageSnapshotPlugin(on, config) in cypress-image-snapshot 4.0.1, addMatchImageSnapshotPlugin(on) in the @simonsmith fork, or configureVisualRegression(on)) in the same change. Keep Cypress's other assertions; only the screenshot call changes.
cy.argosScreenshot takes Cypress's own cy.screenshot() options, so capture, blackout, clip and disableTimersAndAnimations work as they do in Cypress. It also stabilizes the page before capturing. It waits for web fonts, images and aria-busy regions to settle, hides carets and scrollbars and normalizes font anti-aliasing. It is on by default; pass stabilize: false to turn it off. See Flaky test detection for how recurring changes are handled.
To capture several sizes in one call, pass viewports:
cy.argosScreenshot("home", {
viewports: ["iphone-6", { width: 1280, height: 800 }],
});Tolerances
cypress-image-snapshot takes failureThreshold together with failureThresholdType, which is either a pixel count or a percent of the image. cypress-visual-regression takes errorThreshold, a limit on how much of the image may differ. Each is an allowance for how much of the image may differ before the test fails.
Snapvisor has one setting, threshold, a sensitivity between 0 and 1 that defaults to 0.5. Higher means less sensitive. It is a different kind of setting from the plugin options above, so do not copy a number across:
cy.argosScreenshot("chart", { threshold: 0.7 });Start from the default and adjust only when a specific screenshot is noisy. If you raised a failure threshold to absorb font or anti-aliasing differences between machines, remove it and try the default first. A change is flagged for review, not failed, so a small difference is a decision for a reviewer and not a red build.
Masks and animations
Full page. Cypress captures the full page by default, and cy.argosScreenshot does the same. For a viewport-sized capture, pass capture: "viewport":
cy.argosScreenshot("home", { capture: "viewport" });Animations. cy.argosScreenshot leaves Cypress's own disableTimersAndAnimations setting alone, and Cypress disables timers and CSS animations during screenshots by default. Pass disableTimersAndAnimations: false to let them run.
Blackout. By default, elements matching [data-visual-test="blackout"] are blacked out. Passing your own blackout replaces that default, so include [data-visual-test="blackout"] in your array if you rely on it:
cy.argosScreenshot("dashboard", {
blackout: [
'[data-visual-test="blackout"]',
".clock",
"[data-testid=avatar]",
],
});A blacked-out area is covered in the screenshot, so changes inside it are not compared. Use it for clocks, ads and random data rather than raising the threshold.
Delete the snapshot files
Once builds are landing in Snapvisor and the check is required:
- Delete the snapshot folders from the repository, and from Git LFS if you stored them there:
cypress/snapshotsand its__diff_output__folders, or thebase,actualanddifffolders, or whatevercustomSnapshotsDirand thevisualRegression*Directorysettings point to. - Remove the
.gitignorerules that only existed for diff or actual images. - Remove
--env updateSnapshots=true,--expose updateSnapshots=true,requireSnapshots,failOnSnapshotDiff,visualRegressionType,visualRegressionUpdateSnapshotsandvisualRegressionGenerateDifffrompackage.jsonscripts, docs and CI. Accepting a change now happens in the review UI, and no step rewrites files and commits them. - Remove the plugin's registration from
cypress.configand the support file, and uninstall the plugin. - Remove any CI job that only existed to regenerate baselines on a reference machine or container, and the base-creation run if you used
cypress-visual-regression.
Your pull requests stop carrying binary image changes. Reviewers see the before and after in the review UI instead.
The CI change
Before, the job ran the tests, and a changed screenshot failed the job:
- run: npm ci
- run: npx cypress runAfter, the job also has the project token. A changed screenshot no longer fails the job:
- run: npm ci
- run: npx cypress run # captures via cy.argosScreenshot, uploads when the run ends
env:
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}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 the plugin calls and add cy.argosScreenshot next to them:
cy.matchImageSnapshot("login");
cy.argosScreenshot("login");A way to run the overlap:
- Add the Snapvisor task, the support import and the calls, and merge them to your base branch so the first Snapvisor build becomes the baseline. Both commands can be registered at once.
- Keep the plugin assertions 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 the plugin calls and snapshot files.
Make the Snapvisor check the gate
The upload step 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.
First-build checklist
After the first Snapvisor build on your base branch:
- Open the build and confirm it has a screenshot for each snapshot call you replaced.
- 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 have to stop using Cypress?
- No. Snapvisor captures through your Cypress tests. Cypress stays your test runner, and you keep your specs, commands and config.
- Can I import my committed snapshot images 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.
- Does cy.argosScreenshot fail the test when the screenshot changes?
- No. The test passes once the screenshot is captured. 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. Make that check required in branch protection.
- Can I run the snapshot plugin and Snapvisor side by side while I switch?
- Yes. They are independent. Keep your existing matchImageSnapshot or compareSnapshot calls, add cy.argosScreenshot calls next to them, and remove the plugin once the Snapvisor check is your gate.
Migrate from BackstopJS
Move a BackstopJS suite to Snapvisor - how backstop.json scenarios, viewports, selectors, hidden elements, mismatch thresholds, reference images and backstop approve translate.
Migrate from Loki
Move Storybook visual tests from Loki to Snapvisor - how loki test, update and approve, the .loki reference folder, chromeSelector, configurations, tolerance and skipped stories translate.