|
| 1 | +--- |
| 2 | +title: "Testing CSS-in-JS" |
| 3 | +--- |
| 4 | + |
| 5 | +Popular CSS-in-JS libraries like [styled-components](https://github.com/styled-components/styled-components) or [emotion](https://github.com/emotion-js/emotion) can also be tested with the help of [jest-styled-components](https://github.com/styled-components/jest-styled-components) or [jest-emotion](https://github.com/emotion-js/emotion/tree/master/packages/jest-emotion) respectively. These packages improve Jest's built-in snapshot testing experience and are a great way to help avoid unintended changes to your website's UI. Please refer to your package's documentation to see if it also offers testing capabilities. |
| 6 | + |
| 7 | +*Snapshot serializers* like `jest-styled-components` or `jest-emotion` modify the standard output to a more meaningful and readable snapshot, e.g. by removing unnecessary information or displaying data in another format. Which ultimately leads to more compareable and effective snapshot tests. |
| 8 | + |
| 9 | +By default snapshots of your styled components show the generated class names (which you didn't set) and no styling information. When changing the styles you'll only see the diff of some cryptic class names (hashes). That's why you should use the above mentioned *snapshot serializers*. They remove the hashes and format the CSS in style elements. |
| 10 | + |
| 11 | +For this example we'll use emotion. The testing utilities of emotion and glamor are largely based on [jest-styled-components](https://github.com/styled-components/jest-styled-components) so they have a similar usage. Please have a look at the testing section of your library to follow along. |
| 12 | + |
| 13 | +## Installation |
| 14 | + |
| 15 | +```sh |
| 16 | +npm install --save-dev jest-emotion babel-plugin-emotion |
| 17 | +``` |
| 18 | + |
| 19 | +As [Gatsby's emotion plugin](https://www.gatsbyjs.org/packages/gatsby-plugin-emotion/) is using `babel-plugin-emotion` under the hood you'll also need to install it so that Jest can use it. |
| 20 | + |
| 21 | +If you followed along with the [Unit testing guide](docs/unit-testing) you'll have the file `jest-preprocess.js` at the root of your project. Open that file and add the plugin: |
| 22 | + |
| 23 | +```diff |
| 24 | +const babelOptions = { |
| 25 | + presets: ["@babel/react", "@babel/env"], |
| 26 | + plugins: [ |
| 27 | ++ "emotion", |
| 28 | + "@babel/plugin-proposal-optional-chaining", |
| 29 | + "@babel/plugin-proposal-class-properties", |
| 30 | + ], |
| 31 | +} |
| 32 | + |
| 33 | +module.exports = require("babel-jest").createTransformer(babelOptions) |
| 34 | +``` |
| 35 | + |
| 36 | +In order to tell Jest to use the serializer you'll need to create the file `setup-test-env.js` which will be run automatically before every test. Create the file `setup-test-env.js` at the root of your project. Insert this code into it: |
| 37 | + |
| 38 | +```js |
| 39 | +import { createSerializer } from 'jest-emotion'; |
| 40 | +import * as emotion from 'emotion'; |
| 41 | + |
| 42 | +expect.addSnapshotSerializer(createSerializer(emotion)); |
| 43 | +``` |
| 44 | + |
| 45 | +Lastly you need to tell Jest where to find this file. Open your `package.json` and add this entry to your `"jest"` section: |
| 46 | + |
| 47 | +```json |
| 48 | +"jest": { |
| 49 | + "setupTestFrameworkScriptFile": "<rootDir>/setup-test-env.js" |
| 50 | +} |
| 51 | +``` |
| 52 | + |
| 53 | +## Usage |
| 54 | + |
| 55 | +In this example you'll use `react-test-renderer` but you can also use [react-testing-library](docs/testing-react-components) or any other appropriate library. Because you created the `setup-test-env.js` file you can write your unit tests like you used to do. But now you'll also get the styling information! |
| 56 | + |
| 57 | +```js |
| 58 | +// src/components/Button.test.js |
| 59 | + |
| 60 | +import React from 'react' |
| 61 | +import styled from 'react-emotion' |
| 62 | +import renderer from 'react-test-renderer' |
| 63 | + |
| 64 | +const Button = styled.div` |
| 65 | + color: hotpink; |
| 66 | +` |
| 67 | + |
| 68 | +test('Button renders correctly', () => { |
| 69 | + expect( |
| 70 | + renderer.create(<Button>This is hotpink.</Button>).toJSON() |
| 71 | + ).toMatchSnapshot() |
| 72 | +}) |
| 73 | +``` |
| 74 | + |
| 75 | +The resulting snapshot will look like this: |
| 76 | + |
| 77 | +```js |
| 78 | +// Jest Snapshot v1, https://goo.gl/fbAQLP |
| 79 | + |
| 80 | +exports[`Button renders correctly 1`] = ` |
| 81 | +.emotion-0 { |
| 82 | + color: hotpink; |
| 83 | +} |
| 84 | +
|
| 85 | +<div |
| 86 | + className="emotion-0 emotion-1" |
| 87 | +> |
| 88 | + This is hotpink. |
| 89 | +</div> |
| 90 | +`; |
| 91 | +``` |
| 92 | + |
| 93 | +If your styled component depends on `theme` via `ThemeProvider` you'll have two options: |
| 94 | + |
| 95 | +- Wrap all your components with the `ThemeProvider` |
| 96 | +- Use API helpers (have a look at the library's documentation, e.g. [styled-components](https://github.com/styled-components/jest-styled-components#theming) or [emotion](https://github.com/emotion-js/emotion/tree/master/packages/emotion-theming#createbroadcast-function)) |
| 97 | + |
| 98 | +And this is where snapshots tests really shine. If you change, e.g. the primary color in your theme file you'll see which components get affected by this change. This way you can catch unintended changes to the style of your components. |
| 99 | + |
| 100 | +This example uses the first option: |
| 101 | + |
| 102 | +```js |
| 103 | +// src/components/Wrapper.test.js |
| 104 | + |
| 105 | +import React from 'react' |
| 106 | +import { ThemeProvider } from 'emotion-theming' |
| 107 | +import renderer from 'react-test-renderer' |
| 108 | + |
| 109 | +const theme = { |
| 110 | + maxWidth: '1450px', |
| 111 | +} |
| 112 | + |
| 113 | +const Wrapper = styled.section` |
| 114 | + max-width: ${props => props.theme.maxWidth}; |
| 115 | +` |
| 116 | + |
| 117 | +test('Wrapper renders correctly', () => { |
| 118 | + expect( |
| 119 | + renderer.create( |
| 120 | + <ThemeProvider theme={theme}> |
| 121 | + <Wrapper>Content.</Wrapper> |
| 122 | + </ThemeProvider> |
| 123 | + ).toJSON() |
| 124 | + ).toMatchSnapshot() |
| 125 | +}) |
| 126 | +``` |
| 127 | + |
| 128 | +The resulting snapshot will look like this: |
| 129 | + |
| 130 | +```js |
| 131 | +// Jest Snapshot v1, https://goo.gl/fbAQLP |
| 132 | + |
| 133 | +exports[`Wrapper renders correctly 1`] = ` |
| 134 | +.emotion-0 { |
| 135 | + max-width: 1450px; |
| 136 | +} |
| 137 | +
|
| 138 | +<section |
| 139 | + className="emotion-0 emotion-1" |
| 140 | +> |
| 141 | + Content |
| 142 | +</div> |
| 143 | +`; |
| 144 | +``` |
0 commit comments