Snapvisor Docs

Migrate from Happo

Move a Happo suite to Snapvisor - how happo.config targets, the happo wrapper, happoScreenshot with component and variant, Storybook parameters and report approval translate to the Playwright, Cypress and Storybook SDKs.

This guide moves a Happo setup (the happo package with its Playwright, Cypress or Storybook integration) to Snapvisor. You keep your tests, stories and CI. What changes is the capture call, the wrapper around your test command, and where baselines and review live. It follows the structure of the other migration guides, so the Storybook specifics link to Migrate from Loki and Migrate from Chromatic.

Happo facts come from the public Happo docs at docs.happo.io (the Playwright, Cypress, Storybook and getting-started pages). Snapvisor facts were read from the published @snapvisor/playwright 8.0.0, @snapvisor/cypress 8.0.0 and @snapvisor/storybook 7.0.0 packages. Happo's docs have changed between releases, so check the version you run.

The one change that matters

With Happo, your test run sends what Happo needs to render (for Storybook, a built Storybook package) to Happo, and Happo's workers take the screenshots. A report is compared with the baseline report from your main branch, and you accept or reject the diffs in Happo.

Snapvisor takes a real screenshot in the browser your own test run is already using. The capture call stabilizes the page and takes the picture, your pipeline uploads the images, and Snapvisor compares them with the approved screenshots for the branch. There is no separate rendering step, so what you see is what your browser drew. You approve changes in the review UI, and they become the new baseline for that branch. See Baseline builds.

Your Happo reports 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

HappoSnapvisor equivalent
happo.config.ts (or .js) with apiKey, apiSecret, targets and an integrationNo config file for capture. A project token in ARGOS_TOKEN, and the SDK registered in your test config. See Access tokens.
npx happo -- playwright test, npx happo -- cypress runYour normal test command. The SDK uploads when the run ends.
happoScreenshot(locator, { component, variant }) (Playwright, Cypress)argosScreenshot(page, "Component - variant", { element: locator }) for Playwright, cy.argosScreenshot("Component - variant") for Cypress.
component and variantOne screenshot name, unique within a run.
targets (a named browser and viewport)The viewports option, plus Playwright projects for more browsers. See Targets.
Storybook integration (type: 'storybook')@snapvisor/storybook. See Storybook.
snapshotStrategy: 'clip' or the defaultNot needed. The screenshot is taken in the page. Pass element (Playwright) or chain from the element (Cypress) to capture one element.
Parallel runs with --nonce and npx happo finalizeParallel builds with a shared nonce, finalized as one build. See Parallel testing.
GitHub app that posts the result to the pull requestThe Snapvisor status check. See GitHub or GitLab.
Accepting or rejecting diffs in a reportApproving in the review UI. See Review workflow.

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:

 // tests/hero.spec.ts
-import { test } from "happo/playwright";
+import { test } from "@playwright/test";
+import { argosScreenshot } from "@snapvisor/playwright";

-test("start page", async ({ page, happoScreenshot }) => {
+test("start page", async ({ page }) => {
   await page.goto("http://localhost:7676");
   const heroImage = page.locator(".hero-image");
-  await happoScreenshot(heroImage, { component: "Hero Image", variant: "default" });
+  await argosScreenshot(page, "Hero Image - default", { element: heroImage });
 });

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. A capture of an element is the element.
  • 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.

Drop the happo wrapper from your test command (npx happo -- playwright test becomes npx playwright test). 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 "happo/cypress";
+import "@snapvisor/cypress/support";
-cy.get(".header").happoScreenshot({ component: "Header", variant: "large" });
+cy.get(".header").argosScreenshot("Header - large");

Remove the Happo task from cypress.config and stop wrapping cypress run with the happo command. Defaults, read from the package source:

  • cy.argosScreenshot accepts Cypress's own cy.screenshot() options, so Cypress's own capture and disableTimersAndAnimations defaults apply.
  • 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.

Storybook

Happo builds your Storybook and renders every story on its workers. With Snapvisor, your own Storybook test run renders the stories in a real browser and captures one screenshot per story. The setup, for both the Vitest addon and the Test Runner, is in the Storybook section of the Chromatic guide. Register the plugin like this:

// vitest.config.ts
import { defineConfig } from "vitest/config";
import { argosVitestPlugin } from "@snapvisor/storybook/vitest-plugin";

export default defineConfig({
  plugins: [
    argosVitestPlugin({
      // Set to true to upload; nothing is uploaded when this is omitted.
      uploadToArgos: process.env.CI === "true",
    }),
  ],
});

How Happo's story parameters translate:

Happo parameters.happoSnapvisor
themes: ['light', 'dark']Modes, one screenshot per mode. See Viewports and modes.
targets on a storyModes and viewports, as above.
delay, waitFor, waitForContentWait in the story's play function. Snapvisor already waits for fonts and images to settle.
beforeScreenshot, afterScreenshotDo the DOM work in the story's play function. The plugin also has a beforeScreenshot option, but in @snapvisor/storybook 7.0.0 it receives only { runStabilization }, with no page or DOM access.
happo: falseSnapvisor screenshots every story your test run visits. To leave a story out, keep it out of the test run.

In Storybook captures the aria-busy wait is off by default. Passing your own stabilize object replaces that default, so include waitForAriaBusy: false in it to keep the wait off.

Targets

A Happo target is a named browser and viewport, such as chrome at 1024x768. Snapvisor captures in the browser your test run uses, so targets become:

  • Viewports. Pass viewports to capture one screenshot per size from a single call:
await argosScreenshot(page, "Hero Image - default", {
  viewports: ["iphone-6", { width: 1280, height: 800 }],
});
  • Browsers. Add Playwright projects (or run Cypress in more than one browser). Each project runs the whole suite, so more projects means more screenshots and a longer run.

Tolerances

Happo's comparison settings do not carry over. Snapvisor has one setting, threshold, a sensitivity between 0 and 1 that defaults to 0.5. Higher means less sensitive. Set it per screenshot, or for all stories on the Storybook plugin:

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 wrapped the test command with the Happo CLI and passed Happo credentials:

- run: npm ci
- run: npx happo -- playwright test
  env:
    HAPPO_API_KEY: ${{ secrets.HAPPO_API_KEY }}
    HAPPO_API_SECRET: ${{ secrets.HAPPO_API_SECRET }}

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 happo wrapper and happoScreenshot 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 Happo 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 Happo.

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. 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. happo.config.ts (or .happo.js) and the happo dependency. If you are still on Happo's older separate packages (happo.io, happo-e2e, happo-playwright, happo-cypress, happo-plugin-storybook), remove those too.
  2. The happo wrapper in test scripts and CI, and the Happo task and import in your Cypress config and support file.
  3. The Happo API key and secret from CI secrets.
  4. parameters.happo entries in stories.
  5. The Happo GitHub app, once the Snapvisor status check is required.

First-build checklist

After the first Snapvisor build on your base branch:

  1. Open the build and confirm it has a screenshot for each component and variant you replaced.
  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 have to stop using Playwright, Cypress or Storybook?
No. Snapvisor captures through the test run you already have. Your specs, fixtures and stories stay, and only the screenshot call and the wrapper around the run change.
Can I import my Happo reports 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.
Where do component and variant go?
Into the screenshot name. Happo identifies a screenshot by component and variant. Snapvisor identifies it by one name that must be unique within a run, such as Hero Image - default.
Can I run Happo and Snapvisor side by side while I switch?
Yes. They are independent. Keep the happo wrapper and happoScreenshot calls, add the Snapvisor capture calls next to them, and remove Happo once the Snapvisor check is your gate.

On this page