node package manager
Easy sharing. Manage teams and permissions with one click. Create a free org »

happo

Happo Version Badge

Build Status dependency status dev dependency status License Downloads

npm badge

Happo is a command-line tool to visually diff JavaScript components. Read more.

Installation

Happo comes bundled as an npm package. To install it, run

npm install -g happo

You'll also need Firefox installed on the machine. Happo uses selenium-webdriver under the hood, and will support the same version of Firefox as Selenium supports. Happo currently works best with Firefox > 50. It uses geckodriver to control Firefox.

Introduction

Happo works by running twice. It first needs to run on the "current" version of the code (generally latest master) to take screenshots of the current version of your components. Then, it runs on the "next" version of the code (generally your working branch) to take new screenshots of your components and compare them to the earlier version.

To set this up, you define a set of examples that Happo will grab snapshots for. If a previous snapshot exists for a component, Happo will diff the new snapshot with the previous. If a diff is found, a visual representation of the changes will be constructed. You can then use that diff image to decide whether a visual regression has been introduced or not, and take appropriate action based on that information.

Demo of Happo in action

Defining examples

You define your examples in a JavaScript file and include it in the sourceFiles configuration option.

Here's an example of a button component being added to a Happo suite:

happo.define('button', () => {
  var elem = document.createElement('button');
  elem.setAttribute('class', 'button');
  elem.innerHTML = 'Submit';
  document.body.appendChild(elem);
});

Here's an example using React):

happo.define('<MyReactComponent>', () => {
  const div = document.createElement('div');
  document.body.appendChild(div);
  const component = (
    <MyReactComponent
      foo={1}
      bar='baz'
    />
  );
  ReactDOM.render(component, div);
});

Examples are responsible for rendering the element into the DOM. This is because a lot of frameworks (e.g. React) like to maintain control over the DOM. A helper method to reduce some of the boilerplate is probably a good idea in your project.

Setting viewport sizes

By default, Happo renders examples in a 1024px-wide window. If you have components that render differently depending on available screen size you can use the viewports option in the object passed in as the second argument to happo.define. These need to correspond to configured viewports in the .happo.js file. Happo comes pre-configured with three default sizes: large (1024x768), medium (640x888), and small (320x444).

happo.define('responsive component', () => {
  var elem = document.createElement('div');
  elem.setAttribute('class', 'responsive-component');
  document.body.appendChild(elem);
}, { viewports: ['large', 'small'] });

Async examples

If your examples need to do something asynchronous before they finish rendering, you can return a Promise from your define method. Happo will wait for the Promise to resolve before taking a screenshot.

happo.define('async component', () => {
  return new Promise(function(resolve) {
    var elem = document.createElement('div');
    document.body.appendChild(elem);
    setTimeout(function() {
      elem.innerHTML = 'Async content loaded';
      resolve();
    }, 100);
  });
});

Alternatively, use the done callback passed in to the define method.

happo.define('async component', (done) => {
  var elem = document.createElement('div');
  document.body.appendChild(elem);
  setTimeout(() => {
    elem.innerHTML = 'Async content loaded';
    done();
  }, 100);
});

Focusing on examples

During development, you might want to focus on a single example. In those situations, you can use the happo.fdefine function instead of happo.define. Using fdefine will cause happo to only run for the examples that are using fdefine and skip all examples using define.

// This example will be skipped because it is not being "focused". 
happo.define('button', () => {
  var elem = document.createElement('button');
  elem.setAttribute('class', 'button');
  elem.innerHTML = 'Submit';
  document.body.appendChild(elem);
});
 
// This example will not be skipped because it is being "focused". 
happo.fdefine('different button', () => {
  var elem = document.createElement('button');
  elem.setAttribute('class', 'button button--send');
  elem.innerHTML = 'Send';
  document.body.appendChild(elem);
});

Cleaning up the DOM

Happo will clean up the DOM between rendered examples. If you need more control over the clean-up process you can override happo.cleanOutElement with your own implementation. This is useful if you need to clean up event listeners for instance, or if you use React and need to unmount components.

happo.cleanOutElement = function(element) {
  React.unmountComponentAtNode(element);
};

Controlling root nodes

By default, Happo will compute a bounding rectangle used when snapshotting based on the dimensions of all root DOM nodes found in the <body> element and their descendant nodes. You can override this default by implementing a happo.getRootNodes function. If you use React you might want to use this to better control the size of the snapshot.

happo.getRootNodes = function() {
  return document.querySelectorAll('[data-reactroot]');
};

Configuration

Happo loads configuration in one of the following ways:

  • From a JavaScript file specified via a HAPPO_CONFIG_FILE environment variable
  • From .happo.js in the current working directory

Example configuration

module.exports = {
  // Control the interface on which the local server listens (defaults to 'localhost') 
  // (default: 'localhost') 
  bind: '0.0.0.0',
 
  // Control the port used for the local server 
  // (default: 4567) 
  port: 7777,
 
  // List javascript source files. These can be files or raw URLs. 
  // (default: []) 
  sourceFiles: [
    'https://unpkg.com/jquery@3.1.1',
    'application.js',
    'happo-examples.js',
  ],
 
  // List css source files. These can also be files or raw URLs. 
  // (default: []) 
  stylesheets: [
    'application.css',
  ],
 
  // List directories where public files are accessible (useful for e.g. font files) 
  // (default: []) 
  publicDirectories: [
    'public',
  ],
 
  // Specify the folder where snapshots are saved 
  // (default: 'snapshots') 
  snapshotsFolder: 'happo-snapshots',
 
  // Configure the window size when taking snapshots 
  // (defaults shown below) 
  viewports: {
    large: {
      width: 1024,
      height: 768,
    },
    medium: {
      width: 640,
      height: 888,
    },
    small: {
      width: 320,
      height: 444,
    },
  },
};

Command line tools

happo run

This command will fire up a Firefox instance and take snapshots of all your Happo examples.

happo review

Once happo run has finished, run happo review from the command line. This will open a page that compares the latest run's snapshots against the previous snapshots.

happo debug

If you want to debug rendering your examples, you can run happo debug. This will open a browser window pointing at /debug, listing all your examples. If you click one of them, the example will be rendered in isolation and you can do use your developer tools to debug.

happo upload [<triggeredByUrl>]

Uploads all current diff images to an Amazon S3 account and reports back URLs to access those diff images. Requires that S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, and S3_BUCKET_NAME are specified as environment variables. S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY will be the credentials Happo uses to access the bucket named S3_BUCKET_NAME.

S3_BUCKET_PATH can be set as an environment variable to specify a directory path for where you want diff images uploaded within the S3 bucket.

Furthermore, S3_REGION controls what region is used to find or create the bucket.

You can set these in the session by using export:

export S3_ACCESS_KEY_ID=<YOUR_ACCESS_KEY_VALUE>
export S3_SECRET_ACCESS_KEY=<YOUR_SECRET_ACCESS_KEY_VALUE>
export S3_BUCKET_NAME=<YOUR_BUCKET_NAME>
 
happo upload

or by adding them in the beginning of the command:

S3_ACCESS_KEY_ID=<...> S3_SECRET_ACCESS_KEY=<...> ... happo upload

If you want the diff page to link back to a commit/PR, you can pass in a URL as the argument to happo upload. E.g.

happo upload "https://test.example"

To debug uploading, override the uploader configuration option with a debug-enabled S3Uploader instance. This will print additional information to stderr.

const S3Uploader = require('happo/lib/server/S3Uploader');
 
module.exports = {
  uploader: () => new S3Uploader({ debug: true });
}

happo upload-test

Uploads a small text file to an AWS S3 account. This is useful if you want to test your S3 configuration. Uses the same configuration as happo upload does.

happo upload-test

Running in a CI environment

The main purpose for Happo is for it to be run in a CI (Continuous Integration) environment. The command line tools provided are designed to be used as building blocks in a script that you can run in Travis, Jenkins and other Continuous Integration environments.

Below is an example of how you can use Happo to test if a commit introduces any visual change.

  1. Check out the commit previous to the one to test (e.g. git checkout HEAD^)
  2. (optionally) build your JavaScript and/or CSS
  3. Run happo run to generate previous snapshots
  4. Check out the commit to test
  5. (optionally) build your JavaScript and/or CSS
  6. Run happo run to diff against previously created snapshots
  7. Run happo upload to upload diffs to a publicly accessible location

There's an example script implementing these steps located in happo_example.sh. Use that as a starting point for your own CI script.

Headless Happo

Since Happo uses Firefox to generate its snapshots, you need a display. If you are on a build server, you usually don't have a screen. To run happo then, you can use a virtual display server such as xvfb. The example CI script as well as the internal Travis test run for Happo uses xvfb-run in order to obtain a virtual display.

In the wild

Organizations and projects using Happo.