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

Test-runner file for hook configuration #53

Merged
merged 3 commits into from
Feb 16, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
21 changes: 21 additions & 0 deletions .storybook/test-runner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { toMatchImageSnapshot } from 'jest-image-snapshot';
import type { TestRunnerConfig } from '../dist/ts';

const customSnapshotsDir = `${process.cwd()}/__snapshots__`;

const config: TestRunnerConfig = {
setup() {
expect.extend({ toMatchImageSnapshot });
},
async postRender(page, context) {
const image = await page.screenshot();
expect(image).toMatchImageSnapshot({
customSnapshotsDir,
customSnapshotIdentifier: context.id,
failureThreshold: 0.03,
failureThresholdType: 'percent',
});
},
};

export default config;
59 changes: 34 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ Storybook test runner turns all of your stories into executable tests.
- [2. Running against locally built Storybooks in CI](#2-running-against-locally-built-storybooks-in-ci)
- [Experimental test hook API](#experimental-test-hook-api)
- [Image snapshot recipe](#image-snapshot-recipe)
- [Render lifecycle](#render-lifecycle)
- [Troubleshooting](#troubleshooting)
- [The test runner seems flaky and keeps timing out](#the-test-runner-seems-flaky-and-keeps-timing-out)
- [Adding the test runner to other CI environments](#adding-the-test-runner-to-other-ci-environments)
- [The test runner seems flaky and keeps timing out](#the-test-runner-seems-flaky-and-keeps-timing-out)
- [Adding the test runner to other CI environments](#adding-the-test-runner-to-other-ci-environments)
yannbf marked this conversation as resolved.
Show resolved Hide resolved
- [Future work](#future-work)

## Features
Expand Down Expand Up @@ -255,10 +256,40 @@ The test runner renders a story and executes its [play function](https://storybo

To enable use cases like visual or DOM snapshots, the test runner exports test hooks that can be overridden globally. These hooks give you access to the test lifecycle before and after the story is rendered.

The hooks, `preRender` and `postRender`, are functions that take a [Playwright Page](https://playwright.dev/docs/pages) and a context object with the current story `id`, `title`, and `name`. They are globally settable by `@storybook/test-runner`'s `setPreRender` and `setPostRender` APIs.
There are three hooks: `setup`, `preRender`, and `postRender`. `setup` executes once before all the tests run. `preRender` and `postRender` execute within a test before and after a story is rendered.

The render functions are async functions that receive a [Playwright Page](https://playwright.dev/docs/pages) and a context object with the current story `id`, `title`, and `name`. They are globally settable by `@storybook/test-runner`'s `setPreRender` and `setPostRender` APIs.

All three functions can be set up in the configuration file `.storybook/test-runner.js` which can optionally export any of these functions.

> **NOTE:** These test hooks are experimental and may be subject to breaking changes. We encourage you to test as much as possible within the story's play function.

### Image snapshot recipe

Consider, for example, the following recipe to take image snapshots:

```js
// .storybook/test-runner.js
const { toMatchImageSnapshot } = require('jest-image-snapshot');

const customSnapshotsDir = `${process.cwd()}/__snapshots__`;

module.exports = {
setup() {
expect.extend({ toMatchImageSnapshot });
},
async postRender(page, context) {
const image = await page.screenshot();
expect(image).toMatchImageSnapshot({
customSnapshotsDir,
customSnapshotIdentifier: context.id,
});
},
};
```

yannbf marked this conversation as resolved.
Show resolved Hide resolved
### Render lifecycle

To visualize the test lifecycle, consider a simplified version of the test code automatically generated for each story in your Storybook:

```js
Expand All @@ -280,28 +311,6 @@ it('button--basic', async () => {
});
```

### Image snapshot recipe

If you want to make the test runner take image snapshots, the following recipe uses test hooks in `jest-setup.js` to do it:

```js
const { toMatchImageSnapshot } = require('jest-image-snapshot');
const { setPostRender } = require('@storybook/test-runner');

expect.extend({ toMatchImageSnapshot });

// use custom directory/id to align CSF and stories.json mode outputs
const customSnapshotsDir = `${process.cwd()}/__snapshots__`;

setPostRender(async (page, context) => {
const image = await page.screenshot();
expect(image).toMatchImageSnapshot({
customSnapshotsDir,
customSnapshotIdentifier: context.id,
});
});
```

## Troubleshooting

#### The test runner seems flaky and keeps timing out
Expand Down
17 changes: 0 additions & 17 deletions jest-setup.js

This file was deleted.

14 changes: 14 additions & 0 deletions playwright/jest-setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const { getTestRunnerConfig, setPreRender, setPostRender } = require('../dist/cjs');

const testRunnerConfig = getTestRunnerConfig(process.env.STORYBOOK_CONFIG_DIR);
if (testRunnerConfig) {
if (testRunnerConfig.setup) {
testRunnerConfig.setup();
}
if (testRunnerConfig.preRender) {
setPreRender(testRunnerConfig.preRender);
yannbf marked this conversation as resolved.
Show resolved Hide resolved
}
if (testRunnerConfig.postRender) {
setPostRender(testRunnerConfig.postRender);
}
}
2 changes: 1 addition & 1 deletion src/config/jest-playwright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const getJestConfig = () => {
globalSetup: '@storybook/test-runner/playwright/global-setup.js',
globalTeardown: '@storybook/test-runner/playwright/global-teardown.js',
testEnvironment: '@storybook/test-runner/playwright/custom-environment.js',
// @TODO: setupFilesAfterEnv: ['@storybook/test-runner/setup']
setupFilesAfterEnv: ['@storybook/test-runner/playwright/jest-setup.js'],
};

if (TEST_MATCH) {
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './playwright/hooks';
export * from './config/jest-playwright';
export * from './util/getTestRunnerConfig';
6 changes: 6 additions & 0 deletions src/playwright/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export type TestContext = {

export type TestHook = (page: Page, context: TestContext) => Promise<void>;

export interface TestRunnerConfig {
setup?: () => void;
preRender?: TestHook;
postRender?: TestHook;
}

export const setPreRender = (preRender: TestHook) => {
global.__sbPreRender = preRender;
};
Expand Down
17 changes: 17 additions & 0 deletions src/util/getTestRunnerConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { join, resolve } from 'path';
import { serverRequire } from '@storybook/core-common';
import { TestRunnerConfig } from '../playwright/hooks';

let testRunnerConfig: TestRunnerConfig;
let loaded = false;

export const getTestRunnerConfig = (configDir: string): TestRunnerConfig | undefined => {
// testRunnerConfig can be undefined
if (loaded) {
return testRunnerConfig;
}

testRunnerConfig = serverRequire(join(resolve(configDir), 'test-runner'));
loaded = true;
return testRunnerConfig;
};
1 change: 1 addition & 0 deletions src/util/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './getCliOptions';
export * from './getTestRunnerConfig';
export * from './getStorybookMain';
export * from './getStorybookMetadata';
export * from './getParsedCliOptions';
2 changes: 1 addition & 1 deletion test-runner-jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ module.exports = {
globalSetup: './playwright/global-setup.js',
globalTeardown: './playwright/global-teardown.js',
testEnvironment: './playwright/custom-environment.js',
setupFilesAfterEnv: ['<rootDir>/jest-setup.js'],
setupFilesAfterEnv: ['./playwright/jest-setup.js'],
};