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

Add ability to use image snapshots to addon-storyshots #2413

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b96345b
add jest-image-snapshot to storyshots
Dec 1, 2017
d5ce79e
use custom jest config for storyshots with image snapshots & re-add a…
Dec 28, 2017
edd03a9
clean circleci config for image snapshots
Dec 28, 2017
c7c1ef1
Update image snapshots test config for angular-cli
Dec 28, 2017
6d0abf1
reorder cicleci goals
Dec 28, 2017
5759db9
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Dec 28, 2017
6bea608
Add missing snapshot
Dec 29, 2017
7a74298
Merge branch 'master' into storyshots-add-image-snapshots
ndelangen Dec 29, 2017
724ab4c
improve image snapshots stories, remove deprecated use to please linter
Dec 29, 2017
2076574
Merge branch 'master' into storyshots-add-image-snapshots
ndelangen Dec 30, 2017
03e47af
Merge branch 'master' into storyshots-add-image-snapshots
ndelangen Jan 1, 2018
e9c2090
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 2, 2018
c124dd5
update snapshots with the correct ones
Jan 2, 2018
c798cdf
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 3, 2018
d94fa13
remove transformIgnorePatterns since lodash-es has been reverted
Jan 3, 2018
a63e1d8
put image-storyshots to official-storybook & update scripts
Jan 3, 2018
fcce2cd
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 3, 2018
ddde721
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 4, 2018
5a24d9d
remove unused code & fix image-snapshots script
Jan 4, 2018
1df4c87
Add official-storybook static build to CI
Jan 4, 2018
8a69e8b
re-move addon-jest config & script to official-storybook
Jan 4, 2018
ce9f389
Merge branch 'master' into storyshots-add-image-snapshots
ndelangen Jan 4, 2018
2b2b03c
store image snapshots on CI for official-storybook
Jan 5, 2018
ca8b1cc
re-add coverage config to addon-jest.config.js
Jan 5, 2018
e5b3233
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 5, 2018
6dca7b9
re-add cra-kitchen-sink to CI build for integration tests
Jan 5, 2018
9cae936
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 5, 2018
6a5911f
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 8, 2018
49e0338
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 8, 2018
28cb109
Merge branch 'master' into storyshots-add-image-snapshots
igor-dv Jan 8, 2018
c5a1fa1
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 10, 2018
96a5df5
Add more details in READMEfor image snapshots
Jan 10, 2018
1a4d34c
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 10, 2018
b104897
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 10, 2018
425937a
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 11, 2018
57b06f8
remove extra CI step to build static storybook on unit-test
Jan 11, 2018
797f2b7
Merge branch 'master' into storyshots-add-image-snapshots
thomasbertet Jan 11, 2018
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
31 changes: 19 additions & 12 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ jobs:
name: "Link packages"
command: |
yarn install
- run:
name: Workaround for https://github.com/GoogleChrome/puppeteer/issues/290
command: sh ./scripts/workaround-puppeteer-issue-290.sh
- run:
name: "Build official-storybook"
command: |
cd examples/official-storybook
yarn build-storybook
- run:
name: "Build react kitchen-sink"
command: |
Expand All @@ -73,34 +81,33 @@ jobs:
command: |
cd examples/angular-cli
yarn build-storybook

- run:
name: "Run react kitchen-sink"
name: "Run react kitchen-sink (smoke test)"
command: |
cd examples/cra-kitchen-sink
yarn storybook
background: true
yarn storybook --smoke-test
- run:
name: "Run vue kitchen-sink"
name: "Run vue kitchen-sink (smoke test)"
command: |
cd examples/vue-kitchen-sink
yarn storybook
background: true
yarn storybook --smoke-test
- run:
name: "Run angular-cli"
name: "Run angular-cli (smoke test)"
command: |
cd examples/angular-cli
yarn storybook
background: true
yarn storybook --smoke-test
- run:
name: Workaround for https://github.com/GoogleChrome/puppeteer/issues/290
command: sh ./scripts/workaround-puppeteer-issue-290.sh
name: "Run image snapshots"
command: yarn test --image
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please store image diffs as artifacts (see below, plus docs)

That way it would be much easier to find out why is the CI test failing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, will do !

- run:
name: Integration Test - Kichen sinks
command: yarn test --integration
- store_artifacts:
path: integration/__image_snapshots__
destination: integration_image_snapshots
- store_artifacts:
path: examples/official-storybook/image-snapshots/__image_snapshots__
destination: official_storybook_image_snapshots
react-native:
<<: *defaults
steps:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ package-lock.json
storybook-static
integration/__image_snapshots__/__diff_output__
.jest-test-results.json
/examples/cra-kitchen-sink/src/__image_snapshots__/__diff_output__/
8 changes: 8 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ In order for the snapshot-integration tests to be executed properly, examples be

Puppeteer is used to launch and grab screenshots of example pages, while jest is used to assert matching images.

##### CRA-kitchen-sink - Image snapshots using Storyshots

`yarn test --image`

This option executes tests from `<rootdir>/examples/cra-kitchen-sink`
In order for the image snapshots to be correctly generated, you must have static build of the storybook up-to-date.

Puppeteer is used to launch and grab screenshots of example pages, while jest is used to assert matching images. (just like integration tests)

#### 2b. Run e2e tests for CLI

Expand Down
99 changes: 98 additions & 1 deletion addons/storyshots/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Usually, you might already have completed this step. If not, here are some resou

> Note: If you use React 16, you'll need to follow [these additional instructions](https://github.com/facebook/react/issues/9102#issuecomment-283873039).

## Configure Storyshots
## Configure Storyshots for HTML snapshots

Create 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)).
Then add following content to it:
Expand All @@ -53,6 +53,103 @@ Now run your Jest test command. (Usually, `npm test`.) Then you can see all of y

![Screenshot](docs/storyshots.png)


## Configure Storyshots for image snapshots

/*\ **React-native** is **not supported** by this test function.

Internally, it uses [jest-image-snapshot](https://github.com/americanexpress/jest-image-snapshot).

When willing to generate and compare image snapshots for your stories, you have to two options:
- Have a storybook running (ie. accessible via http(s):// , for instance using `yarn run storybook`)
- Have a static build of the storybook (for instance, using `yarn run build-storybook`)

Then you will need to reference the storybook URL (`file://...` if local, `http(s)://...` if served)

### Using default values for _imageSnapshots_

Then you can either create a new Storyshots instance or edit the one you previously used:
```js
import initStoryshots, { imageSnapshot } from '@storybook/addon-storyshots';

initStoryshots({suite: 'Image storyshots', test: imageSnapshot});
```
This will assume you have a storybook running on at _http://localhost:6006_.
Internally here are the steps:
- Launches a Chrome headless using [puppeteer](https://github.com/GoogleChrome/puppeteer)
- Browses each stories (calling _http://localhost:6006/iframe.html?..._ URL),
- Take screenshots & save all images under _\_image_snapshots\__ folder.

### Specifying the storybook URL

If you want to set specific storybook URL, you can specify via the `storybookUrl` parameter, see below:
```js
import initStoryshots, { imageSnapshot } from '@storybook/addon-storyshots';

initStoryshots({suite: 'Image storyshots', test: imageSnapshot({storybookUrl: 'http://my-specific-domain.com:9010'})});
```
The above config will use _https://my-specific-domain.com:9010_ for screenshots.


You may also use a local static build of storybook if you do not want to run the webpack dev-server:
```js
import initStoryshots, { imageSnapshot } from '@storybook/addon-storyshots';

initStoryshots({suite: 'Image storyshots', test: imageSnapshot({storybookUrl: 'file:///path/to/my/storybook-static'})});
```

### Specifying options to _jest-image-snapshots_

If 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.
```js
import initStoryshots, { imageSnapshot } from '@storybook/addon-storyshots';
const getMatchOptions = ({context : {kind, story}, url}) => {
return {
failureThreshold: 0.2,
failureThresholdType: 'percent',
}
}
initStoryshots({suite: 'Image storyshots', test: imageSnapshot({storybookUrl: 'http://localhost:6006', getMatchOptions})});
```
`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.


### Integrate image storyshots with regular app
You may want to use another Jest project to run your image snapshots as they require more resources: Chrome and Storybook built/served.
You can find a working example of this in the [official-storybook](https://github.com/storybooks/storybook/tree/master/examples/official-storybook) example.

### Integrate image storyshots with [Create React App](https://github.com/facebookincubator/create-react-app)
You have two options here, you can either:

- Simply 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.

- Create a custom test file using Jest outside of the CRA scope:

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 storyshots with image snapshots.
This use case can be achieved by using a custom name for the test file, ie something like `image-storyshots.runner.js`. This file will contains the `initStoryshots` call with image snapshots configuration.
Then you will create a separate script entry in your package.json, for instance
```json
{
"scripts": {
"image-snapshots" : "jest image-storyshots.runner.js --config path/to/custom/jest.config.json"
}
}
```
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.

Once that's setup, you can run `yarn run image-snapshots` (or `npm run image-snapshots`).

### Reminder
An image snapshot is simply a screenshot taken by a web browser (in our case, Chrome).

The browser opens a page (either using the static build of storybook or a running instance of Storybook)

If you run your test without either the static build or a running instance, this wont work.

To make sure your screenshots are taken from latest changes of your Storybook, you must keep your static build or running Storybook up-to-date.
This can be achieved by adding a step before running the test ie: `yarn run build-storybook && yarn run image-snapshots`.
If you run the image snapshots against a running Storybook in dev mode, you don't have to care about being up-to-date because the dev-server is watching changes and rebuilds automatically.

## Options

### `configPath`
Expand Down
2 changes: 2 additions & 0 deletions addons/storyshots/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
"glob": "^7.1.2",
"global": "^4.3.2",
"jest-specific-snapshot": "^0.3.0",
"jest-image-snapshot": "^2.2.0",
"prop-types": "^15.6.0",
"puppeteer": "^0.13.0",
"read-pkg-up": "^3.0.0"
},
"devDependencies": {
Expand Down
23 changes: 19 additions & 4 deletions addons/storyshots/src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'path';
import fs from 'fs';
import glob from 'glob';
import global, { describe, it } from 'global';
import global, { describe, it, beforeEach, afterEach } from 'global';
import readPkgUp from 'read-pkg-up';
import addons from '@storybook/addons';

Expand All @@ -18,6 +18,8 @@ export {
renderOnly,
} from './test-bodies';

export { imageSnapshot } from './test-body-image-snapshot';

export { getSnapshotFileName };

let storybook;
Expand Down Expand Up @@ -100,6 +102,20 @@ export default function testStorySnapshots(options = {}) {
}

describe(suite, () => {
beforeEach(() => {
if (typeof options.test.beforeEach === 'function') {
return options.test.beforeEach();
}
return Promise.resolve();
});

afterEach(() => {
if (typeof options.test.afterEach === 'function') {
return options.test.afterEach();
}
return Promise.resolve();
});

describe(kind, () => {
// eslint-disable-next-line
for (const story of group.stories) {
Expand All @@ -109,7 +125,7 @@ export default function testStorySnapshots(options = {}) {
}

it(story.name, () => {
const context = { fileName, kind, story: story.name };
const context = { fileName, kind, story: story.name, isRNStorybook };
return options.test({
story,
context,
Expand All @@ -122,14 +138,13 @@ export default function testStorySnapshots(options = {}) {
}

describe('Storyshots Integrity', () => {
describe('Abandoned Storyshots', () => {
test('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);
});
});
67 changes: 67 additions & 0 deletions addons/storyshots/src/test-body-image-snapshot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import puppeteer from 'puppeteer';
import { toMatchImageSnapshot } from 'jest-image-snapshot';

expect.extend({ toMatchImageSnapshot });

export const imageSnapshot = ({
storybookUrl = 'http://localhost:6006',
getMatchOptions = () => {},
}) => {
let browser; // holds ref to browser. (ie. Chrome)
let page; // Hold ref to the page to screenshot.

const testFn = ({ context }) => {
if (context.isRNStorybook) {
// Skip tests since we de not support RN image snapshots.
console.error(
"It seems you are running imageSnapshot on RN app and it's not supported. Skipping test."
);
return Promise.resolve();
}

const encodedKind = encodeURIComponent(context.kind);
const encodedStoryName = encodeURIComponent(context.story);
const storyUrl = `/iframe.html?selectedKind=${encodedKind}&selectedStory=${encodedStoryName}`;
const url = storybookUrl + storyUrl;
if (!browser || !page) {
console.error(
`Error when generating image snapshot for test ${context.kind} - ${
context.story
} : It seems the headless browser is not running.`
);
return Promise.reject(new Error('no-headless-browser-running'));
}

expect.assertions(1);
return page
.goto(url)
.catch(e => {
console.error(
`ERROR WHILE CONNECTING TO ${url}, did you start or build the storybook first ? A storybook instance should be running or a static version should be built when using image snapshot feature.`,
e
);
throw e;
})
.then(() =>
page.screenshot().then(image => {
expect(image).toMatchImageSnapshot(getMatchOptions({ context, url }));
})
);
};

testFn.beforeEach = () =>
puppeteer
// add some options "no-sandbox" to make it work properly on some Linux systems as proposed here: https://github.com/Googlechrome/puppeteer/issues/290#issuecomment-322851507
.launch({ args: ['--no-sandbox ', '--disable-setuid-sandbox'] })
.then(b => {
browser = b;
})
.then(() => browser.newPage())
.then(p => {
page = p;
});

testFn.afterEach = () => browser.close();

return testFn;
};
1 change: 0 additions & 1 deletion examples/cra-kitchen-sink/README.md

This file was deleted.

1 change: 0 additions & 1 deletion examples/cra-kitchen-sink/__mocks__/fileMock.js

This file was deleted.

1 change: 0 additions & 1 deletion examples/cra-kitchen-sink/__mocks__/styleMock.js

This file was deleted.

28 changes: 0 additions & 28 deletions examples/cra-kitchen-sink/config-addon-jest.json

This file was deleted.

1 change: 0 additions & 1 deletion examples/cra-kitchen-sink/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"build": "react-scripts build",
"build-storybook": "build-storybook -s public",
"eject": "react-scripts eject",
"generate-addon-jest-testresults": "jest src/stories/addon-jest.test.js --config=config-addon-jest.json --env=jsdom --json --outputFile=src/stories/addon-jest.testresults.json",
"start": "react-scripts start",
"storybook": "start-storybook -p 9010 -s public",
"test": "react-scripts test --env=jsdom"
Expand Down
4 changes: 0 additions & 4 deletions examples/cra-kitchen-sink/src/enzyme.js

This file was deleted.

Loading