npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@forsakringskassan/cypress-visual-regression

v2.3.2

Published

Module for adding visual regression testing to Cypress

Downloads

3,963

Readme

@forsakringskassan/cypress-visual-regression

Module for adding visual regression testing to Cypress.

This is a fork of cypress-visual-regression v1.7.0. The most notable changes are:

  • Support both headed and headless.
  • Support both E2E and Component Tests with screenshots stored next to test-cases in a __screenshots__ folder.
  • Retryability: tries multiple times to match the screenshot against the base images.
  • Delayed screenshots: optional delay before taking base screenshots.
  • Forced software rendering, antialiasing detection: to prevent flaky tests due to different hardware rendering slightly different pixels.

Getting Started

Install:

$ npm install --save-dev @forsakringskassan/cypress-visual-regression

Add the following config to your cypress.config.ts. file:

the default for screenshotsfolder is cypress/screenshots. Your screenshotfolder is where the failing tests will be transfered to.

{
    "screenshotsFolder": "WHERE_YOU_WANT_FAILING_TEST_IMAGES_TO_END_UP/",
    "trashAssetsBeforeRuns": true
}

cypress/support/commands.ts:

import "@forsakringskassan/cypress-visual-regression/commands";

cypress.config.ts:

import getToMatchScreenshotsPlugin from "@forsakringskassan/cypress-visual-regression/plugin";
import { defineConfig } from "cypress";

export default defineConfig({
    e2e: {
        setupNodeEvents(on, config) {
            config = getToMatchScreenshotsPlugin(on, config);
        },
    },
});

This plug uses the before:browser:launch event, due to Cypress#5240 if you have another plugin or you use it yourself in cypress.config.ts you need a workaround. See the section below on an example of such workaround.

For more info on how to use TypeScript with Cypress, please refer to this document.

Options

failSilently is enabled by default. Add the following config to your cypress.json file to see the errors:

{
    "env": {
        "failSilently": false
    }
}

To Use

The plugin has two modes, base and actual.

base means taking a new screenshot as a baseline while actual means taking a screenshot and then comparing it to the base-image.

Default is actual.

To run in base-mode locally, add type=base as a environment variable

$ cypress run -- --env type=base

Implementation in test files

Add cy.toMatchScreenshot(); in your tests specs whenever you want to test for visual regressions. You can also add an optional error threshold: Value can range from 0.00 (no difference) to 1.00 (every pixel is different). So, if you enter an error threshold of 0.51, the test would fail only if > 51% of pixels are different.

Default value of threshold is 0.01, you can not go lower than that.

More examples:

| Threshold | Fails when | | --------- | ---------- | | .25 | > 25% | | .30 | > 30% | | .50 | > 50% | | .75 | > 75% |

Sample:

it("should display the login page correctly", () => {
    cy.visit("/03.html");
    cy.get("H1").contains("Login");
    cy.toMatchScreenshot(0.1);
});

You can target a single HTML element as well:

cy.get("#my-header").toMatchScreenshot();

You can pass arguments as an object to cy.screenshot(), rather than just an error threshold, as well:

it("should display the login page correctly", () => {
    cy.visit("/03.html");
    cy.toMatchScreenshot({
        capture: "fullPage",
        errorThreshold: 0.1,
    });
});

Waiting for components to load and retry function

Sometimes things don't load for the screenshot. In order to solve this, you can use the argument baseDelay which will cy.wait(x) before taking base screenshots.

cy.toMatchScreenshot({ baseDelay: 500 });

When not running the test as base, it will retake screenshots after 200ms delay each until match or the default of 3 tries. If it doesn't find match and no more retires, test will fail. You can also configure how many retries it will make.

cy.toMatchScreenshot({ retries: 6 });

Saved base images and failing tests

Base-images will be stored inside same directory in which the test is, in a sub-folder called __screenshots__. For example

root
├─┬ cypress
│ └─┬ e2e
│   ├── test.cy.ts
│   └─┬ __screenshots__
│     └── test -- should match screenshot.png
└─┬ src
  └─┬ component
    ├── component.vue
    ├── component.cy.ts
    └─┬ __screenshots__
      └── component -- should match screenshot.png

When a test fails, a sub-directory will be created under your configured screenshotsFolder, inside there will be a copy of the base image and the actual image.

Using multiple event listeners

Cypress does not support multiple listeners on the same event. This plugin uses before:browser:launch and if this collides with another plugin you can apply the following workaround:

cypress.config.ts:

/**
 * Workaround for https://github.com/cypress-io/cypress/issues/5240
 *
 * Cypress `on` cannot accept multiple listeners, since we have multiple plugins
 * trying to hook into the same eents only one of them gets called. This
 * workaround creates a wrapped `on` supporting multiple listeners.
 */
class EventForwarder {
    private emitter: EventEmitter;
    private task: Cypress.Tasks;
    public on: Cypress.PluginEvents;

    public constructor() {
        this.emitter = new EventEmitter();
        this.task = {};
        this.on = (action, arg) => {
            if (action === "task") {
                Object.assign(this.task, arg);
            } else {
                this.emitter.on(action, arg as () => void);
            }
        };
    }

    public forward(on: Cypress.PluginEvents): void {
        for (const event of this.emitter.eventNames()) {
            /* eslint-disable-next-line @typescript-eslint/no-explicit-any -- because we cannot extract the action names as a union of strings */
            on(event as any, async (...args: unknown[]) => {
                if (event === "before:browser:launch") {
                    const browser = args[0];
                    let launchOptions = args[1];
                    for (const listener of this.emitter.listeners(event)) {
                        launchOptions = await listener(browser, launchOptions);
                    }
                } else {
                    for (const listener of this.emitter.listeners(event)) {
                        await listener(...args);
                    }
                }
            });
        }
        on("task", this.task);
    }
}

export default defineConfig({
    component: {
        setupNodeEvents(cypressOn, config) {
            const eventForwarder = new EventForwarder();
            const on = eventForwarder.on;

            try {
                /* [..] */

                getToMatchScreenshotsPlugin(on, config);

                /* [..] */
            } finally {
                eventForwarder.forward(cypressOn);
            }
        },
    },
});