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 🙏

© 2025 – Pkg Stats / Ryan Hefner

deviance

v0.3.0

Published

Extends NightwatchJS with a reporting tool and the ability to do visual regression assertions

Readme

Deviance

:warning: Not ready for production use. Deviance is currently in active development and as such is likely to change. Use at your own peril.

Nightwatch.js reporting tool with extended commands and assertions to perform visual regressions.

Installation

Using npm:

$ npm i --save-dev deviance

Usage

Settings

Nightwatch.js will need to be told to load in the command and assertion provided by Deviance. To do this, add the following lines to your Nightwatch.js configuration file(s):

custom_commands_path: [
    'node_modules/deviance/dist/commands',
],
custom_assertions_path: [
    'node_modules/deviance/dist/assertions',
],

Note that these properties accept an array and can be used with other commands and assertions. They should sit at the same depth as src_folders within the configuration file. You can read more about configuration settings in the Nightwatch.js configuration documentation.

Deviance will default to the following settings:

const defaultSettings = {
    reporting: {
        enabled: false,
        port: 8083,
    },
    regression: {
        expectedPath: 'tests_output/deviance/regression/expected',
        actualPath: 'tests_output/deviance/regression/actual',
        screenshotsPath: '',
        threshold: 0.05,
        screenshotTimeout: 20000,
    },
};

You can modify these settings from within your globals file, as is defined by globals_path within your Nightwatch.js configuration file.

const Deviance = require('deviance');

/*
 * Deviance can be constructed with settings to fit your requirements.
 * You can override just a single setting, some or all of them.
 */
const deviance = new Deviance({
    reporting: {
        enabled: true,
    },
    regression: {
        expectedPath: 'your/regression/expected/path',
        actualPath: 'your/regression/actual/path',
        screenshotsPath: 'your/nightwatch/screenshots/path',
        threshold: 0.15,
    },
});

/*
 * Then provide Nightwatch.js with the Deviance settings as a global property
 * and optionally define the reporter.
 */
module.exports = {
    deviance: deviance.settings,
    reporter: deviance.reporter,
};

In the example above, the Deviance reporter is optional, but for Deviance to function settings are mandatory.

You can optionally override the enabled setting for the report, by setting a node environment variable when running Nightwatch.js:

OPEN_REPORT=false nightwatch

Command

Deviance provides a single command captureElementScreenshot, typically its usage would look like this:

module.exports = {
    'sample capture element image': function (browser) {
        browser
            .url('http://app.host')
            .waitForElementVisible('body')
            .captureElementScreenshot('.selector')
            .end();
    }
};

This will also work with named selectors from page objects e.g. captureElementScreenshot('@selector'). If no parameter is supplied, it will default to the selector body. Any screenshot generated for the body will define it's boundaries from the document scrollWidth and scrollHeight (i.e. the full size of the document).

The command captures the element defined by the selector and saves it to the path defined by the settings. It then checks for an expected image and performs a diff between the two. It performs no assertions or tests, but will raise errors should they occur.

By default the image will be named after the selector passed to it, for example .selector would be named selector.png and img would be called img.png. The path for saving images is derived from the settings defined in the globals file (or the defaults mentioned above) combined with the test group (the directory structure under test suites root) and the test name. This should hopefully prevent collision of filenames for most tests, however if you do find a collision within a test (capturing an element more than once within the same test) then captureElementScreenshot takes an optional second argument:

module.exports = {
    'sample capture element image': function (browser) {
        browser
            .url('http://app.host')
            .waitForElementVisible('body')
            .captureElementScreenshot('.selector', 'selector_1')
            .someOperationOn('.selector')
            .captureElementScreenshot('.selector', 'selector_2')
            .end();
    }
};

The above will now generate images called selector_1.png and selector_2.png.

The command will also take an optional third parameter as a function:

module.exports = {
    'sample capture element image': function (browser) {
        browser
            .url('http://app.host')
            .waitForElementVisible('body')
            .captureElementScreenshot('.selector', '.selector', function(results) {
                // do stuff
            })
            .end();
    }
};

In the above example results is an object with the following structure:

{
    actual: {
        path: 'output/path/of/captured/image',
        width: 0, // image width as Number
        height: 0, // image height as Number
    },
    // the following are only populated if an expected file exists
    expected: {
        path: 'path/of/expected/image',
        width: 0, // image width as Number
        height: 0, // image height as Number
    },
    diff: {
        path: 'path/of/generated/diff/image',
        percent: 0 // difference in image on a scale from 0 to 1
    },
}

Assertion

Deviance provides a single assertion elementRegresses, typically its usage would look like this:

module.exports = {
    'sample assert element regresses': function (browser) {
        browser
            .url('http://app.host')
            .waitForElementVisible('body')
            .verify.elementRegresses('img')
            .assert.elementRegresses('h1');
    },
};

The assertion (like all Nightwatch.js assertions) will work with verify and assert. It will also work with named selectors from page objects e.g. elementRegresses('@selector'). If no parameter is supplied, it will default to the selector body.

Internally the assertion uses the captureElementScreenshot command and performs some analysis on the results provided. Like the command, it can accept an optional second argument to override the filename of the generated image:

module.exports = {
    'sample assert element regresses': function (browser) {
        browser
            .url('http://app.host')
            .waitForElementVisible('body')
            .verify.elementRegresses('div', 'div_1')
            .someActionToElement('div')
            .verify.elementRegresses('div', 'div_2');
    },
};

Please read the command information above for reasoning behind this.

Optionally the assertion accepts a third command in the form of a Number between 0 and 1 which allows you to override the defined threshold in the globals file (or the defaults mentioned above) for this one regression assertion. The threshold is the ratio of difference that is deemed tolerable before the test should fail:

module.exports = {
    'sample assert element regresses': function (browser) {
        browser
            .url('http://app.host')
            .waitForElementVisible('body')
            .verify.elementRegresses('div', 'div', 0.005); // very strict threshold
    },
};

It is advisable to use a very strict threshold for elements where there is a considerably dominant colour. Where possible though we would advise avoiding the use of the regression assertion on elements like this.

Running in Docker

We explored using Deviance within a Docker image and decided to leave some loose guidance on getting set up with Docker.

We configured our base image with Firefox, Google Chrome, Node.js, Yarn and Java. Within the image we created a volume for our application and a basic entrypoint script that would run yarn followed by running Nightwatch.js. As Deviance will generate some files (images for visual regression) we also created a user (node in our case) so any files would not be generated as root.