A Jest plugin for creating screenshots of React components with a little help of Puppeteer
A jest-puppe-shots
is a Jest plugin that can help you create Visual Regression tests for your React components.
If you ever used Enzyme or Snapshots you will love the jest-puppe-shots
.
The Snapshot testing approach is really awesome but it has one downside: it cannot be used to make visual regression tests of your components. Thanks to the Puppeteer API it's now possible to use Chromium browser in Jest and create real screenshots of your components!
You can install the plugin using NPM:
npm install jest-puppe-shots --save-dev
or by Yarn:
yarn add jest-puppe-shots --dev
You will have to install jest
and react-dom
if you don't have them yet:
npm install jest react-dom
The jest-puppe-shots
API is written on top of the Puppeteer which means the currently supported version of Node is 7.6+.
You will also have to setup your Babel configuration to use the Async/Await from ES7.
Here you can find the tutorial how to setup your Babel settings to use it.
Before starting using the jest-puppe-shots
you will need to change your Jest configuration file.
Open the jest.config.json
file in your project and add additional entry:
{
"preset": "jest-puppe-shots-preset"
}
If you are are using the jest.config.js
file, then instead adjust your configuration like this:
module.exports = {
// You config goes here
preset: 'jest-puppe-shots-preset'
}
After setting up the configuration you can start writing your first integration test that will take screenshots.
The jest-puppe-shots
it utilizing a couple of concepts and tools under the hood:
- It's using Puppeteer to start Chromium browser in the headless mode
- It takes a Screenshot of the component and stores it as a Snapshot inside your repository under the
__image_snapshots__
directory. If you are not familiar with the Jest Snapshots testing take a look at Jest documentation page. - Uses the
.toMatchImageSnapshot()
matcher to compare the base screenshot with the current version token during the test execution.
Take a look at the example test code:
const { openNewPage } = require('jest-puppe-shots'); // 1. Require the jest-puppe-shots module into your test
import { openNewPage } from 'jest-puppe-shots'; // or use the ES module import if you like
import MyComponent from './MyComponent'; // 2. Import your React component
let page;
beforeEach(async () => {
page = await openNewPage(); // 3. Open new page for taking screenshots
});
test('should render <Foo> component', async () => {
const component = await page.mount( // 4. Mount your component
<MyComponent className="my-component">
<strong>Hello World!</strong>
</MyComponent>
);
const screenshot = await page.takeScreenshot(component); // 5. Take a screenshot of your component
expect(screenshot).toMatchImageSnapshot(); // 6. Assert image snapshots and you're done!
});
Running this code for the first time by Jest, will produce a Base Screenshot and store it inside the repository at your-test-location/__image_snapshots__
directory.
To update the Base Screenshot run Jest with --updateSnapshot
or -u
parameter.
If your components are based on additional CSS code from your code base like ex. reset.css
you might like to mount the static content on the page:
import path from 'path';
import { openNewPage } from 'jest-puppe-shots';
let page;
beforeEach(async () => {
page = await openNewPage();
await page.mountCssContext(path.resolve('../path/to/my-assets-dir', [
'css/reset.css',
'css/custom-styles.css'
]))
});
Mounting the CSS directory might also help you with loading static content files like images or font.
You can also load and put an external CSS and JS files on your page:
import { openNewPage } from 'jest-puppe-shots';
let page;
beforeEach(async () => {
page = await openNewPage();
await page.loadExternalCss(
'https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css',
'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css'
);
await page.loadExternalJs(
'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js'
);
});
Both loadExternalCss
and loadExternalJs
function can accept multiple arguments so you can pass as URLs as you want.
- Transform the
*.less
and*.sass
files into CSS code when importing component modules by Jest - Better support for the
Enzyme
like API. You should be able to use ex.get
, orfind
functions after mounting component - Making screenshots of component parts by ex. selecting DOM nodes
- More built-in renderers and support for custom renderers (pass a function)
- Debugging: allow to start test without the headless mode and see what browser is doing
You will have to install Jest 22+. The main reason of that is version 22 introduced the globalSetup
and globalTeardown
options APIs.
Since both Puppeteer and jest-puppe-shots
are heavily depending on the async/await
API you will have to use Node 7.6+ that enabled the support for it.
I don't want to run Puppeteer each time I'm running my tests. It takes ages to start Jest and it's getting slow!
No problem! You don't need to launch Puppeteer for you regular Unit Tests. You will just have to adjust your environment a little bit. Take a look at the example:
- Edit your
package.json
file and provide additional entries for running screenshot tests:
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch"
}
}
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:screenshots": "cross-env TAKE_SCREENSHOTS=true jest",
"test:watch:screenshots": "cross-env TAKE_SCREENSHOTS=true jest --watch"
}
}
We are using the cross-env
package in order to set environment variables for all the operating systems.
- Rename your
jest.config.json
tojest.config.js
and adjust the source code to usejest-puppe-shots
preset only when we need it.
{
"preset": "jest-puppe-shots-preset"
}
let config = {
/* Your Jest config goes here */
};
if (process.env.TAKE_SCREENSHOTS) {
config.preset = 'jest-puppe-shots-preset';
}
module.exports = config;
- Right now you can run unit tests as usual by
npm run test
and you can launch integration tests by runningnpm run test:screenshots
That's fine. Good news for you is that jest-puppe-shots
is supporting styled-components
.
All you need to do is to inform runner that you would like to use a custom renderer.
Edit your Jest config file jest.config.json
and add new globals
entry to the configuration:
{
"globals": {
"__JEST_PUPPE_SHOTS_RENDERER__": "STYLED_COMPONENTS"
}
}
Currently, there are two supported renderers:
REACT_SERVER
(default) - It's using thereact-dom/server
package to render component as stringSTYLED_COMPONENTS
- It's using theServerStyleSheet
fromstyled-components
to intercept the produced CSS styles and inject them on the page