npm version Build Status

Table of Contents

About haunterjs

When you work on a big website or web application, you (and maybe a bunch of people) are constantly introducing changes in the visual components.
Sometimes this changes are intentional, as you are improving the look and feel, or adding new features, but sometimes this changes are introduced by mistake.
Imagine you are refactoring some component, for example a button, but you are not sure how this change is gonna impact the site. You would need to browse the whole site to make sure everything looks good.
With css regression tests you can automate the process, and make sure none breaks the styles across the website.
There are some tools like PhantomCSS or wraith which allow you to write tests to compare screenshots of your website and detect changes.

haunterjs was built on top of PhantomCSS, adding some new features:

  1. Annotations for each screenshot
  2. Sense of ordered steps in the execution of the test
  3. More simplicity in the syntax for the tests
  4. A viewer to compare and choose the correct screenshots

haunterjs masks some complex features of casperjs, allowing to code simple tests, with a more compact and readable syntax.

This example test opens github.com and searches for 'bootstrap'

var haunter = require('../haunter.js');
haunter.start('search/bootstrap', 'Search for bootstrap repo');
haunter.snap('.header', 'Go to github.com');
haunter.sendKeys('input[type=text]', 'bootstrap');
haunter.snap('.header', 'Type bootstrap on the search field');
haunter.snap('.sort-bar', 'Press enter and view number of results');

Taking snaps

haunterjs is based on the concept of snaps.

A snap is a screenshot of a given css selector and an annotation for that screenshot, describing the current action being performed.

haunter.snap('.sort-bar', 'Press enter and view number of results');

Responsive sites

haunterjs allows to configure multiple viewports, to test responsive sites in a single test.
This way haunterjs will take screenshots with every screen size in each snap.

config.defaultViewports = [
        name: 'small',
        width: 320,
        height: 480
        name: 'medium',
        width: 768,
        height: 480
        name: 'large',
        width: 1024,
        height: 768

If you want to take a snap of a single component, like a button, and you don't care about multiple screen sizes, call the snap method with the last parameter with false value.

haunter.snap('.selector', 'Annotation', false);

Snap viewer

haunterjs includes a snap viewer written in nodejs and angularjs. You can use this tool as a navigation manual, browsing the snapshots of a specific user flow. You can compare them and choose the correct one, in the event of conflict.

snap viewer

haunterjs allows the test to be structured in a hierarchy or folder system, to facilitate the process of browsing/running the tests. For example you can separate your tests by features or modules of your website.

This virtual path is setup in each test:

haunter.start('search/bootstrap', 'Search for bootstrap repo');

Solving conflicts

When a conflict it's found, the viewer asks you which one is the correct version of that component.

snap viewer

So you can update the baseline screenshot, or fix possible visual inconsistencies.

snap viewer

After solving the conflicts, you must commit the changes in your repo.

API Documentation

haunter.start(hierarchy, description)

Initialize components needed to run the test
hierarchy {String} A virtual path to organize the test
description {String} Description of the test


Sets viewport dimensions for the test. Setup the viewport after calling the method start()
viewports {Array} Array of desired viewports. Each viewport has name, width and height)


Sets user agent for the test
ua {String} User Agent string


Navigate to that url
url {String} Destination url

haunter.snap(cssSelector, annotation, multiple)

Take a screenshot with an annotation
cssSelector {String} CSS selector of the element to capture
annotation {String} Comment for the screnshot multiple {boolean} Take screenshot in all the viewports?

haunter.snapExcluding(cssSelector, excludeSelector, annotation, multiple)

Take a screenshot excluding an element
cssSelector {String} CSS selector of the element to capture
excludeSelector {String} CSS selector of the element to exclude
annotation {String} Comment for the screnshot multiple {boolean} Take screenshot in all the viewports?


Click an element
cssSelector {String} CSS selector of the element to click

haunter.sendKeys(cssSelector, keys)

Type some text into an element
cssSelector {String} CSS selector of the element to send keys to
keys {String} String of text to input in the element


Press enter key while focused on an element
cssSelector {String} CSS selector of the element to focus


Place the mouse over some element
cssSelector {String} CSS selector of the element to mouseover

haunter.uploadFile(cssSelector, filePath)

Upload a file to the browser
cssSelector {String} CSS selector for the file input filePath {String} Path to the file to upload

haunter.evaluate(actions, params)

Evaluate some js code on the browser Parameters
actions {function} JavaScript code to evaluate params {Object} Params for the js evaluation


Wait some time in ms. Masks casperjs wait Parameters
miliseconds {Number} Time in miliseconds to wait


Proceed to compare the screenshots and figure out if there are errors


haunterjs is available as a node package

npm install haunterjs

Start by setting up your configuration on the config.js file. Or just leave all the values by default. Make sure the viewerPort configured in the config.js file matches the one in www/js/app.js

Run the demo tests with:

casperjs test demo-tests

Run the snap viewer with:

npm start

And then open http://localhost:[viewerPort] in your browser

Best practices

Organize tests

Using a tidy hierarchization, you can sort them by features, or modules within your webapp.

Take a look at PhantomCSS best practices

Which I will summarize here: