Configuration
Happo looks for configuration in a .happo.js
file in your current working
directory. You can override this path using the --config
CLI option or the
HAPPO_CONFIG_FILE
environment variable. The config file doesn't undergo babel
transpilation, so use CommonJS syntax unless you're on the latest Node version.
The configuration file can export either an object containing configuration
options or an (async) function that resolves to configuration options.
apiKey
and apiSecret
These tokens authenticate you with happo.io. Never store these tokens in plain text. Use environment variables instead.
module.exports = {
apiKey: process.env.HAPPO_API_KEY,
apiSecret: process.env.HAPPO_API_SECRET,
};
targets
Specify the browsers you want to include in your happo run. For example:
module.exports = {
targets: {
// The first part ('firefox-desktop' in this case) is a name you give
// to the specific browser target. You'll see this name in the reports
// generated during a happo run.
'firefox-desktop': new RemoteBrowserTarget('firefox', {
viewport: '1024x768',
}),
'firefox-mobile': new RemoteBrowserTarget('firefox', {
viewport: '320x640',
}),
chrome: new RemoteBrowserTarget('chrome', {
viewport: '800x600',
}),
edge: new RemoteBrowserTarget('edge', {
viewport: '800x600',
}),
},
};
Viewport sizes can range from 300x300
to 2000x2000
for Chrome and Firefox.
Edge and Safari must be between 400x400
and 1200x1200
. The ios-safari
target runs on an iPhone with a fixed viewport of 375x667
. The ipad-safari
target is always 1080x810
.
Supported browser targets:
firefox
chrome
edge
safari
ios-safari
(runs on iPhone 7)ipad-safari
(runs on iPad)
Target freezeAnimations
By default, Happo freezes CSS animations on the first frame. To freeze
animations on the last frame instead, use the freezeAnimations
option.
module.exports = {
targets: {
ie: new RemoteBrowserTarget('edge', {
viewport: '1024x768',
freezeAnimations: 'last-frame',
}),
},
};
Target chunks
Targets run in parallel by default. To split a specific target into multiple
chunks (running in parallel), use the experimental chunks
option for
RemoteBrowserTarget
:
module.exports = {
targets: {
ie: new RemoteBrowserTarget('chrome', {
viewport: '1024x768',
chunks: 2,
}),
},
};
Happo.io attempts to run chunks in parallel, but there's no guarantee. The
chunks
option adds some overhead, so if your test suite isn't large, using
more than one chunk might actually slow things down.
Target maxHeight
Use maxHeight
to override the default maximum height used by Happo workers
(5000 pixels). This is useful when taking screenshots of tall components or
pages. For example:
module.exports = {
targets: {
chrome: new RemoteBrowserTarget('chrome', {
viewport: '1024x768',
maxHeight: 10000,
}),
},
};
Note: The maximum width defaults to the maximum height, so if you set
maxHeight
, you may also want to set maxWidth
at the same time.
Target maxWidth
Use maxWidth
to override the default maximum width used by Happo workers
(defaults to maxHeight
, which defaults to 5000 pixels). This is useful when
taking screenshots of wide components or pages. For example:
module.exports = {
targets: {
chrome: new RemoteBrowserTarget('chrome', {
viewport: '1024x768',
maxWidth: 10000,
}),
},
};
Target hideBehavior
This option controls how Happo handles elements with the data-happo-hide
attribute. By default, elements with this attribute are made invisible. Use the
value ignore
to make the content appear in screenshots but exclude it from
comparison.
module.exports = {
targets: {
safari: new RemoteBrowserTarget('safari', {
viewport: '1024x768',
hideBehavior: 'ignore',
}),
},
};
Target useFullPageFallbackForTallScreenshots
This option applies to Chrome and Firefox only.
When Chrome and Firefox workers take screenshots of pages taller than 4000
pixels, they apply a workaround that briefly resizes the viewport so all content
fits inside it. Without this workaround, content below the viewport's bottom
edge can disappear inconsistently. However, this workaround can cause other
issues, especially when using the vh
CSS unit. A page with an element of
height: 100vh
will take up the entire screenshot when the viewport-altering
fallback is active. To disable this workaround completely, set
useFullPageFallbackForTallScreenshots: false
.
module.exports = {
targets: {
chrome: new RemoteBrowserTarget('chrome', {
viewport: '1024x768',
useFullPageFallbackForTallScreenshots: false,
}),
firefox: new RemoteBrowserTarget('firefox', {
viewport: '1024x768',
useFullPageFallbackForTallScreenshots: false,
}),
},
};
Target applyPseudoClasses
When set to true
, this option allows you to add data-happo-hover
,
data-happo-focus
, and data-happo-active
attributes to your DOM elements and
have Happo apply the corresponding :hover
, :focus
, or :active
styles. For
example, if you have this markup:
<button>Hover me</button>
<style>
button:hover {
background-color: blue;
}
</style>
To apply the hover style before taking the screenshot (making the button blue), change the markup to:
<button data-happo-hover>Hover me</button>
<style>
button:hover {
background-color: blue;
}
</style>
Similarly, you can add focus to elements using data-happo-focus
:
<input type="text" data-happo-focus />
And add data-happo-active
to elements to simulate the :active
state:
<button data-happo-active>Click me</button>
<style>
button:active {
background-color: red;
}
</style>
Target prefersColorScheme
Set prefersColorScheme: 'dark'
or prefersColorScheme: 'light'
to set the
color scheme preference in the browser. Note: This option has no effect in
iOS Safari.
// .happo.js
module.exports = {
targets: {
chrome: new RemoteBrowserTarget('chrome', {
viewport: '1024x768',
prefersColorScheme: 'dark',
}),
},
};
When enabled, styles affected by the color scheme will be activated:
background: white;
color: black;
@media (prefers-color-scheme: dark) {
background: black;
color: white;
}
Target prefersReducedMotion
Set prefersReducedMotion: true
to make the browser prefer reduced motion when
rendering the UI. Note: This option has no effect in iOS Safari.
// .happo.js
module.exports = {
targets: {
chrome: new RemoteBrowserTarget('chrome', {
viewport: '1024x768',
prefersReducedMotion: true,
}),
},
};
When enabled, media queries that use prefers-reduced-motion: reduce
will be
activated:
@media (prefers-reduced-motion: reduce) {
button {
animation: none;
}
}
Target allowPointerEvents
By default, Happo injects this CSS to prevent spurious hover effects caused by the system mouse pointer:
* {
pointer-events: none !important;
}
If you rely on mouse interaction in your tests (e.g., when using Storybook interactive stories), you might see an error like this in your logs:
Error: Unable to perform pointer interaction as the element has
pointer-events: none
In some cases, this error prevents the variant from being included in the report.
To resolve this, tell Happo to skip injecting the pointer-events: none
CSS
block using the allowPointerEvents
option:
// .happo.js
module.exports = {
targets: {
chrome: new RemoteBrowserTarget('chrome', {
viewport: '1024x768',
allowPointerEvents: true,
}),
},
};
If you're interested in testing hover, focus, and active states with Happo, you
may also want to use the
applyPseudoClasses
option.
project
If you have multiple projects configured for your happo.io account, specify the name of the project you want to associate with. If left empty, the default project will be used.
include
This option only applies when using the Happo Examples integration
Controls which files Happo will extract examples from. The default is
'**/@(*-happo|happo).@(js|jsx)'
. This option is useful if you want to apply a
different naming scheme, such as **/*-examples.js
.
stylesheets
This option only applies when using the Happo Examples integration
If you rely on external stylesheets, list their URLs or absolute file paths in
this config option, such as ['/path/to/file.css', 'http://cdn/style.css']
. If
you're using
conditionally applied stylesheets, use
objects instead of paths:
module.exports = {
stylesheets: [
{ id: 'main', source: '/path/to/main.css' },
{ id: 'secondary', source: '/path/to/conditional.css', conditional: true },
],
};
By default, all stylesheets are applied at render time. If you specify
conditional: true
, only examples that conditionally apply the stylesheet will
receive styles from that stylesheet.
type
This option only applies when using the Happo Examples integration
Either react
(default) or plain
. Determines the strategy Happo will use when
rendering examples. When set to react
, example functions are expected to
return a React component (e.g., export default () => <Foo />
). When set to
plain
, example functions are expected to write directly to document
(e.g.,
export default () => { document.body.appendChild(foo()) }
).
customizeWebpackConfig
This option only applies when using the Happo Examples integration
A function you can use to override or modify the default webpack config used
internally by Happo during a run. Always return the passed config
. For
example:
module.exports = {
customizeWebpackConfig: config => {
config.module.rules.push({
test: /\.css$/,
use: [{ loader: cssLoader }],
});
// It's important to return the modified config
return config;
},
};
In many cases, directly depending on the modules
object of an existing webpack
configuration is sufficient. For instance, this is what you need to get started
with a project using
create-react-app:
const craWebpackConfig = require('react-scripts/config/webpack.config');
module.exports = {
customizeWebpackConfig: config => {
// Use the built-in webpack config provided by create-react-app
config.module = craWebpackConfig('development').module;
return config;
},
};
If you need to perform asynchronous actions to generate a webpack configuration, you can return a promise that resolves with the config. Here's an example using async/await:
module.exports = {
customizeWebpackConfig: async config => {
config.module = await doSomethingAsync();
return config;
},
};
plugins
An array of Happo plugins you want to use. Find available plugins on the Plugins page.
const happoPluginStorybook = require('happo-plugin-storybook');
module.exports = {
plugins: [happoPluginStorybook()],
};
publicFolders
This option only applies when using the Happo Examples integration
An array of absolute paths specifying where public assets are located. Useful if
you have examples that depend on publicly available images (e.g.,
<img src="/foo.png" />
).
const path = require('path');
module.exports = {
publicFolders: [path.resolve(__dirname, 'src/public')],
};
prerender
This option only applies when using the Happo Examples integration
Controls whether examples are pre-rendered in a JSDOM environment (or Chrome if
you're using
happo-plugin-puppeteer). The
default is true
. Set to false
to let your examples render remotely on the
happo.io browser workers instead. This can help resolve certain rendering issues
(e.g., when using shadow DOM). The downside of remote rendering is that errors
are harder to surface.
module.exports = {
prerender: false,
};
pages
An array containing pages you want to screenshot. For example:
module.exports = {
pages: [
{ url: 'https://www.google.com/', title: 'Google' },
{ url: 'https://www.airbnb.com/', title: 'Airbnb' },
],
};
The url
of a page must be publicly accessible, otherwise the Happo browser
workers won't be able to access it.
The title
of a page is used as the "component" identifier in the happo.io UI,
so ensure it's unique for each page.
setupScript
This option only applies when using the Happo Examples integration
An absolute path to a file that will be executed before rendering your
components. This is useful if you want to inject global CSS styling (e.g., a CSS
reset), custom fonts, polyfills, etc. This script is executed in a DOM
environment, so it's safe to inject things into the <head>
.
const path = require('path');
module.exports = {
setupScript: path.resolve(__dirname, 'happoSetup.js'),
};
renderWrapperModule
This option only applies when using the Happo Examples integration
An absolute path to a file exporting a function where you can wrap the rendering of Happo examples. This is useful if you have a theme provider or store provider.
// .happo.js
const path = require('path');
module.exports = {
renderWrapperModule: path.resolve(__dirname, 'happoWrapper.js'),
};
// happoWrapper.js
import React from 'react';
import ThemeProvider from '../ThemeProvider';
export default component => <ThemeProvider>{component}</ThemeProvider>;
rootElementSelector
This option only applies when using the Happo Examples integration
A selector used to find a DOM element that Happo will use as the container. In most cases, leave this empty and let Happo determine the root element automatically. However, in some cases it's useful to override the default behavior and provide a different root. For example, if you have wrapper components that you don't want to be part of the screenshot.
module.exports = {
rootElementSelector: '.react-live-preview',
};
(Example from mineral-ui)
tmpdir
Happo uses webpack internally. By default, bundles are created in the temp
folder provided by the operating system. You can override where bundles are
stored using the tmpdir
configuration option.
module.exports = {
tmpdir: '/some/absolute/path/to/an/existing/folder',
};
jsdomOptions
This option only applies when using the Happo Examples integration
Happo uses jsdom internally. By default, it provides sensible defaults to the
JSDOM
constructor. See
processSnapsInBundle.js.
You can override any options here, but your mileage may vary. See
https://github.com/jsdom/jsdom#simple-options. Here's an example where the
document's referrer
is being set:
module.exports = {
jsdomOptions: {
referrer: 'http://google.com',
},
};
compareThreshold
This option is deprecated (since February 2021). The setting has moved into deep-compare settings for projects. See the Compare Threshold guide for more information on how to set things up.
By default, a shallow comparison is made when happo compare
is called. If two
images have one or more different pixels, they will be reported as a diff—even
if the diff is very small. If you set a compareThreshold
, a deep comparison
will be performed instead, where individual pixels are inspected.
A color distance is computed for every diffing pixel. If all diffing pixels have
a color distance smaller than the compareThreshold
, the diff is considered
acceptable and the two images will be considered visually equal.
The difference is calculated according to the paper "Measuring perceived color difference using YIQ NTSC transmission color space in mobile applications" by Y. Kotsarenko and F. Ramos.
Warning: If the threshold is too high, you risk hiding diffs that you wouldn't want to be hidden. Be careful when using this option.
module.exports = {
compareThreshold: 0.005,
};
To help find the right value, you can make dry-run comparisons. Find one or a
few comparisons (via https://happo.io/dashboard) and run
happo compare <sha1> <sha2> --dry-run
on the SHAs and examine the logged
output to determine what threshold value you want to use.
asyncTimeout
This option only applies when using the Happo Examples integration
If an example renders nothing to the DOM, Happo will wait a short while for
content to appear. Specified in milliseconds, the default is 200
.
module.exports = {
asyncTimeout: 500,
};
githubApiUrl
Used when you have the CI script configured to
post Happo statuses as comments.
The default is https://api.github.com
. If you're using GitHub Enterprise,
enter the URL to your local GitHub API here, such as
https://ghe.mycompany.zone/api/v3
(the default for GHE installation is for the
API to be located at /api/v3
).