React
Visual regression testing for React components with Percy:
- If you use Storybook for React, please see Percy for Storybook.
- If you use react-rails, please see the Percy for Rails.
react-percy is deprecated
The react-percy SDK has been deprecated in favor of our Storybook, Puppeteer, or Cypress SDKs. If you need help transitioning to a newer SDK feel free to reach out to support.
Setup
Make sure you have completed Setup of your CI service first. You can also test Percy in your local development environment.
Install @percy/react
using npm
:
npm install --save-dev @percy/react
Or using yarn
:
yarn add --dev @percy/react
Add the following section to your package.json
:
{
"scripts": {
"percy": "react-percy"
}
}
Usage
Let's get started by creating a snapshot for a React component in your project. Suppose you have a Button
component in a file called Button.js
:
import React from 'react';
export default class Button extends React.Component {
render() {
return <button>{this.props.children}</button>;
}
}
Create a new file Button.percy.js
. This will contain our Percy snapshot:
import React from 'react';
import Button from './Button';
percySnapshot('Button', () => {
return <Button>My Button</Button>
});
That's it!
Now, whenever CI runs, a snapshot of the Button
component will be uploaded to Percy for visual regression testing!
ESLint Configuration
If your project uses ESLint, you will want to whitelist the react-percy
globals to avoid "percySnapshot is not defined" errors. The easiest way to do this is using eslint-plugin-react-percy
.
Install eslint-plugin-react-percy
using npm
:
npm install --save-dev eslint-plugin-react-percy
Or using yarn
:
yarn add --dev eslint-plugin-react-percy
Add react-percy
to the plugins section of your .eslintrc
configuration file:
{
"plugins": [
"react-percy"
]
}
You can whitelist the environment variables provided by react-percy
by doing:
{
"env": {
"react-percy/globals": true
}
}
If you're using ESLint v4.1.0 or later, you can instead scope the react-percy
environment variables to only react-percy
snapshot files by doing:
{
"overrides": [
{
"files": [
"**/*.percy.{js,jsx}"
],
"env": {
"react-percy/globals": true
}
}
]
}
Advanced configuration
Webpack configuration
The default Webpack configuration of react-percy
will run Babel on all .js
and .jsx
files in your project, with support for all modern JS language features.
If your project is a non-ejected create-react-app
, we also automatically include create-react-app
Webpack rules for CSS and image files.
Otherwise, if you already have your own Webpack setup, react-percy
will not use it by default. However, we allow you to customize our Webpack setup by creating a percy.config.js
file in your project's root directory and including an object with any custom Webpack settings.
Reusing webpack config
If you have a particularly complex Webpack config, you may wish to simply reuse it rather than rewriting your rules and plugins in percy.config.js
.
To do so, you can import your Webpack config in percy.config.js
and set it as the webpack
field like so:
import config from './webpack.config.js';
export default {
webpack: config
};
Keep in mind that we ignore the entry
and output
fields of your Webpack settings, so if your Webpack config has any plugins that rely on these you may encounter errors. For example, the Webpack CommonsChunkPlugin
often refers to specific entry
names, but as we ignore your original entry
settings it will not be able to find them.
Also remember that the webpack
field must be a plain object; it cannot be an array or a function.
Custom Webpack config
For example, let's say you wanted to add SCSS support in your snapshots. Simply add the following to a file called percy.config.js
in your project's root directory:
export default {
webpack: {
module: {
rules: [
{
test: /\.scss$/,
loaders: ['style-loader', 'css-loader', 'sass-loader']
}
]
}
}
};
You can also provide plugins:
import webpack from 'webpack';
export default {
webpack: {
plugins: [
new webpack.DefinePlugin({
'process.env.CUSTOM_ENV': '"foo"',
}),
]
}
};
Supported Webpack options
There are a few things to keep in mind when customizing our Webpack configuration.
You can add almost any Webpack config options in percy.config.js
, including rules, plugins, and aliases. But you will not be able to modify the entry or output fields.
Also note that the webpack
field in percy.config.js
must be an object. Unlike a plain webpack.config.js
file, it cannot be a function or an array.
Including entry files
As noted above, react-percy
ignores the entry
section of your Webpack settings and instead only builds Percy snapshot files and their imports. If your original Webpack entry
includes additional global files like babel-polyfill
or CSS files that you want to include in all snapshots, you can include them by creating a percy.config.js
file in your project's root directory and adding an includeFiles
setting like so:
export default {
includeFiles: [
'babel-polyfill',
'./styles/global.css'
]
};
These files can be package names or relative paths to files within your project. Note that relative paths will be resolved from the root directory of your project.
Any packages or files listed there will be loaded before any snapshots.
Best practices
Percy snapshot files
react-percy
looks for files in your project ending with .percy.js
or .percy.jsx
.
We recommend keeping your Percy snapshots close to the components being tested, but it's entirely up to you!
One common pattern is to add Percy snapshot files right next to the component files. So if you have a component in src/components/Button.js
, you would add its snapshots in src/components/Button.percy.js
.
Another common pattern is to keep Percy snapshots in a __percy__
folder. For the above example, that would be src/components/__percy__/Button.percy.js
.
Basic snapshots
percySnapshot(name, fn)
The first argument is the snapshot name; the second argument is a function that returns the React markup to snapshot. For example:
percySnapshot('Button', () => {
return <Button>My Button</Button>;
});
The name is what you will see when reviewing snapshots in Percy's web interface, and must be unique across all snapshots in your project. In the above example, the snapshot will be named "Button".
The function may include any necessary setup, but must ultimately return the React markup to snapshot.
Testing multiple breakpoints
percySnapshot(name, options, fn)
A powerful feature of Percy is visual regression testing for responsive designs. To take snapshots of your component at multiple breakpoints, simply specify the widths to use via the optional middle options
argument:
percySnapshot('Button', { widths: [320, 768, 1024] }, () => {
return <Button>My Button</Button>;
});
This will take snapshots of "Button" at widths of 320px, 768px, and 1024px.
Hooks
In some cases while writing Percy snapshots you may have some setup work that needs to happen before snapshots run. This is often useful for setting up props that you'd like to share across multiple snapshots.
For example, suppose you had a PlayerInfo
component with multiples variations that share many of the same props. You could initialize the common props in beforeEach
and use them in each snapshot:
let props;
beforeEach(() => {
props = {
firstName: 'Venus',
lastName: 'Williams',
age: 37
};
});
percySnapshot('PlayerInfo: basic', () => {
return <PlayerInfo {...props} />;
});
percySnapshot('PlayerInfo: expanded', () => {
return <PlayerInfo {...props} country="USA" sport="Tennis" />;
});
Grouping snapshots
You may also group snapshots together using a suite
block.
suite
blocks have the added benefit of prefixing the names of all snapshots within them. For example:
suite('Button', () => {
percySnapshot('primary', () => {
return <Button primary>Primary Button</Button>;
});
percySnapshot('secondary', () => {
return <Button secondary>Secondary Button</Button>;
});
percySnapshot('inverse', () => {
return <Button inverse>Inverse Button</Button>;
});
});
This will generate three snapshots: Button: primary
, Button: secondary
, and Button: inverse
.
You can also set the widths for all snapshots within a suite
block:
suite('Button', { widths: [320, 768] }, () => {
percySnapshot('primary', () => {
return <Button primary>Primary Button</Button>;
});
percySnapshot('secondary', () => {
return <Button secondary>Secondary Button</Button>;
});
percySnapshot('inverse', () => {
return <Button inverse>Inverse Button</Button>;
});
});
This will generate snapshots of each button at both 320px and 768px wide.
You can override the widths specified on a suite
on individual snapshots:
suite('Button', { widths: [320, 768] }, () => {
percySnapshot('primary', () => {
return <Button primary>Primary Button</Button>;
});
percySnapshot('secondary', () => {
return <Button secondary>Secondary Button</Button>;
});
percySnapshot('inverse', { widths: [480, 1024] }, () => {
return <Button inverse>Inverse Button</Button>;
});
});
This will generate snapshots of Button: primary
and Button: secondary
at 320px and 768px wide, and "Button: inverse" at 480px and 1024px wide.
When they are inside a suite
block, beforeEach
blocks only apply to the snapshots within that suite
block:
beforeEach(() => {
// this will run before each snapshot in this file
});
suite('Button', () => {
beforeEach(() => {
// this will only run before "Button: primary" and "Button: secondary"
});
percySnapshot('primary', () => {
return <Button primary>Primary Button</Button>;
});
percySnapshot('secondary', () => {
return <Button secondary>Secondary Button</Button>;
});
});
suite('Textbox', () => {
beforeEach(() => {
// this will only run before "Textbox: disabled"
});
percySnapshot('disabled', () => {
return <Textbox disabled />;
});
});
Additionally, you can nest suite
blocks within each other, and the names and widths will cascade down:
suite('Button', { widths: [320, 768] }, () => {
percySnapshot('primary', () => {
return <Button primary>Primary Button</Button>;
});
percySnapshot('secondary', () => {
return <Button secondary>Secondary Button</Button>;
});
suite('submit', () => {
percySnapshot('enabled', () => {
return <Button submit>Submit Button</Button>;
});
percySnapshot('disabled', () => {
return <Button submit disabled>Disabled Submit Button</Button>;
});
});
});
This will generate 320px and 768px wide snapshots of Button: primary
, Button: secondary
, Button: submit: enabled
, and Button: submit: disabled
.
Troubleshooting
Percy is rendering a blank white page
If you're seeing blank white snapshots in Percy, it usually means an error occurred client-side when loading your snapshot. To debug this, you can run react-percy
locally with the --debug
flag so you can load snapshots in your local browser.
$ react-percy --debug
After running react-percy
with the --debug
flag, you'll find a .percy-debug
folder at the root of your project. Open the index.html
file inside in your web browser and specify the snapshot you'd like to view as the snapshot
query parameter.
For example, if you wanted to debug a snapshot named "Button: primary", you would open file:///path/to/project/.percy-debug/index.html
in your browser and append ?snapshot=Button%3A%20primary
to the URL. Note that the snapshot name is URL encoded. The console output of running react-percy
with the --debug
flag will give you the URL-encoded query strings to use to preview any snapshot.
Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined
This error typically occurs when you are incorrectly importing your component.
For example, if your component is exported as a named export, be sure to import it that way:
export class MyComponent extends React.Component {
render() {
return <div>My component</div>;
}
}
import { MyComponent } from './MyComponent';
You may also see this error if you are mixing ES module imports/exports with CommonJS modules. For example, if your component is using an ES default export but your snapshot file is written in CommonJS, remember to append .default
to your require import:
export default class MyComponent extends React.Component {
render() {
return <div>My component</div>;
}
}
const MyComponent = require('./MyComponent').default;
Contributing
- Fork it ( https://github.com/percy/react-percy/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new pull request
Updated over 4 years ago