Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

887 Generate snapshot per story file #1584

Merged
merged 31 commits into from
Aug 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
917f314
Use experemental jest-specific-snapshot to generate snapshot per stor…
igor-dv Aug 2, 2017
1d54e27
Add multiSnapshotWithOptions + upgrade jest-specific-snapshot to 0.2.0
igor-dv Aug 3, 2017
810a63b
Fix the case there is no "?"
igor-dv Aug 3, 2017
5e60a9c
Merge branch 'master' into 887-generate-snapshot-per-story-file
igor-dv Aug 6, 2017
dc8bbd7
Use patchStoriesOfFunction for RN
igor-dv Aug 6, 2017
8f9926f
Merge branch 'master' into 887-generate-snapshot-per-story-file
ndelangen Aug 7, 2017
3a78efe
Merge branch 'master' into 887-generate-snapshot-per-story-file
igor-dv Aug 8, 2017
9e20cb7
Merge branch 'master' into 887-generate-snapshot-per-story-file
igor-dv Aug 9, 2017
0eb3093
Add fileName prop to the storybook client api
igor-dv Aug 9, 2017
9f6e573
Try storyshot example pass on CI
igor-dv Aug 9, 2017
1c1c9db
Save snapshots
igor-dv Aug 9, 2017
ca294e9
Merge branch 'master' into 887-generate-snapshot-per-story-file
igor-dv Aug 9, 2017
3eb8390
Merge branch 'master' into 887-generate-snapshot-per-story-file
igor-dv Aug 10, 2017
ab71413
Support fileName in Vue
igor-dv Aug 10, 2017
ba02cc8
Merge branch 'master' into 887-generate-snapshot-per-story-file
igor-dv Aug 11, 2017
6bf56cb
Merge branch 'master' into 887-generate-snapshot-per-story-file
igor-dv Aug 12, 2017
e83b2fd
Merge branch 'master' into 887-generate-snapshot-per-story-file
ndelangen Aug 13, 2017
54cb5d1
Merge branch 'master' into 887-generate-snapshot-per-story-file
igor-dv Aug 15, 2017
70c2ebe
Merge branch 'master' into 887-generate-snapshot-per-story-file
igor-dv Aug 18, 2017
9b815a0
Merge branch 'master' into 887-generate-snapshot-per-story-file
Aug 20, 2017
96a389b
Merge branch 'master' into 887-generate-snapshot-per-story-file
ndelangen Aug 22, 2017
571373d
Merge branch 'release/3.3' into 887-generate-snapshot-per-story-file
ndelangen Aug 24, 2017
7de3acc
Merge branch 'release/3.3' into 887-generate-snapshot-per-story-file
igor-dv Aug 24, 2017
be9ab35
Merge branch 'release/3.3' into 887-generate-snapshot-per-story-file
ndelangen Aug 25, 2017
8e87fb8
CHANGE path of the snapshots files into the regular `__snapshots__` f…
ndelangen Aug 25, 2017
af02e15
CHANGE storyshots on cra-kitchen-sink to use this new feature
ndelangen Aug 25, 2017
fd3d5e5
CLEANUP reduce noise in unit tests
ndelangen Aug 25, 2017
07146a8
Merge branch 'release/3.3' into 887-generate-snapshot-per-story-file
shilman Aug 26, 2017
66c0d09
Document new option in README
shilman Aug 26, 2017
91e0105
Merge branch 'release/3.3' into 887-generate-snapshot-per-story-file
ndelangen Aug 26, 2017
6496454
Add integrity test to check if every .storyshot file has its story file
igor-dv Aug 27, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions addons/storyshots/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ Just render the story, don't check the output at all (useful if you just want to

Like the default, but allows you to specify a set of options for the test renderer. [See for example here](https://github.com/storybooks/storybook/blob/b915b5439786e0edb17d7f5ab404bba9f7919381/examples/test-cra/src/storyshots.test.js#L14-L16).

### `multiSnapshotWithOptions(options)`

Like `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.

### `shallowSnapshot`

Take a snapshot of a shallow-rendered version of the component.
10 changes: 8 additions & 2 deletions addons/storyshots/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
"scripts": {
"build-storybook": "build-storybook",
"prepublish": "babel ./src --out-dir ./dist",
"storybook": "start-storybook -p 6006"
"storybook": "start-storybook -p 6006",
"example": "jest storyshot.test"
},
"dependencies": {
"babel-runtime": "^6.23.0",
"glob": "^7.1.2",
"global": "^4.3.2",
"jest-specific-snapshot": "^0.2.0",
"prop-types": "^15.5.10",
"read-pkg-up": "^2.0.0"
},
Expand All @@ -24,11 +27,14 @@
"@storybook/channels": "^3.2.0",
"@storybook/react": "^3.2.8",
"babel-cli": "^6.24.1",
"babel-jest": "^20.0.3",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.0",
"babel-preset-react": "^6.24.1",
"react": "^15.6.1",
"react-dom": "^15.6.1"
"react-dom": "^15.6.1",
"jest": "^20.0.4",
"jest-cli": "^20.0.4"
},
"peerDependencies": {
"@storybook/addons": "^3.2.6",
Expand Down
33 changes: 29 additions & 4 deletions addons/storyshots/src/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import path from 'path';
import fs from 'fs';
import glob from 'glob';
import global, { describe, it } from 'global';
import readPkgUp from 'read-pkg-up';
import addons from '@storybook/addons';

import runWithRequireContext from './require_context';
import createChannel from './storybook-channel-mock';
import { snapshot } from './test-bodies';
import { getPossibleStoriesFiles } from './utils';

export { snapshotWithOptions, snapshot, shallowSnapshot, renderOnly } from './test-bodies';
export {
snapshot,
multiSnapshotWithOptions,
snapshotWithOptions,
shallowSnapshot,
renderOnly,
} from './test-bodies';

let storybook;
let configPath;
Expand Down Expand Up @@ -48,6 +57,7 @@ export default function testStorySnapshots(options = {}) {
runWithRequireContext(content, contextOpts);
} else if (isRNStorybook) {
storybook = require.requireActual('@storybook/react-native');

configPath = path.resolve(options.configPath || 'storybook');
require.requireActual(configPath);
} else {
Expand All @@ -70,13 +80,15 @@ export default function testStorySnapshots(options = {}) {

// eslint-disable-next-line
for (const group of stories) {
if (options.storyKindRegex && !group.kind.match(options.storyKindRegex)) {
const { fileName, kind } = group;

if (options.storyKindRegex && !kind.match(options.storyKindRegex)) {
// eslint-disable-next-line
continue;
}

describe(suite, () => {
describe(group.kind, () => {
describe(kind, () => {
// eslint-disable-next-line
for (const story of group.stories) {
if (options.storyNameRegex && !story.name.match(options.storyNameRegex)) {
Expand All @@ -85,11 +97,24 @@ export default function testStorySnapshots(options = {}) {
}

it(story.name, () => {
const context = { kind: group.kind, story: story.name };
const context = { fileName, kind, story: story.name };
options.test({ story, context });
});
}
});
});
}
}

describe('Storyshots Integrity', () => {
describe('Abandoned Storyshots', () => {
const storyshots = glob.sync('**/*.storyshot');

const abandonedStoryshots = storyshots.filter(fileName => {
const possibleStoriesFiles = getPossibleStoriesFiles(fileName);
return !possibleStoriesFiles.some(fs.existsSync);
});

expect(abandonedStoryshots).toHaveLength(0);
});
});
32 changes: 30 additions & 2 deletions addons/storyshots/src/test-bodies.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,40 @@
import renderer from 'react-test-renderer';
import shallow from 'react-test-renderer/shallow';
import 'jest-specific-snapshot';
import { getStoryshotFile } from './utils';

export const snapshotWithOptions = options => ({ story, context }) => {
function getRenderedTree(story, context, options) {
const storyElement = story.render(context);
const tree = renderer.create(storyElement, options).toJSON();
return renderer.create(storyElement, options).toJSON();
}

function getSnapshotFileName(context) {
const fileName = context.fileName;

if (!fileName) {
return null;
}

return getStoryshotFile(fileName);
}

export const snapshotWithOptions = options => ({ story, context }) => {
const tree = getRenderedTree(story, context, options);
expect(tree).toMatchSnapshot();
};

export const multiSnapshotWithOptions = options => ({ story, context }) => {
const tree = getRenderedTree(story, context, options);
const snapshotFileName = getSnapshotFileName(context);

if (!snapshotFileName) {
expect(tree).toMatchSnapshot();
return;
}

expect(tree).toMatchSpecificSnapshot(snapshotFileName);
};

export const snapshot = snapshotWithOptions({});

export function shallowSnapshot({ story, context }) {
Expand Down
15 changes: 15 additions & 0 deletions addons/storyshots/src/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import path from 'path';

export function getStoryshotFile(fileName) {
const { dir, name } = path.parse(fileName);
return path.format({ dir: path.join(dir, '__snapshots__'), name, ext: '.storyshot' });
}

export function getPossibleStoriesFiles(storyshotFile) {
const { dir, name } = path.parse(storyshotFile);

return [
path.format({ dir: path.dirname(dir), name, ext: '.js' }),
path.format({ dir: path.dirname(dir), name, ext: '.jsx' }),
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Storyshots Another Button with some emoji 1`] = `
<button
className="css-1yjiefr"
onClick={[Function]}
>
😀 😎 👍 💯
</button>
`;

exports[`Storyshots Another Button with text 1`] = `
<button
className="css-1yjiefr"
onClick={[Function]}
>
Hello Button
</button>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Storyshots Button with some emoji 1`] = `
<button
className="css-1yjiefr"
onClick={[Function]}
>
😀 😎 👍 💯
</button>
`;

exports[`Storyshots Button with text 1`] = `
<button
className="css-1yjiefr"
onClick={[Function]}
>
Hello Button
</button>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Storyshots Welcome to Storybook 1`] = `
<article
className="css-1fqbdip"
>
<h1
className="css-nil"
>
Welcome to storybook
</h1>
<p>
This is a UI component dev environment for your app.
</p>
<p>
We've added some basic stories inside the

<code
className="css-mteq83"
>
src/stories
</code>

directory.
<br />
A story is a single state of one or more UI components. You can have as many stories as you want.
<br />
(Basically a story is like a visual test case.)
</p>
<p>
See these sample

<a
className="css-ca0824"
onClick={[Function]}
role="button"
tabIndex="0"
>
stories
</a>

for a component called

<code
className="css-mteq83"
>
Button
</code>
.
</p>
<p>
Just like that, you can add your own components as stories.
<br />
You can also edit those components and see changes right away.
<br />
(Try editing the
<code
className="css-mteq83"
>
Button
</code>
stories located at
<code
className="css-mteq83"
>
src/stories/index.js
</code>
.)
</p>
<p>
Usually we create stories with smaller UI components in the app.
<br />
Have a look at the

<a
className="css-ca0824"
href="https://storybook.js.org/basics/writing-stories"
rel="noopener noreferrer"
target="_blank"
>
Writing Stories
</a>

section in our documentation.
</p>
<p
className="css-bwdon3"
>
<b>
NOTE:
</b>
<br />
Have a look at the

<code
className="css-mteq83"
>
.storybook/webpack.config.js
</code>

to add webpack loaders and plugins you are using in this project.
</p>
</article>
`;
8 changes: 8 additions & 0 deletions addons/storyshots/stories/storyshot.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import path from 'path';
import initStoryshots, { multiSnapshotWithOptions } from '../src';

initStoryshots({
framework: 'react',
configPath: path.join(__dirname, '..', '.storybook'),
test: multiSnapshotWithOptions({}),
});
10 changes: 8 additions & 2 deletions app/react-native/src/preview/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ export default class Preview {
if (module && module.hot) {
// TODO remove the kind on dispose
}
return new StoryKindApi(this._stories, this._addons, this._decorators, kind);

const fileName = module ? module.filename : null;

return new StoryKindApi(this._stories, this._addons, this._decorators, kind, fileName);
}

setAddon(addon) {
Expand All @@ -44,11 +47,14 @@ export default class Preview {

getStorybook() {
return this._stories.getStoryKinds().map(kind => {
const fileName = this._stories.getStoryFileName(kind);

const stories = this._stories.getStories(kind).map(name => {
const render = this._stories.getStory(kind, name);
return { name, render };
});
return { kind, stories };

return { kind, fileName, stories };
});
}

Expand Down
5 changes: 3 additions & 2 deletions app/react-native/src/preview/story_kind.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/* eslint no-underscore-dangle: 0 */

export default class StoryKindApi {
constructor(stories, addons, decorators, kind) {
constructor(stories, addons, decorators, kind, fileName) {
this.kind = kind;
this._stories = stories;
this._decorators = decorators.slice();
this._fileName = fileName;
Object.assign(this, addons);
}

Expand All @@ -15,7 +16,7 @@ export default class StoryKindApi {

add(story, fn) {
const decorated = this._decorate(fn);
this._stories.addStory(this.kind, story, decorated);
this._stories.addStory(this.kind, story, decorated, this._fileName);
return this;
}

Expand Down
Loading