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.
This guide moves a Visual Regression Tracker setup (the self-hosted server plus the @visual-regression-tracker/agent-playwright or @visual-regression-tracker/agent-cypress agent) to Snapvisor. You keep your tests and CI. What changes is the capture call, the config, and where baselines and review live.
VRT facts come from the READMEs of the published @visual-regression-tracker/agent-playwright 5.3.1, @visual-regression-tracker/agent-cypress 5.7.0 and @visual-regression-tracker/sdk-js 5.7.1 packages, and from the VRT repository README. Snapvisor facts were read from the published @snapvisor/playwright 8.0.0 and @snapvisor/cypress 8.0.0 packages. Check the versions you run.
The one change that matters
With VRT, you run a server. Your agent sends each screenshot to it, the server compares the image with the baseline for that name, branch, OS, browser, viewport and device, and you accept or reject the result in the VRT frontend. A failed comparison throws in your test unless you enable soft assertions.
Snapvisor is not a server you operate. 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 VRT baselines 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
| Visual Regression Tracker | Snapvisor equivalent |
|---|---|
| VRT backend, frontend and database (Docker) | Nothing to host. See Access tokens. |
vrt.json, the VRT_* environment variables, or visualRegressionTracker in Cypress env (apiUrl, project, apiKey) | A project token in ARGOS_TOKEN, and the SDK registered in your test config. |
branchName (required) | Detected from your CI. No setting. |
baselineBranchName | The base branch of the project. See Baseline builds. |
ciBuildId | Parallel builds with a shared nonce. See Parallel testing. |
vrt.start() / vrt.stop(), cy.vrtStart() / cy.vrtStop() | Not needed. The SDK uploads when the run ends. |
vrt.trackPage(page, name), vrt.trackElementHandle(el, name), cy.vrtTrack(name) | argosScreenshot(page, name) and argosScreenshot(page, name, { element }) for Playwright, cy.argosScreenshot(name) for Cypress. |
ignoreAreas (x, y, width, height) | Masking. See Ignore regions. |
diffTollerancePercent | threshold. See Tolerances. |
os, device, viewport, customTags | The screenshot name, and the viewports option. |
enableSoftAssert, the Cypress error callback | Not needed. A visual difference never fails the test. The status check is the gate. |
retryCount (Playwright), retryLimit (Cypress) | Not needed. Snapvisor does not compare during the test, so there is no "difference found" result to retry. |
| Comparison provider setting (Pixelmatch, Looks-Same, Odiff, VLM) | No such setting. There is one threshold. |
| Accepting a test run in the VRT frontend | Approving in the review UI. |
Playwright
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 change the capture and drop the start and stop calls:
-import {
- PlaywrightVisualRegressionTracker,
-} from "@visual-regression-tracker/agent-playwright";
+import { argosScreenshot } from "@snapvisor/playwright";
-const vrt = new PlaywrightVisualRegressionTracker(browserName, config);
-await vrt.start();
-await vrt.trackPage(page, "Home page");
-await vrt.trackElementHandle(page.locator("#navbar"), "Navbar");
-await vrt.stop();
+await argosScreenshot(page, "Home page");
+await argosScreenshot(page, "Navbar", { element: page.locator("#navbar") });argosScreenshot takes Playwright's own screenshot options, so mask and clip work as in Playwright. Defaults worth knowing, all read from the package source:
- Full page. A capture of a page is full page unless you pass
fullPage: false. The VRT agent documentsfullPageas defaulting tofalse, so full-page captures are a change in what you get. - Animations are disabled unless you pass
animations: "allow". - Masks.
[data-visual-test="blackout"]is masked by default. Passing your ownmaskreplaces that default, so includepage.locator('[data-visual-test="blackout"]')in your array if you rely on it. - Stabilization is on by default: fonts, images and
aria-busyregions settle first, carets and scrollbars are hidden, and font anti-aliasing is normalized.
See SDKs for the full option list.
Cypress
npm install --save-dev @snapvisor/cypress// 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. Without this option the default is to upload.
uploadToArgos: !!process.env.CI,
});
return config;
},
},
}); // cypress/support/e2e.ts
-import { addVrtCommands } from "@visual-regression-tracker/agent-cypress";
-addVrtCommands();
+import "@snapvisor/cypress/support";-cy.vrtStart();
-cy.vrtTrack("Whole page");
-cy.get("#navbar").vrtTrack("Navbar");
-cy.vrtStop();
+cy.argosScreenshot("Whole page");
+cy.get("#navbar").argosScreenshot("Navbar");Remove addVisualRegressionTrackerPlugin(on, config) and the visualRegressionTracker block in env. Defaults, read from the package source:
cy.argosScreenshotaccepts Cypress's owncy.screenshot()options.- Elements matching
[data-visual-test="blackout"]are blacked out by default. Passing your ownblackoutarray replaces that default, so include'[data-visual-test="blackout"]'in it to keep it. - Stabilization is on by default; pass
stabilize: falseto turn it off.
Ignore regions
VRT ignores rectangles by pixel coordinates. Prefer masking the element that moves, which keeps working when the layout shifts. In Playwright, pass locators:
await argosScreenshot(page, "Dashboard", {
mask: [
page.locator('[data-visual-test="blackout"]'),
page.locator(".live-clock"),
],
});In Cypress, pass selectors in blackout, keeping '[data-visual-test="blackout"]' if you rely on the default. If you can only describe an area by coordinates, Playwright's clip option limits the capture to a rectangle.
Tolerances
VRT's diffTollerancePercent is an acceptable difference from the baseline, a number between 0 and 100. Snapvisor has one setting, threshold, a sensitivity between 0 and 1 that defaults to 0.5. Higher means less sensitive. The two scales measure different things, so do not copy a number across. Set it per screenshot:
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 job needed a reachable VRT server and its credentials. The VRT_* variables below apply when the suite relies on the environment or vrt.json; an explicit config object in code overrides them:
- run: npm ci
- run: npx playwright test
env:
VRT_APIURL: ${{ secrets.VRT_APIURL }}
VRT_APIKEY: ${{ secrets.VRT_APIKEY }}
VRT_PROJECT: My project
VRT_BRANCHNAME: ${{ github.head_ref || github.ref_name }}
VRT_CIBUILDID: ${{ github.sha }}After, the job runs your tests with the project token:
- run: npm ci
- run: npx playwright test # captures via argosScreenshot, uploads via the reporter
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 VRT calls and add the Snapvisor capture next to them:
- Add the Snapvisor SDK and calls, and merge them to your base branch so the first Snapvisor build becomes the baseline.
- Keep VRT 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 VRT.
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
@visual-regression-tracker/*dependencies,vrt.json, and theVRT_*variables and CI secrets. - The start, stop and track calls, and the Cypress plugin and
envblock. - The VRT server stack (backend, frontend, database and their volumes). Export or keep what you want from its history first, since Snapvisor cannot import it.
- Any soft-assertion and error-callback code.
First-build checklist
After the first Snapvisor build on your base branch:
- Open the build and confirm it has a screenshot for each image VRT tracked.
- 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 need to run the VRT server?
- No. VRT is a self-hosted stack (a backend, a frontend and a database, started from its Docker setup). Snapvisor does not need that stack. Your test run uploads screenshots with a project token, and baselines, comparison and review live in the Snapvisor app. Keep the VRT stack running until the Snapvisor check is your gate, and keep its data if you want the old history.
- Can I import my VRT 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.
- Where do os, device, viewport and customTags go?
- VRT uses them to separate baselines for the same image name. Snapvisor identifies a screenshot by one name that must be unique within a run, so put whatever distinguishes the variant in the name, such as Checkout - mobile.
- Can I run VRT and Snapvisor side by side while I switch?
- Yes. They are independent. Keep the VRT calls, add the Snapvisor capture calls next to them, and remove VRT once the Snapvisor check is your gate.
Migrate from reg-suit
Move a reg-suit setup to Snapvisor - how regconfig.json, key generators, storage publishers and notifier plugins translate, and how to keep your capture tool (storycap or plain Playwright screenshots) or switch to the Snapvisor 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.