happo-olivier-target-firefox v5.0.1
Happo Target: Firefox
Installation
If you haven't already installed happo
you will need to do that:
npm install --save-dev happo
To get the Firefox target, you will install from npm:
npm install --save-dev happo-target-firefox
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.
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]');
};
Example configuration
// .happo.js
// =========
var FirefoxTarget = require('happo-target-firefox');
module.exports = {
// ...
targets: [
// ...
new FirefoxTarget({
// an overridable name to identify the target
// (useful for running a specific target from the CLI.)
// (default: 'firefox')
name: 'firefox',
// 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',
],
// 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,
},
},
}),
],
};
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.