# Visual Regression Testing with Jest
This is a walkthrough of how to set up Visual Regression Testing with Jest for an application created with [`create-react-app`](https://github.com/facebook/create-react-app).
The following walkthrough uses React as an example, but the approach should work for any modern frontend library! I assume it can be used with _Angular_, _Vue_, _Cycle.js_ and more.
This gist walks you through a `create-react-app` application as an example of how to set up Visual Regression Testing in Jest using libraries I wrote recently which enable this: [`jsdom-screenshot`](https://github.com/dferber90/jsdom-screenshot), [`jest-transform-css`](https://github.com/dferber90/jest-transform-css) and [`jest-transform-file`](https://github.com/dferber90/jest-transform-file).
I will write a more detailed article soon. This gist is for the curious.
*You can also check out [this repo](https://github.com/dferber90/visual-regression-testing-create-react-app-example) which is what you'll end up with in case you get confused along the way.*
## ToC
- [Visual Regression Testing with Jest](#visual-regression-testing-with-jest)
- [Set up `create-react-app`](#set-up-create-react-app)
- [Set up Visual Regression Testing libraries](#set-up-visual-regression-testing-libraries)
- [Add `react-testing-library`](#add-react-testing-library)
- [More features](#more-features)
- [Interacting with components before taking the screenshot](#interacting-with-components-before-taking-the-screenshot)
- [PostCSS, CSS Modules, Styled Components, ..](#postcss-css-modules-styled-components-)
- [Request Interception](#request-interception)
- [Static File Serving](#static-file-serving)
- [Debugging](#debugging)
- [Summary](#summary)
- [Disclaimer](#Disclaimer)
## Set up `create-react-app`
Create a new create-react-app project called "vrt-cra" (short for visual-regression-testing-create-react-app), or with whatever name you prefer.
```
npx create-react-app vrt-cra
```
Open project
```
cd vrt-cra
```
Eject project, as we need a more sophisticated setup.
You will be asked whether you really want to eject, confirm it by typing y and pressing Enter.
```
yarn eject
```
> You can start the application to see what it looks like by running `yarn start`.
> You can also run the tests once to ensure they work properly with `yarn test App.test.js`. You have to quit them with `q` after they ran as they start in _watch_ mode automatically.
## Set up Visual Regression Testing libraries
Now we need to add some libraries to enable Visual Regression Testing: [`jsdom-screenshot`](https://github.com/dferber90/jsdom-screenshot), [`jest-transform-css`](https://github.com/dferber90/jest-transform-css) and [`jest-transform-file`](https://github.com/dferber90/jest-transform-file):
```
yarn add jest-image-snapshot jsdom-screenshot jest-transform-file jest-transform-css
```
Edit `package.json` to add the Visual Regression Testing libraries to Jest.
```js
// package.json
"jest": {
// ... more stuff ...
// add this entry
"setupTestFrameworkScriptFile": "./src/setupTests.js",
// this entry already exists, change it from "node" to "jsdom"
"testEnvironment": "jsdom",
// this entry already exists, change it
"transform": {
"^.+\\.(js|jsx|mjs)$": "/node_modules/babel-jest",
"^.+\\.css$": "jest-transform-css",
"^.+\\.svg$": "jest-transform-file",
"^(?!.*\\.(js|jsx|mjs|css|json|svg)$)": "/config/jest/fileTransform.js"
},
// ... more stuff ...
}
```
_Notice that we added "svg" to the list of files not handled by `fileTransform.js` in the last line, as we're now transforming SVGs through `jest-transform-file` instead._
Extend the `expect` with the `toMatchImageSnapshot` function so that we can compare images.
```js
// src/setupTests.js
import { toMatchImageSnapshot } from "jest-image-snapshot";
expect.extend({ toMatchImageSnapshot });
```
Adapt the `App.test.js` test.
```js
// src/App.test.js
import React from "react";
import ReactDOM from "react-dom";
import { generateImage } from "jsdom-screenshot";
import App from "./App";
it("has no visual regressions", async () => {
// render App into jsdom
const div = document.createElement("div");
document.body.appendChild(div);
ReactDOM.render(, div);
// prevent spinner from rotating to ensure consistent screenshots
document
.getElementsByClassName("App-logo")[0]
.setAttribute("style", "animation: none");
// Take screenshot with generateImage()
const screenshot = await generateImage();
// and compare it to the previous sceenshot with toMatchImageSnapshot()
expect(screenshot).toMatchImageSnapshot();
// clean up for next test
ReactDOM.unmountComponentAtNode(div);
document.body.removeChild(div);
});
```
🎉 This is already the end of the setup for basic Visual Regression Testing!
We can now rerun the tests with `yarn test App.test.js`.
_Notice that you must restart your tests completely (exit the "watch" mode) as we changed the configuration quite a bit._
You will see a screenshot was saved to `src/__image_snapshots__`. The next time you run your tests, another screenshot will get taken and compared with the one already existing there. If they match, your tests succeed as there were no visual regressions. When they differ, an image showing the differences will be created and your tests will fail. In case the changes were on purpose, the saved image can be updated by pressing u in the tests, as may already know from Jest's snapshotting feature (not to confuse with screenshots).
This is great as it gives you confidence that your layout did not change. You don't need to make any tests for classnames anymore, just take a screenshot instead.
Make some changes to `App.js` and see how it affects the tests.
_A sidenote on performance: Taking a screenshot (the `generateImage` function) takes one or two seconds, depeding on your system and the size of the sreenshot. So use the feature wisely._
## Add `react-testing-library`
The setup is quite cumbersome for each test as we're manually mounting the `div`, rendering the application and cleaning up. We can let [`react-testing-library`](https://github.com/kentcdodds/react-testing-library) handle that. If you're not using React, you can use [`dom-testing-library`](https://github.com/kentcdodds/dom-testing-library) instead.
Add `react-testing-library` and `jest-dom`.
```
yarn add react-testing-library jest-dom
```
Enable `jest-dom` helpers and clean up `react-testing-library` automatically.
```js
// src/setupTests.js
// add some helpful assertions
import "jest-dom/extend-expect";
// clean up after each test
import "react-testing-library/cleanup-after-each";
// This was here before as we added it earlier. We still need it.
import { toMatchImageSnapshot } from "jest-image-snapshot";
expect.extend({ toMatchImageSnapshot });
```
And now, we can clean up our test in `App.test.js`:
```js
// src/App.test.js
import React from "react";
import { generateImage } from "jsdom-screenshot";
import { render } from "react-testing-library";
import App from "./App";
it("has no visual regressions", async () => {
// render App into jsdom
render();
// prevent spinner from rotating to ensure consistent screenshots
document
.getElementsByClassName("App-logo")[0]
.setAttribute("style", "animation: none");
// Take screenshot with generateImage()
const screenshot = await generateImage();
// and compare it to the previous sceenshot with toMatchImageSnapshot()
expect(screenshot).toMatchImageSnapshot();
});
```
## More features
#### Interacting with components before taking the screenshot
It is possible to interact with any component before taking the screenshot. The screenshot will contain whatever the jsdom used in tests contains at that moment. See [`react-testing-library`](https://github.com/kentcdodds/react-testing-library) for more information about that.
#### PostCSS, CSS Modules, Styled Components, ..
This example showed how to use [`jest-transform-css`](https://github.com/dferber90/jest-transform-css) with global CSS. The library can also handle [CSS modules](https://github.com/css-modules/css-modules) and [Styled Components](https://www.styled-components.com/). See [`jest-transform-css`](https://github.com/dferber90/jest-transform-css) for setup instructions.
#### Request interception
In case your components make requests when mounted, you can use Request Interception to respond to requests from the tests. See [`jsdom-screenshot`](https://github.com/dferber90/jsdom-screenshot) for more information.
#### Static File Serving
A tiny webserver gets started by passing `generateImage({ serve: ['public'] })` which can serve local assets for the screenshots. See [`jsdom-screenshot`](https://github.com/dferber90/jsdom-screenshot) for more information.
#### Debugging
It is possible to print the markup that the screenshot gets taken of by passing `generateImage({ debug: true })`. See [`jsdom-screenshot`](https://github.com/dferber90/jsdom-screenshot) for more information.
## Summary
This setup has shown how to do Visual Regression Testing in Jest by the example of a `create-react-app` application. We were able to load the component's styles and the SVG file (or any other images). The walkthrough hinted at how we can use
A more advanced setup can be found at [visual-regression-testing-example](https://github.com/dferber90/visual-regression-testing-example).
## Disclaimer
This setup is highly experimental. I tried it with a few different configurations and it worked fine so far, but I'm sure there is more that needs to be fixed. Handle it with care, it is early stage!
> Feel free to ask any questions on Twitter: [@dferber90](https://twitter.com/dferber90/)!