UNPKG

12.3 kBMarkdownView Raw
1# StoryShots + [Puppeteer](https://github.com/GoogleChrome/puppeteer)
2
3## Getting Started
4
5Add the following modules into your app.
6
7```sh
8npm install @storybook/addon-storyshots-puppeteer puppeteer --save-dev
9```
10
11⚠️ As of Storybook 5.3 `puppeteer` is no more included in addon dependencies and must be added to your project directly.
12
13## Configure Storyshots for Puppeteer tests
14
15⚠️ **React-native** is **not supported** by this test function.
16
17When willing to run Puppeteer tests for your stories, you have two options:
18
19- Have a storybook running (ie. accessible via http(s), for instance using `npm run storybook`)
20- Have a static build of the storybook (for instance, using `npm run build-storybook`)
21
22Then you will need to reference the storybook URL (`file://...` if local, `http(s)://...` if served)
23
24## _puppeteerTest_
25
26Allows to define arbitrary Puppeteer tests as `story.parameters.puppeteerTest` function.
27
28You can either create a new Storyshots instance or edit the one you previously used:
29
30```js
31import initStoryshots from '@storybook/addon-storyshots';
32import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';
33
34initStoryshots({ suite: 'Puppeteer storyshots', test: puppeteerTest() });
35```
36
37Then, in your stories:
38
39```js
40export const myExample = () => {
41 ...
42};
43myExample.parameters = {
44 async puppeteerTest(page) {
45 const element = await page.$('<some-selector>');
46 await element.click();
47 expect(something).toBe(something);
48 },
49};
50```
51
52This will assume you have a storybook running on at _<http://localhost:6006>_.
53Internally here are the steps:
54
55- Launches a Chrome headless using [puppeteer](https://github.com/GoogleChrome/puppeteer)
56- Browses each stories (calling _<http://localhost:6006/iframe.html?...>_ URL),
57- Runs the `parameters.puppeteerTest` function if it's defined.
58
59### Specifying the storybook URL
60
61If you want to set specific storybook URL, you can specify via the `storybookUrl` parameter, see below:
62
63```js
64import initStoryshots from '@storybook/addon-storyshots';
65import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';
66
67initStoryshots({
68 suite: 'Puppeteer storyshots',
69 test: puppeteerTest({ storybookUrl: 'http://my-specific-domain.com:9010' }),
70});
71```
72
73The above config will use _<https://my-specific-domain.com:9010>_ for tests. You can also use query parameters in your URL (e.g. for setting a different background for your storyshots, if you use `@storybook/addon-backgrounds`).
74
75You may also use a local static build of storybook if you do not want to run the webpack dev-server:
76
77```js
78import initStoryshots from '@storybook/addon-storyshots';
79import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';
80
81initStoryshots({
82 suite: 'Puppeteer storyshots',
83 test: puppeteerTest({
84 storybookUrl: 'file:///path/to/my/storybook-static',
85 // storybookUrl: 'file://${path.resolve(__dirname, '../storybook-static')}'
86 }),
87});
88```
89
90### Specifying options to _goto()_ (Puppeteer API)
91
92You might use `getGotoOptions` to specify options when the storybook is navigating to a story (using the `goto` method). Will be passed to [Puppeteer .goto() fn](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagegotourl-options)
93
94```js
95import initStoryshots from '@storybook/addon-storyshots';
96import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';
97
98const getGotoOptions = ({ context, url }) => {
99 return {
100 waitUntil: 'networkidle0',
101 };
102};
103
104initStoryshots({
105 suite: 'Puppeteer storyshots',
106 test: puppeteerTest({ storybookUrl: 'http://localhost:6006', getGotoOptions }),
107});
108```
109
110### Specifying custom Chrome executable path (Puppeteer API)
111
112You might use `chromeExecutablePath` to specify the path to a different version of Chrome, without downloading Chromium. Will be passed to [Runs a bundled version of Chromium](https://github.com/GoogleChrome/puppeteer#default-runtime-settings)
113
114```js
115import initStoryshots from '@storybook/addon-storyshots';
116import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';
117
118const chromeExecutablePath = '/usr/local/bin/chrome';
119
120initStoryshots({
121 suite: 'Puppeteer storyshots',
122 test: puppeteerTest({ storybookUrl: 'http://localhost:6006', chromeExecutablePath }),
123});
124```
125
126### Specifying a custom Puppeteer `browser` instance
127
128You might use the async `getCustomBrowser` function to obtain a custom instance of a Puppeteer `browser` object. This will prevent `storyshots-puppeteer` from creating its own `browser`. It will create and close pages within the `browser`, and it is your responsibility to manage the lifecycle of the `browser` itself.
129
130```js
131import initStoryshots from '@storybook/addon-storyshots';
132import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';
133import puppeteer from 'puppeteer';
134
135(async function () {
136 initStoryshots({
137 suite: 'Puppeteer storyshots',
138 test: puppeteerTest({
139 storybookUrl: 'http://localhost:6006',
140 getCustomBrowser: () => puppeteer.connect({ browserWSEndpoint: 'ws://yourUrl' }),
141 }),
142 });
143})();
144```
145
146### Customizing a `page` instance
147
148Sometimes, there is a need to customize a page before it calls the `goto` api.
149
150An example of device emulation:
151
152```js
153import initStoryshots from '@storybook/addon-storyshots';
154import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';
155const devices = require('puppeteer/DeviceDescriptors');
156
157const iPhone = devices['iPhone 6'];
158
159function customizePage(page) {
160 return page.emulate(iPhone);
161}
162
163initStoryshots({
164 suite: 'Puppeteer storyshots',
165 test: puppeteerTest({
166 storybookUrl: 'http://localhost:6006',
167 customizePage,
168 }),
169});
170```
171
172### Specifying setup and tests timeout
173
174By default, `@storybook/addon-storyshots-puppeteer` uses 15 second timeouts for browser setup and test functions.
175Those can be customized with `setupTimeout` and `testTimeout` parameters.
176
177### Integrate Puppeteer storyshots with regular app
178
179You may want to use another Jest project to run your Puppeteer storyshots as they require more resources: Chrome and Storybook built/served.
180You can find a working example of this in the [official-storybook](https://github.com/storybookjs/storybook/tree/master/examples/official-storybook) example.
181
182### Integrate Puppeteer storyshots with [Create React App](https://github.com/facebookincubator/create-react-app)
183
184You have two options here, you can either:
185
186- Add the storyshots configuration inside any of your `test.js` file. You must ensure you have either a running storybook or a static build available.
187
188- Create a custom test file using Jest outside of the CRA scope:
189
190 A more robust approach would be to separate existing test files ran by create-react-app (anything `(test|spec).js` suffixed files) from the test files to run Puppeteer storyshots.
191 This use case can be achieved by using a custom name for the test file, ie something like `puppeteer-storyshots.runner.js`. This file will contain the `initStoryshots` call with Puppeteer storyshots configuration.
192 Then you will create a separate script entry in your package.json, for instance
193
194 ```json
195 {
196 "scripts": {
197 "puppeteer-storyshots": "jest puppeteer-storyshots.runner.js --config path/to/custom/jest.config.json"
198 }
199 }
200 ```
201
202 Note that you will certainly need a custom config file for Jest as you run it outside of the CRA scope and thus you do not have the built-in config.
203
204 Once that's setup, you can run `npm run puppeteer-storyshots`.
205
206### Reminder
207
208Puppeteer launches a web browser (Chrome) internally.
209
210The browser opens a page (either using the static build of storybook or a running instance of Storybook)
211
212If you run your test without either the static build or a running instance, this wont work.
213
214To make sure your tests run against the latest changes of your Storybook, you must keep your static build or running Storybook up-to-date.
215This can be achieved by adding a step before running the test ie: `npm run build-storybook && npm run image-snapshots`.
216If you run the Puppeteer storyshots against a running Storybook in dev mode, you don't have to worry about the stories being up-to-date because the dev-server is watching changes and rebuilds automatically.
217
218## _axeTest_
219
220Runs [Axe](https://www.deque.com/axe/) accessibility checks and verifies that they pass using [jest-puppeteer-axe](https://github.com/WordPress/gutenberg/tree/master/packages/jest-puppeteer-axe).
221
222```js
223import initStoryshots from '@storybook/addon-storyshots';
224import { axeTest } from '@storybook/addon-storyshots-puppeteer';
225
226initStoryshots({ suite: 'A11y checks', test: axeTest() });
227```
228
229For configuration, it uses the same `story.parameters.a11y` parameter as [`@storybook/addon-a11y`](https://github.com/storybookjs/storybook/tree/next/addons/a11y#parameters)
230
231## _imageSnapshots_
232
233Generates and compares screenshots of your stories using [jest-image-snapshot](https://github.com/americanexpress/jest-image-snapshot).
234
235```js
236import initStoryshots from '@storybook/addon-storyshots';
237import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer';
238
239initStoryshots({ suite: 'Image storyshots', test: imageSnapshot() });
240```
241
242It saves all images under \_\_image_snapshots\_\_ folder.
243
244### Specifying options to _jest-image-snapshots_
245
246If you wish to customize [jest-image-snapshot](https://github.com/americanexpress/jest-image-snapshot), then you can provide a `getMatchOptions` parameter that should return the options config object. Additionally, you can provide `beforeScreenshot` which is called before the screenshot is captured and a `afterScreenshot` handler which is called after the screenshot and receives the just created image.
247
248```js
249import initStoryshots from '@storybook/addon-storyshots';
250import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer';
251const getMatchOptions = ({ context: { kind, story }, url }) => {
252 return {
253 failureThreshold: 0.2,
254 failureThresholdType: 'percent',
255 };
256};
257const beforeScreenshot = (page, { context: { kind, story }, url }) => {
258 return new Promise((resolve) =>
259 setTimeout(() => {
260 resolve();
261 }, 600)
262 );
263};
264const afterScreenshot = ({ image, context }) => {
265 return new Promise((resolve) =>
266 setTimeout(() => {
267 resolve();
268 }, 600)
269 );
270};
271initStoryshots({
272 suite: 'Image storyshots',
273 test: imageSnapshot({
274 storybookUrl: 'http://localhost:6006',
275 getMatchOptions,
276 beforeScreenshot,
277 afterScreenshot,
278 }),
279});
280```
281
282`getMatchOptions` receives an object: `{ context: {kind, story}, url}`. _kind_ is the kind of the story and the _story_ its name. _url_ is the URL the browser will use to screenshot.
283
284`beforeScreenshot` receives the [Puppeteer page instance](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-page) and an object: `{ context: {kind, story}, url}`. _kind_ is the kind of the story and the _story_ its name. _url_ is the URL the browser will use to screenshot. `beforeScreenshot` is part of the promise chain and is called after the browser navigation is completed but before the screenshot is taken. It allows for triggering events on the page elements and delaying the screenshot and can be used avoid regressions due to mounting animations.
285
286`afterScreenshot` receives the created image from puppeteer.
287
288### Specifying options to _screenshot()_ (Puppeteer API)
289
290You might use `getScreenshotOptions` to specify options for screenshot. Will be passed to [Puppeteer .screenshot() fn](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagescreenshotoptions)
291
292```js
293import initStoryshots from '@storybook/addon-storyshots';
294import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer';
295const getScreenshotOptions = ({ context, url }) => {
296 return {
297 encoding: 'base64', // encoding: 'base64' is a property required by puppeteer
298 fullPage: false, // Do not take the full page screenshot. Default is 'true' in Storyshots.,
299 };
300};
301initStoryshots({
302 suite: 'Image storyshots',
303 test: imageSnapshot({ storybookUrl: 'http://localhost:6006', getScreenshotOptions }),
304});
305```
306
307`getScreenshotOptions` receives an object `{ context: {kind, story}, url}`. _kind_ is the kind of the story and the _story_ its name. _url_ is the URL the browser will use to screenshot.