UNPKG

25.8 kBMarkdownView Raw
1# StoryShots
2
3StoryShots adds automatic Jest Snapshot Testing for [Storybook](https://storybook.js.org/).
4
5[Framework Support](https://github.com/storybookjs/storybook/blob/master/ADDONS_SUPPORT.md)
6
7![StoryShots In Action](https://raw.githubusercontent.com/storybookjs/storybook/HEAD/addons/storyshots/storyshots-core/docs/storyshots-fail.png)
8
9To use StoryShots, you must use your existing Storybook stories as the input for Jest Snapshot Testing.
10
11## Getting Started
12
13Add the following module into your app.
14
15```sh
16yarn add @storybook/addon-storyshots --dev
17```
18
19## Configure Storyshots for HTML snapshots
20
21Create a new test file with the name `Storyshots.test.js`. (Or whatever the name you prefer, as long as it matches Jest's config [`testMatch`](http://facebook.github.io/jest/docs/en/configuration.html#testmatch-array-string)).
22Then add following content to it:
23
24```js
25import initStoryshots from '@storybook/addon-storyshots';
26
27initStoryshots();
28```
29
30That's all.
31
32Now run your Jest test command. (Usually, `npm test`.) Then you can see all of your stories are converted as Jest snapshot tests.
33
34![Screenshot](https://raw.githubusercontent.com/storybookjs/storybook/HEAD/addons/storyshots/storyshots-core/docs/storyshots.png)
35
36### Testing stories that rely on addon-added decorators
37
38If you have stories in your Storybook that can only render inside a decorator (for instance the [`apollo-storybook-decorator`](https://github.com/abhiaiyer91/apollo-storybook-decorator)), you'll need to ensure those decorators are applied in Storyshots.
39
40If you export those decorators from your `.storybook/preview.js` then Storyshots will apply those decorators for you in the same way that Storybook does. However if the addon _automatically_ adds the decorator for you (which is a new feature in Storybook 6.0), you will find the decorator does not get added in Storyshots. This is a limitation in Storyshots currently.
41
42To ensure such decorators get added, export them from `.storybook/preview.js`:
43
44```js
45import addonDecorator from 'some-addon';
46
47export const decorators = [addonDecorator];
48```
49
50## Configure your app for Jest
51
52In many cases, for example Create React App, it's already configured for Jest. You need to create a filename with the extension `.test.js`.
53
54If you still need to configure jest you can use the resources mentioned below:
55
56- [Getting Started - Jest Official Documentation](https://facebook.github.io/jest/docs/en/getting-started.html)
57- [Javascript Testing with Jest - Egghead](https://egghead.io/lessons/javascript-test-javascript-with-jest). **_paid content_**
58
59> Note: If you use React 16, you'll need to follow [these additional instructions](https://github.com/facebook/react/issues/9102#issuecomment-283873039).
60>
61> Note: Make sure you have added the `json` extension to `moduleFileExtensions` in `jest.config.json`. If this is missing it leads to the [following error](https://github.com/storybookjs/storybook/issues/3728): `Cannot find module 'spdx-license-ids' from 'scan.js'`.
62>
63> Note: Please make sure you are using `jsdom` as the testEnvironment on your jest config file.
64
65### Configure Jest to work with Webpack's [require.context()](https://webpack.js.org/guides/dependency-management/#require-context)
66
67**NOTE**: if you are using Storybook 5.3's `main.js` to list story files, this is no longer needed.
68
69Sometimes it's useful to configure Storybook with Webpack's require.context feature. You could be loading stories [one of two ways](https://storybook.js.org/docs/react/writing-stories/loading-stories).
70
711. If you're using the `storiesOf` API, you can integrate it this way:
72
73```js
74import { configure } from '@storybook/react';
75
76const req = require.context('../stories', true, /\.stories\.js$/); // <- import all the stories at once
77
78function loadStories() {
79 req.keys().forEach((filename) => req(filename));
80}
81
82configure(loadStories, module);
83```
84
852. If you're using Component Story Format (CSF), you'll integrate it like so:
86
87```js
88import { configure } from '@storybook/react';
89
90const req = require.context('../stories', true, /\.stories\.js$/); // <- import all the stories at once
91
92configure(req, module);
93```
94
95The problem here is that it will work only during the build with webpack,
96other tools may lack this feature. Since Storyshot is running under Jest,
97we need to polyfill this functionality to work with Jest. The easiest
98way is to integrate it to babel.
99
100You can do this with a Babel [plugin](https://github.com/smrq/babel-plugin-require-context-hook) or [macro](https://github.com/storybookjs/require-context.macro). If you're using `create-react-app` (v2 or above), use the macro.
101
102#### Option 1: Plugin
103
104First, install it:
105
106```sh
107yarn add babel-plugin-require-context-hook --dev
108```
109
110Next, it needs to be registered and loaded before each test. To register it, create a file with the following register function `.jest/register-context.js`:
111
112```js
113import registerRequireContextHook from 'babel-plugin-require-context-hook/register';
114registerRequireContextHook();
115```
116
117That file needs to be added as a setup file for Jest. To do that, add (or create) a property in Jest's config called [`setupFiles`](https://jestjs.io/docs/en/configuration.html#setupfiles-array). Add the file name and path to this array.
118
119```json
120setupFiles: ['<rootDir>/.jest/register-context.js']
121```
122
123Finally, add the plugin to `.babelrc`:
124
125```json
126{
127 "presets": ["..."],
128 "plugins": ["..."],
129 "env": {
130 "test": {
131 "plugins": ["require-context-hook"]
132 }
133 }
134}
135```
136
137The plugin is only added to the test environment otherwise it could replace webpack's version of it.
138
139#### Option 2: Macro
140
141First, install it:
142
143```sh
144yarn add require-context.macro --dev
145```
146
147Now, inside of your Storybook config file, import the macro and run it in place of `require.context`, like so:
148
149```javascript
150import requireContext from 'require-context.macro';
151
152// const req = require.context('../stories', true, /\.stories\.js$/); <-- replaced
153const req = requireContext('../stories', true, /\.stories\.js$/);
154```
155
156### Configure Jest for React
157
158StoryShots addon for React is dependent on [react-test-renderer](https://github.com/facebook/react/tree/master/packages/react-test-renderer), but
159[doesn't](#deps-issue) install it, so you need to install it separately.
160
161```sh
162yarn add react-test-renderer --dev
163```
164
165### Configure Jest for Angular
166
167StoryShots addon for Angular is dependent on [jest-preset-angular](https://github.com/thymikee/jest-preset-angular), but
168[doesn't](#deps-issue) install it, so you need to install it separately.
169
170```sh
171yarn add jest-preset-angular
172```
173
174If you already use Jest for testing your angular app - probably you already have the needed jest configuration.
175Anyway you can add these lines to your jest config:
176
177```js
178module.exports = {
179 globals: {
180 __TRANSFORM_HTML__: true,
181 },
182 transform: {
183 '^.+\\.jsx?$': 'babel-jest',
184 '^.+\\.(ts|html)$': '<rootDir>/node_modules/jest-preset-angular/preprocessor.js',
185 },
186 moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node', '.html'],
187};
188```
189
190### Configure Jest for Vue
191
192StoryShots addon for Vue is dependent on [jest-vue-preprocessor](https://github.com/vire/jest-vue-preprocessor), but
193[doesn't](#deps-issue) install it, so you need to install it separately.
194
195```sh
196yarn add jest-vue-preprocessor
197```
198
199If you already use Jest for testing your vue app - probably you already have the needed jest configuration.
200Anyway you can add these lines to your jest config:
201
202```js
203module.exports = {
204 transform: {
205 '^.+\\.jsx?$': 'babel-jest',
206 '.*\\.(vue)$': '<rootDir>/node_modules/jest-vue-preprocessor',
207 },
208 transformIgnorePatterns: ['/node_modules/(?!(@storybook/.*\\.vue$))'],
209 moduleFileExtensions: ['vue', 'js', 'jsx', 'json', 'node'],
210};
211```
212
213### Configure Jest for Preact
214
215StoryShots addon for Preact is dependent on [preact-render-to-json](https://github.com/nathancahill/preact-render-to-json), but
216[doesn't](#deps-issue) install it, so you need to install it separately.
217
218```sh
219yarn add preact-render-to-json --dev
220```
221
222### Configure Jest for Web Components
223
224StoryShots addon for Web Components requires [jsdom](https://github.com/jsdom/jsdom) 16 or later to fully support the
225web component shadow dom. To use jsdom 16 or later you can set the Jest `testEnvironment` configuration key to
226`jest-environment-jsdom-sixteen`. This should work back to Jest 24 and is the default in Jest 26 and later.
227
228### Configure Jest for MDX Docs Add-On Stories
229
230If using the [Docs add-on](../../docs/README.md) with
231[MDX stories](../../docs/docs/mdx.md) you will need
232to configure Jest to transform MDX stories into something Storyshots can understand:
233
234Add the following to your Jest configuration:
235
236```json
237{
238 "transform": {
239 "^.+\\.[tj]sx?$": "babel-jest",
240 "^.+\\.mdx?$": "@storybook/addon-docs/jest-transform-mdx"
241 }
242}
243```
244
245### <a name="deps-issue"></a>Why don't we install dependencies of each framework ?
246
247Storyshots addon is currently supporting React, Angular and Vue. Each framework needs its own packages to be integrated with Jest. We don't want people that use only React will need to bring other dependencies that do not make sense for them.
248
249`dependencies` - will installed an exact version of the particular dep - Storyshots can work with different versions of the same framework (let's say React v16 and React v15), that have to be compatible with a version of its plugin (react-test-renderer).
250
251`optionalDependencies` - behaves like a regular dependency, but do not fail the installation in case there is a problem to bring the dep.
252
253`peerDependencies` - listing all the deps in peer will trigger warnings during the installation - we don't want users to install unneeded deps by hand.
254
255`optionalPeerDependencies` - unfortunately there is nothing like this =(
256
257For more information read npm [docs](https://docs.npmjs.com/files/package.json#dependencies)
258
259### Using `createNodeMock` to mock refs
260
261`react-test-renderer` doesn't provide refs for rendered components. By
262default, it returns null when the refs are referenced. In order to mock
263out elements that rely on refs, you will have to use the
264`createNodeMock` option [added to React](https://reactjs.org/blog/2016/11/16/react-v15.4.0.html#mocking-refs-for-snapshot-testing) starting with version 15.4.0.
265
266Here is an example of how to specify the `createNodeMock` option in Storyshots:
267
268```js
269import initStoryshots, { snapshotWithOptions } from '@storybook/addon-storyshots';
270import TextareaThatUsesRefs from '../component/TextareaThatUsesRefs';
271
272initStoryshots({
273 test: snapshotWithOptions({
274 createNodeMock: (element) => {
275 if (element.type === TextareaThatUsesRefs) {
276 return document.createElement('textarea');
277 }
278 },
279 }),
280});
281```
282
283Provide a function to have story-specific options:
284
285```js
286initStoryshots({
287 test: snapshotWithOptions((story) => ({
288 createNodeMock: (element) => {
289 if (story.name == 'foobar') {
290 return null;
291 }
292 return element;
293 },
294 })),
295});
296```
297
298### StoryShots for async rendered components
299
300You can make use of [Jest done callback](https://jestjs.io/docs/en/asynchronous) to test components that render asynchronously. This callback is passed as param to test method passed to `initStoryshots(...)` when the `asyncJest` option is given as true.
301
302#### Example
303
304The following example shows how we can use the **done callback** to take StoryShots of a [Relay](http://facebook.github.io/relay/) component. Each kind of story is written into its own snapshot file with the use of `getSnapshotFileName`.
305
306Add _stories of UserForm_ in the file: UserForm.story.jsx
307
308```jsx
309/* global module */
310import React from 'react';
311import { QueryRenderer } from 'react-relay';
312import { storiesOf } from '@storybook/react';
313
314// Use the same queries used in YOUR app routes
315import { newUserFormQuery, editUserFormQuery } from 'app/routes';
316import UserFormContainer from 'app/users/UserForm';
317
318// YOUR function to generate a Relay Environment mock.
319// See https://github.com/1stdibs/relay-mock-network-layer for more info
320import getEnvironment from 'test/support/relay-environment-mock';
321
322// User test data YOU generated for your tests
323import { user } from 'test/support/data/index';
324
325// Use this function to return a new Environment for each story
326const Environment = () =>
327 getEnvironment({
328 mocks: {
329 Node: () => ({ __typename: 'User' }),
330 User: () => user,
331 },
332 });
333
334/**
335
336 NOTICE that the QueryRenderer render its children via its render props.
337
338 If we don't take the StoryShot async then we will only see the QueryRenderer in the StoryShot.
339
340 The following QueryRenderer returns null in the first render (it can be a loading indicator instead in real file) and then when it gets the data to respond to query, it renders again with props containing the data for the Component
341 */
342const renderStory = (query, environment, variables = {}) => (
343 <QueryRenderer
344 environment={environment}
345 query={query}
346 variables={variables}
347 render={({ props, error }) => {
348 if (error) {
349 console.error(error);
350 } else if (props) {
351 return <UserFormContainer {...props} />;
352 }
353 return null;
354 }}
355 />
356);
357
358storiesOf('users/UserForm', module)
359 .add('New User', () => {
360 const environment = new Environment();
361 return renderStory(newUserFormQuery, environment);
362 })
363 .add('Editing User', () => {
364 const environment = new Environment();
365 return renderStory(editUserFormQuery, environment, { id: user.id });
366 });
367```
368
369Then, init Storyshots for async component in the file: StoryShots.test.js
370
371```jsx
372import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots';
373import { mount } from 'enzyme';
374import toJson from 'enzyme-to-json';
375
376// Runner
377initStoryshots({
378 asyncJest: true, // this is the option that activates the async behaviour
379 test: ({
380 story,
381 context,
382 done, // --> callback passed to test method when asyncJest option is true
383 }) => {
384 const converter = new Stories2SnapsConverter();
385 const snapshotFilename = converter.getSnapshotFileName(context);
386 const storyElement = story.render();
387
388 // mount the story
389 const tree = mount(storyElement);
390
391 // wait until the mount is updated, in our app mostly by Relay
392 // but maybe something else updating the state of the component
393 // somewhere
394 const waitTime = 1;
395 setTimeout(() => {
396 if (snapshotFilename) {
397 expect(toJson(tree.update())).toMatchSpecificSnapshot(snapshotFilename);
398 }
399
400 done();
401 }, waitTime);
402 },
403 // other options here
404});
405```
406
407NOTICE that When using the `asyncJest: true` option, you also must specify a `test` method that calls the `done()` callback.
408
409This is a really powerful technique to write stories of Relay components because it integrates data fetching with component rendering. So instead of passing data props manually, we can let Relay do the job for us as it does in our application.
410
411Whenever you change your data requirements by adding (and rendering) or (accidentally) deleting fields in your graphql query fragments, you'll get a different snapshot and thus an error in the StoryShot test.
412
413## Using a custom directory
414
415Depending on your project's needs, you can configure the `@storybook/addon-storyshots` to use a custom directory for the snapshots. You can read more about it in the [official docs](https://storybook.js.org/docs/react/workflows/snapshot-testing).
416
417## Options
418
419### `config`
420
421The `config` parameter must be a function that helps to configure storybook like the `preview.js` does.
422If it's not specified, storyshots will try to use [configPath](#configPath) parameter.
423
424```js
425import initStoryshots from '@storybook/addon-storyshots';
426
427initStoryshots({
428 config: ({ configure }) =>
429 configure(() => {
430 require('../stories/Button.story.js');
431 }, module),
432});
433```
434
435### `configPath`
436
437By default, Storyshots assumes the config directory path for your project as below:
438
439- Storybook for React: `.storybook`
440- Storybook for React Native: `storybook`
441
442If you are using a different config directory path, you could change it like this:
443
444```js
445import initStoryshots from '@storybook/addon-storyshots';
446
447initStoryshots({
448 configPath: '.my-storybook-config-dir',
449});
450```
451
452Or, as a more complex example, if we have a package in our `lerna` project called `app` with the path `./packages/app/src/__tests__/storyshots.js` and the storybook config directory `./packages/app/.storybook`:
453
454```js
455import path from 'path';
456import initStoryshots from '@storybook/addon-storyshots';
457
458initStoryshots({ configPath: path.resolve(__dirname, '../../.storybook') });
459```
460
461`configPath` can also specify path to the `preview.js` itself. In this case, config directory will be
462a base directory of the `configPath`. It may be useful when the `preview.js` for test should differ from the
463original one. It also may be useful for separating tests to different test configs:
464
465```js
466initStoryshots({
467 configPath: '.my-storybook-config-dir/testConfig1.js',
468});
469
470initStoryshots({
471 configPath: '.my-storybook-config-dir/testConfig2.js',
472});
473```
474
475### `suite`
476
477By default, Storyshots groups stories inside a Jest test suite called "Storyshots". You could change it like this:
478
479```js
480import initStoryshots from '@storybook/addon-storyshots';
481
482initStoryshots({
483 suite: 'MyStoryshots',
484});
485```
486
487### `storyKindRegex`
488
489If you'd like to only run a subset of the stories for your snapshot tests based on the story's kind:
490
491```js
492import initStoryshots from '@storybook/addon-storyshots';
493
494initStoryshots({
495 storyKindRegex: /^MyComponent$/,
496});
497```
498
499This can be useful if you want to separate the snapshots in directories next to each component. See an example [here](https://github.com/storybookjs/storybook/issues/892).
500
501If you want to run all stories except stories of a specific kind, you can write an inverse regex which is true for all kinds except those with a specific word such as `DontTest`
502
503```js
504import initStoryshots from '@storybook/addon-storyshots';
505
506initStoryshots({
507 storyKindRegex: /^((?!.*?DontTest).)*$/,
508});
509```
510
511This can be useful while testing react components which make use of the findDomNode API since they always fail with snapshot testing
512while using react-test-renderer see [here](https://github.com/facebook/react/issues/8324)
513
514### `storyNameRegex`
515
516If you'd like to only run a subset of the stories for your snapshot tests based on the story's name:
517
518```js
519import initStoryshots from '@storybook/addon-storyshots';
520
521initStoryshots({
522 storyNameRegex: /buttons/,
523});
524```
525
526### `framework`
527
528If you are running tests from outside of your app's directory, storyshots' detection of which framework you are using may fail. Pass `"react"` or `"react-native"` to short-circuit this.
529
530### `test`
531
532Run a custom test function for each story, rather than the default (a vanilla snapshot test).
533Setting `test` will take precedence over the `renderer` option.
534You can still overwrite what renderer is used for the test function:
535
536```js
537import initStoryshots, { renderWithOptions } from '@storybook/addon-storyshots';
538import { mount } from 'enzyme';
539
540initStoryshots({
541 test: renderWithOptions({
542 renderer: mount,
543 }),
544});
545```
546
547### `renderer`
548
549Pass a custom renderer (such as enzymes `mount`) to record snapshots.
550This may be necessary if you want to use React features that are not supported by the default test renderer,
551such as **ref** or **Portals**.
552Note that setting `test` overrides `renderer`.
553
554```js
555import initStoryshots from '@storybook/addon-storyshots';
556import { mount } from 'enzyme';
557
558initStoryshots({
559 renderer: mount,
560});
561```
562
563If you are using enzyme, you need to make sure jest knows how to serialize rendered components.
564For that, you can pass an enzyme-compatible snapshotSerializer (like [enzyme-to-json](https://github.com/adriantoine/enzyme-to-json), [jest-serializer-enzyme](https://github.com/rogeliog/jest-serializer-enzyme) etc.) with the `snapshotSerializer` option (see below).
565
566### `snapshotSerializers`
567
568Pass an array of snapshotSerializers to the jest runtime that serializes your story (such as enzyme-to-json).
569
570```js
571import initStoryshots from '@storybook/addon-storyshots';
572import { createSerializer } from 'enzyme-to-json';
573
574initStoryshots({
575 renderer: mount,
576 snapshotSerializers: [createSerializer()],
577});
578```
579
580This option needs to be set if either:
581
582- the multiSnapshot function is used to create multiple snapshot files (i.e. one per story), since it ignores any serializers specified in your jest config.
583- serializers not specified in your jest config should be used when snapshotting stories.
584
585### `serializer` (deprecated)
586
587Pass a custom serializer (such as enzyme-to-json) to serialize components to snapshot-comparable data. The functionality of this option is completely covered by [snapshotSerializers](`snapshotSerializers`) which should be used instead.
588
589```js
590import initStoryshots from '@storybook/addon-storyshots';
591import toJSON from 'enzyme-to-json';
592
593initStoryshots({
594 renderer: mount,
595 serializer: toJSON,
596});
597```
598
599This option only needs to be set if the default `snapshotSerializers` is not set in your jest config.
600
601### `stories2snapsConverter`
602
603This parameter should be an instance of the [`Stories2SnapsConverter`](src/Stories2SnapsConverter.js) (or a derived from it) Class that is used to convert story-file name to snapshot-file name and vice versa.
604
605By default, the instance of this class is created with these default options:
606
607```js
608{
609 snapshotsDirName: '__snapshots__',
610 snapshotExtension: '.storyshot',
611 storiesExtensions: ['.js', '.jsx', '.ts', '.tsx'],
612}
613```
614
615This class might be overridden to extend the existing conversion functionality or instantiated to provide different options:
616
617```js
618import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots';
619
620initStoryshots({
621 stories2snapsConverter: new Stories2SnapsConverter({
622 snapshotExtension: '.storypuke',
623 storiesExtensions: ['.foo'],
624 }),
625});
626```
627
628## Exports
629
630Apart from the default export (`initStoryshots`), Storyshots also exports some named test functions (see the `test` option above):
631
632### `snapshot`
633
634The default, render the story as normal and take a Jest snapshot.
635
636### `renderOnly`
637
638Just render the story, don't check the output at all. This is useful as a low-effort way of smoke testing your
639components to ensure they do not error.
640
641### `snapshotWithOptions(options)`
642
643Like the default, but allows you to specify a set of options for the test renderer. [See for example here](https://github.com/storybookjs/storybook/blob/b915b5439786e0edb17d7f5ab404bba9f7919381/examples/test-cra/src/storyshots.test.js#L14-L16).
644
645### `renderWithOptions(options)`
646
647Like the default, but allows you to specify a set of options for the renderer, just like `snapshotWithOptions`.
648
649### `multiSnapshotWithOptions(options)`
650
651Like `snapshotWithOptions`, but generate a separate snapshot file for each stories file rather than a single monolithic file (as is the convention in Jest). This makes it dramatically easier to review changes. If you'd like the benefit of separate snapshot files, but don't have custom options to pass, you can pass an empty object.
652If you use [Component Story Format](https://storybook.js.org/docs/react/api/csf), you may also need to add an additional Jest transform to automate detecting story file names:
653
654```js
655// jest.config.js
656module.exports = {
657 transform: {
658 '^.+\\.stories\\.jsx?$': '@storybook/addon-storyshots/injectFileName',
659 '^.+\\.jsx?$': 'babel-jest',
660 },
661};
662```
663
664#### integrityOptions
665
666This option is useful when running test with `multiSnapshotWithOptions(options)` in order to track snapshots are matching the stories. (disabled by default).
667The value is a [settings](https://github.com/isaacs/node-glob#options) to a `glob` object, that searches for the snapshot files.
668
669```js
670initStoryshots({
671 integrityOptions: { cwd: __dirname }, // it will start searching from the current directory
672 test: multiSnapshotWithOptions(),
673});
674```
675
676### `shallowSnapshot`
677
678Take a snapshot of a shallow-rendered version of the component. Note that this option will be overridden if you pass a `renderer` option.
679
680### `Stories2SnapsConverter`
681
682This is a class that generates snapshot's name based on the story (kind, story & filename) and vice versa.
683
684###### Example:
685
686Let's say we wanted to create a test function for shallow && multi-file snapshots:
687
688```js
689import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots';
690import { shallow } from 'enzyme';
691import toJson from 'enzyme-to-json';
692
693const converter = new Stories2SnapsConverter();
694
695initStoryshots({
696 test: ({ story, context }) => {
697 const snapshotFileName = converter.getSnapshotFileName(context);
698 const storyElement = story.render();
699 const shallowTree = shallow(storyElement);
700
701 if (snapshotFileName) {
702 expect(toJson(shallowTree)).toMatchSpecificSnapshot(snapshotFileName);
703 }
704 },
705});
706```
707
708### `asyncJest`
709
710Enables Jest `done()` callback in the StoryShots tests for async testing. See [StoryShots for async rendered components](#storyshots-for-async-rendered-components) for more info.
711
712## Story Parameters
713
714### `disable`
715
716Some stories are difficult or impossible to snapshot, such as those covering components that use external DOM-modifying libraries, and those that deliberately throw errors. It is possible to skip stories like these by giving them a parameter of `storyshots: {disable: true}`. There is also a shorthand for this, `storyshots: false`.
717
718```js
719export const Exception = () => {
720 throw new Error('storyFn threw an error! WHOOPS');
721};
722Exception.storyName = 'story throws exception';
723Exception.parameters = {
724 storyshots: { disable: true },
725};
726```