Snapvisor Docs

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 TrackerSnapvisor 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.
baselineBranchNameThe base branch of the project. See Baseline builds.
ciBuildIdParallel 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.
diffTollerancePercentthreshold. See Tolerances.
os, device, viewport, customTagsThe screenshot name, and the viewports option.
enableSoftAssert, the Cypress error callbackNot 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 frontendApproving 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 documents fullPage as defaulting to false, 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 own mask replaces that default, so include page.locator('[data-visual-test="blackout"]') in your array if you rely on it.
  • Stabilization is on by default: fonts, images and aria-busy regions 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.argosScreenshot accepts Cypress's own cy.screenshot() options.
  • Elements matching [data-visual-test="blackout"] are blacked out by default. Passing your own blackout array replaces that default, so include '[data-visual-test="blackout"]' in it to keep it.
  • Stabilization is on by default; pass stabilize: false to 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:

  1. Add the Snapvisor SDK and calls, and merge them to your base branch so the first Snapvisor build becomes the baseline.
  2. Keep VRT as your gate, and the Snapvisor check optional, while you compare results.
  3. 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.

If the check is optional, nothing blocks a merge. A green test job means the screenshots arrived, not that they are unchanged. With VRT the test itself failed on a difference, so this is a change in where the gate sits. Require the check in branch protection — see GitHub or GitLab.

What you can delete

Once builds are landing in Snapvisor and the check is required:

  1. The @visual-regression-tracker/* dependencies, vrt.json, and the VRT_* variables and CI secrets.
  2. The start, stop and track calls, and the Cypress plugin and env block.
  3. 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.
  4. Any soft-assertion and error-callback code.

First-build checklist

After the first Snapvisor build on your base branch:

  1. Open the build and confirm it has a screenshot for each image VRT tracked.
  2. Look through the screenshots for fonts, images and animations that did not settle. Fix those first, before any threshold change.
  3. Run the same build twice on one commit and check that nothing shows as changed.
  4. Open a pull request with one small visual change. Confirm the Snapvisor status check appears, asks for review, and clears after you approve.
  5. 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.

On this page