Storybook 6.5 testing updates

This commit is contained in:
jonniebigodes 2022-04-14 19:08:57 +01:00
parent 22336259d4
commit c5c64b4984
62 changed files with 866 additions and 103 deletions

View File

@ -5,6 +5,8 @@ import { Canvas, Meta, Story } from '@storybook/addon-docs';
import { within, userEvent } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import { LoginForm } from './LoginForm.component';
<Meta title="Form" component={LoginForm} />
@ -22,17 +24,26 @@ export const Template = (args) => ({ props: args });
// Starts querying the component from its root element
const canvas = within(canvasElement);
// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com', {
delay: 100,
});
await userEvent.type(canvas.getByTestId('password'), 'a-random-password', {
delay: 100,
});
// See https://storybook.js.org/docs/angular/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
}}>
{Template.bind({})}
</Story>
</Canvas>
```

View File

@ -5,6 +5,8 @@ import { Meta, Story } from '@storybook/angular';
import { userEvent, within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import { LoginForm } from './LoginForm.component';
export default {
@ -27,14 +29,23 @@ FilledForm.play = async ({ canvasElement }) => {
// Starts querying the component from its root element
const canvas = within(canvasElement);
// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com', {
delay: 100,
});
await userEvent.type(canvas.getByTestId('password'), 'a-random-password', {
delay: 100,
});
// See https://storybook.js.org/docs/angular/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
};
```

View File

@ -8,7 +8,7 @@ describe('Login Form', () => {
cy.visit('/iframe.html?id=components-login-form--example');
cy.get('#login-form').within(() => {
cy.log('**enter the email**');
cy.get('#email').should('have.value', 'your-own-emailaddress@provider.com');
cy.get('#email').should('have.value', 'email@provider.com');
cy.log('**enter password**');
cy.get('#password').should('have.value', 'a-random-password');
});

View File

@ -7,7 +7,7 @@ test('Login Form inputs', async ({ page }) => {
await page.goto('http://localhost:6006/iframe.html?id=components-login-form--example');
const email = await page.inputValue('#email');
const password = await page.inputValue('#password');
await expect(email).toBe('your-own-emailaddress@provider.com');
await expect(email).toBe('email@provider.com');
await expect(password).toBe('a-random-password');
});
```

View File

@ -0,0 +1,3 @@
```shell
npm install @storybook/addon-a11y --save-dev
```

View File

@ -0,0 +1,3 @@
```shell
yarn add --dev @storybook/addon-a11y
```

View File

@ -2,11 +2,11 @@
// .storybook/main.js
module.exports = {
stories:[],
addons:[
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/preset-create-react-app',
'@storybook/addon-a11y', //👈 The a11y addon goes here
],
};
```
```

View File

@ -0,0 +1,17 @@
```ts
// .storybook/main.ts
// Imports Storybook's configuration API
import type { StorybookConfig } from '@storybook/core-common';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-a11y', //👈 The a11y addon goes here
],
};
module.exports = config;
```

View File

@ -0,0 +1,3 @@
```shell
npm install @storybook/testing-library @storybook/jest @storybook/addon-interactions --save-dev
```

View File

@ -0,0 +1,3 @@
```shell
yarn add --dev @storybook/testing-library @storybook/jest @storybook/addon-interactions
```

View File

@ -0,0 +1,14 @@
```js
// .storybook/main.js
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
// Other Storybook addons
'@storybook/addon-interactions', // 👈 Addon is registered here
],
features: {
interactionsDebugger: true, // 👈 Enable playback controls
},
};
```

View File

@ -0,0 +1,19 @@
```ts
// .storybook/main.ts
// Imports Storybook's configuration API
import type { StorybookConfig } from '@storybook/core-common';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
// Other Storybook addons
'@storybook/addon-interactions', // 👈 Addon is registered here
],
features: {
interactionsDebugger: true, // 👈 Enable playback controls
},
};
module.exports = config;
```

View File

@ -1,4 +1,3 @@
```shell
# Starts Storybook in development mode
npm run storybook
```

View File

@ -1,4 +1,3 @@
```shell
# Starts Storybook in development mode
yarn storybook
```

View File

@ -0,0 +1,22 @@
```js
// .storybook/test-runner.js
const { injectAxe, checkA11y } = require('axe-playwright');
module.exports = {
/* Hook to execute before a story is rendered.
* The page argument is the Playwright's page object for the story.
* The context argument is a Storybook object containing the story's id, title, and name.
*/
async preRender(page, context) {
await injectAxe(page);
},
/* Hook to execute after a story is rendered.
* The page argument is the Playwright's page object for the story
* The context argument is a Storybook object containing the story's id, title, and name.
*/
async postRender(page, context) {
await checkA11y(page, context.title.includes('Page') ? undefined : '#root');
},
};
```

View File

@ -0,0 +1,26 @@
```ts
// .storybook/test-runner.ts
import { injectAxe, checkA11y } from 'axe-playwright';
import type { TestRunnerConfig } from '@storybook/test-runner';
const a11yConfig: TestRunnerConfig = {
/* Hook to execute before a story is rendered.
* The page argument is the Playwright's page object for the story.
* The context argument is a Storybook object containing the story's id, title, and name.
*/
async preRender(page, context) {
await injectAxe(page);
},
/* Hook to execute after a story is rendered.
* The page argument is the Playwright's page object for the story
* The context argument is a Storybook object containing the story's id, title, and name.
*/
async postRender(page, context) {
await checkA11y(page, context.title.includes('Page') ? undefined : '#root');
},
};
module.exports = a11yConfig;
```

View File

@ -0,0 +1,3 @@
```shell
npm install axe-playwright --save-dev
```

View File

@ -0,0 +1,3 @@
```shell
yarn add --dev axe-playwright
```

View File

@ -0,0 +1,3 @@
```shell
npm run test-storybook -- --no-stories-json
```

View File

@ -0,0 +1,3 @@
```shell
yarn test-storybook --no-stories-json
```

View File

@ -0,0 +1,3 @@
```shell
Timeout - Async callback was not invoked within the 15000 ms timeout specified by jest.setTimeout
```

View File

@ -0,0 +1,3 @@
```shell
npm run test-storybook -- --watch
```

View File

@ -0,0 +1,3 @@
```shell
yarn test-storybook --watch
```

View File

@ -0,0 +1,3 @@
```shell
TARGET_URL=https://the-storybook-url-here.com yarn test-storybook
```

View File

@ -0,0 +1,3 @@
```shell
npm run test-storybook -- --url https://the-storybook-url-here.com
```

View File

@ -0,0 +1,3 @@
```shell
yarn test-storybook --url https://the-storybook-url-here.com
```

View File

@ -0,0 +1,3 @@
```shell
npm run test-storybook
```

View File

@ -0,0 +1,3 @@
```shell
yarn test-storybook
```

View File

@ -0,0 +1,24 @@
```js
// .storybook/test-runner.js
module.exports = {
// Hook to execute once the test runner is ready
setup() {
// Add your configuration here.
},
/* Hook to execute before a story is rendered.
* The page argument is the Playwright's page object for the story.
* The context argument is a Storybook object containing the story's id, title, and name.
*/
async preRender(page, context) {
// Add your configuration here.
},
/* Hook to execute after a story is rendered.
* The page argument is the Playwright's page object for the story
* The context argument is a Storybook object containing the story's id, title, and name.
*/
async postRender(page, context) {
// Add your configuration here.
},
};
```

View File

@ -0,0 +1,28 @@
```ts
// .storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner';
const config: TestRunnerConfig = {
// Hook to execute once the test runner is ready
setup() {
// Add your configuration here.
},
/* Hook to execute before a story is rendered.
* The page argument is the Playwright's page object for the story.
* The context argument is a Storybook object containing the story's id, title, and name.
*/
async preRender(page, context) {
// Add your configuration here.
},
/* Hook to execute after a story is rendered.
* The page argument is the Playwright's page object for the story
* The context argument is a Storybook object containing the story's id, title, and name.
*/
async postRender(page, context) {
// Add your configuration here.
},
};
module.exports = config;
```

View File

@ -0,0 +1,3 @@
```shell
npm install @storybook/test-runner jest --save-dev
```

View File

@ -0,0 +1,3 @@
```shell
yarn add --dev @storybook/test-runner jest
```

View File

@ -0,0 +1,26 @@
```yml
# .github/workflows/storybook-tests.yml
name: 'Storybook Tests'
on: push
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14.x'
- name: Install dependencies
run: yarn
- name: Install Playwright
run: npx playwright install --with-deps
- name: Build Storybook
run: yarn build-storybook --quiet
- name: Serve Storybook and run tests
run: |
npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
"npx http-server storybook-static --port 6006 --silent" \
"npx wait-on tcp:6006 && yarn test-storybook"
```

View File

@ -0,0 +1,24 @@
```yml
# .github/workflows/storybook-tests.yml
name: Storybook Tests
on: deployment_status
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
if: github.event.deployment_status.state == 'success'
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14.x'
- name: Install dependencies
run: yarn
- name: Install Playwright
run: npx playwright install --with-deps
- name: Run Storybook tests
run: yarn test-storybook
env:
TARGET_URL: '${{ github.event.deployment_status.target_url }}'
```

View File

@ -0,0 +1,3 @@
```shell
npm run test-storybook -- --stories-json
```

View File

@ -0,0 +1,3 @@
```shell
yarn test-storybook --stories-json
```

View File

@ -0,0 +1,3 @@
```shell
npm install --save-dev @storybook/testing-( react | vue | vue3 | angular)
```

View File

@ -0,0 +1,3 @@
```shell
yarn add --dev @storybook/testing-( react | vue | vue3 | angular )
```

View File

@ -1,23 +0,0 @@
```js
// MyComponent.test.js
import { render } from '@testing-library/react';
import { composeStories } from '@storybook/testing-react';
import { axe, toHaveNoViolations } from 'jest-axe';
import * as MyComponentStories from './MyComponent.stories';
const { Accessible } = composeStories(MyComponentStories);
expect.extend(toHaveNoViolations);
test('Example accessibility test', async () => {
const { container } = render(<Accessible />);
const AxeResults = await axe(container);
expect(AxeResults).toHaveNoViolations();
});
```

View File

@ -5,6 +5,8 @@ import React from 'react';
import { within, userEvent } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import { LoginForm } from './LoginForm';
export default {
@ -25,6 +27,7 @@ FilledForm.play = async ({ canvasElement }) => {
// Starts querying the component from its root element
const canvas = within(canvasElement);
// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com', {
delay: 100,
});
@ -34,5 +37,12 @@ FilledForm.play = async ({ canvasElement }) => {
// See https://storybook.js.org/docs/react/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
};
```
```

View File

@ -5,6 +5,8 @@ import { Canvas, Meta, Story } from '@storybook/addon-docs';
import { within, userEvent } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import { LoginForm } from './LoginForm';
<Meta title="Form" component={LoginForm} />
@ -22,16 +24,26 @@ export const Template = (args) => <LoginForm {...args} />;
// Starts querying the component from its root element
const canvas = within(canvasElement);
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com', {
delay: 100,
});
await userEvent.type(canvas.getByTestId('password'), 'a-random-password', {
delay: 100,
});
// See https://storybook.js.org/docs/react/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
}}>
{Template.bind({})}
// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com', {
delay: 100,
});
await userEvent.type(canvas.getByTestId('password'), 'a-random-password', {
delay: 100,
});
// See https://storybook.js.org/docs/react/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
}}>
{Template.bind({})}
</Story>
</Canvas>
```

View File

@ -7,6 +7,8 @@ import { ComponentStory, ComponentMeta } from '@storybook/react';
import { within, userEvent } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import { LoginForm } from './LoginForm';
export default {
@ -27,6 +29,7 @@ FilledForm.play = async ({ canvasElement }) => {
// Starts querying the component from its root element
const canvas = within(canvasElement);
// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com', {
delay: 100,
});
@ -36,5 +39,12 @@ FilledForm.play = async ({ canvasElement }) => {
// See https://storybook.js.org/docs/react/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
};
```

View File

@ -3,6 +3,8 @@
import { within, userEvent } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import LoginForm from './LoginForm.svelte';
export default {
@ -26,14 +28,23 @@ FilledForm.play = async ({ canvasElement }) => {
// Starts querying the component from its root element
const canvas = within(canvasElement);
// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com', {
delay: 100,
});
await userEvent.type(canvas.getByTestId('password'), 'a-random-password', {
delay: 100,
});
// See https://storybook.js.org/docs/svelte/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
};
```

View File

@ -5,6 +5,8 @@ import { Canvas, Meta, Story } from '@storybook/addon-docs';
import { within, userEvent } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import LoginForm from './LoginForm.svelte';
<Meta title="Form" component={LoginForm} />
@ -22,19 +24,27 @@ export const Template = (args) => ({
<Story
name="Filled Form"
play={async ({ canvasElement }) => {
// Starts querying the component from its root element
const canvas = within(canvasElement);
// 👇 Simulate interactions
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com', {
delay: 100,
});
await userEvent.type(canvas.getByTestId('password'), 'a-random-password', {
delay: 100,
});
// See https://storybook.js.org/docs/svelte/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
}}>
{Template.bind({})}
</Story>

View File

@ -1,23 +0,0 @@
```js
// MyComponent.test.js
import { render } from '@testing-library/vue';
import { composeStories } from '@storybook/testing-vue';
import { axe, toHaveNoViolations } from 'jest-axe';
import * as MyComponentStories from './MyComponent.stories';
const { Accessible } = composeStories(MyComponentStories);
expect.extend(toHaveNoViolations);
test('Example accessibility test', async () => {
const { container } = render(Accessible());
const AxeResults = await axe(container);
expect(AxeResults).toHaveNoViolations();
});
```

View File

@ -3,6 +3,8 @@
import { userEvent, within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import LoginForm from './LoginForm.vue';
export default {
@ -27,14 +29,23 @@ FilledForm.play = async ({ canvasElement }) => {
// Starts querying the component from its root element
const canvas = within(canvasElement);
// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com', {
delay: 100,
});
await userEvent.type(canvas.getByTestId('password'), 'a-random-password', {
delay: 100,
});
// See https://storybook.js.org/docs/vue/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
};
```

View File

@ -3,6 +3,8 @@
import { userEvent, within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import LoginForm from './LoginForm.vue';
export default {
@ -29,14 +31,23 @@ FilledForm.play = async ({ canvasElement }) => {
// Starts querying the component from its root element
const canvas = within(canvasElement);
// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com', {
delay: 100,
});
await userEvent.type(canvas.getByTestId('password'), 'a-random-password', {
delay: 100,
});
// See https://storybook.js.org/docs/vue/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
};
```

View File

@ -5,6 +5,8 @@ import { Canvas, Meta, Story } from '@storybook/addon-docs';
import { userEvent, within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import LoginForm from './LoginForm.vue';
<Meta title="Form" component={LoginForm} />
@ -17,7 +19,7 @@ export const Template = (args, { argTypes }) => ({
<Canvas>
<Story name="Empty Form">
{Template.bind({})}
{Template.bind(())}
</Story>
<Story
@ -26,15 +28,24 @@ export const Template = (args, { argTypes }) => ({
// Starts querying the component from its root element
const canvas = within(canvasElement);
// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com', {
delay: 100,
});
await userEvent.type(canvas.getByTestId('password'), 'a-random-password', {
delay: 100,
});
// See https://storybook.js.org/docs/vue/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
// See https://storybook.js.org/docs/angular/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
}}>
{Template.bind({})}
</Story>

View File

@ -5,6 +5,8 @@ import { Canvas, Meta, Story } from '@storybook/addon-docs';
import { userEvent, within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import LoginForm from './LoginForm.vue';
<Meta title="Form" component={LoginForm} />
@ -22,21 +24,35 @@ export const Template = (args) => ({
{Template.bind({})}
</Story>
<Canvas>
<Story name="Empty Form">
{Template.bind(())}
</Story>
<Story
name="Filled Form"
play={async ({ canvasElement }) => {
// Starts querying the component from its root element
const canvas = within(canvasElement);
// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com', {
delay: 100,
});
await userEvent.type(canvas.getByTestId('password'), 'a-random-password', {
delay: 100,
});
// See https://storybook.js.org/docs/vue/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
// See https://storybook.js.org/docs/angular/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
}}>
{Template.bind({})}
</Story>

View File

@ -179,6 +179,11 @@ module.exports = {
title: 'Introduction',
type: 'link',
},
{
pathSegment: 'test-runner',
title: 'Test runner',
type: 'link',
},
{
pathSegment: 'visual-testing',
title: 'Visual tests',

View File

@ -6,29 +6,37 @@ Accessibility is the practice of making websites inclusive to all. That means su
Accessibility tests audit the rendered DOM against a set of heuristics based on [WCAG](https://www.w3.org/WAI/standards-guidelines/wcag/) rules and other industry-accepted best practices. They act as the first line of QA to catch blatant accessibility violations.
Storybooks official [a11y addon](https://storybook.js.org/addons/@storybook/addon-a11y) runs accessibility audits while youre developing components to give you a fast feedback loop. It's powered by Deque's [axe-core](https://github.com/dequelabs/axe-core), which automatically catches up to [57% of WCAG issues](https://www.deque.com/blog/automated-testing-study-identifies-57-percent-of-digital-accessibility-issues/).
![Accessibility testing](./accessibility-testing-storybook.gif)
## Setup a11y addon
## Accessibility testing with a11y addon
To enable accessibility testing with Storybook, you'll need to install the [`@storybook/addon-a11y`](https://storybook.js.org/addons/@storybook/addon-a11y/) addon. Run the following command:
Storybook provides an official [a11y addon](https://storybook.js.org/addons/@storybook/addon-a11y). Powered by Deque's [axe-core](https://github.com/dequelabs/axe-core), which automatically catches up to [57% of WCAG issues](https://www.deque.com/blog/automated-testing-study-identifies-57-percent-of-digital-accessibility-issues/).
```shell
# With npm
npm install @storybook/addon-a11y --save-dev
### Set up the a11y addon
# With yarn
yarn add --dev @storybook/addon-a11y
```
If you want to run accessibility tests using the [addon](https://storybook.js.org/addons/@storybook/addon-a11y/), you'll need additional steps to set it up. Documented below is our recommendation.
Update your Storybook configuration (in `.storybook/main.js`) to include the accessibility addon:
Run the following command to install the addon.
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-a11y-install.yarn.js.mdx',
'common/storybook-a11y-install.npm.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
Update your Storybook configuration (in `.storybook/main.js|ts`) to include the accessibility addon.
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-a11y-register.js.mdx',
'common/storybook-a11y-register.ts.mdx',
]}
/>
@ -73,7 +81,7 @@ Cycling through both stories, you will see that the `Inaccessible` story contain
### Configure
Out of the box, Storybook's accessibility addon includes a set of accessibility rules that cover most issues. You can also fine tune the [addon configuration](https://github.com/storybookjs/storybook/tree/next/addons/a11y#parameters) or override [Axe's ruleset](https://github.com/storybookjs/storybook/tree/next/addons/a11y#handling-failing-rules) to best suit your needs.
Out of the box, Storybook's accessibility addon includes a set of accessibility rules that cover most issues. You can also fine-tune the [addon configuration](https://github.com/storybookjs/storybook/tree/next/addons/a11y#parameters) or override [Axe's ruleset](https://github.com/storybookjs/storybook/tree/next/addons/a11y#handling-failing-rules) to best suit your needs.
#### Global a11y configuration
@ -148,35 +156,61 @@ Disable accessibility testing for stories or components by adding the following
<!-- prettier-ignore-end -->
## Automate accessibility tests with Jest
## Automate accessibility tests with test runner
Accessibility testing with Storybook shortens the feedback loop which means you fix issues faster. Reuse stories in a Jest test and run an accessibility audit on them using the [jest-axe integration](https://github.com/nickcolley/jest-axe). That also unlocks the ability to integrate accessibility tests into your functional testing pipeline.
Accessibility testing with Storybook shortens the feedback loop, which means you fix issues faster. You can then integrate these accessibility tests into your test automation pipeline using the Storybook [test runner](./test-runner.md#test-hook-api-experimental) and [axe-playwright](https://github.com/abhinaba-ghosh/axe-playwright).
For example, include the following test file to run an accessibility test on a story:
### Setup
To enable accessibility testing with the test runner, you will need to take additional steps to set it up properly. Detailed below is our recommendation to configure and execute them.
Run the following command to install the required dependencies.
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'react/accessibility-testing-with-jest-axe.js.mdx',
'vue/accessibility-testing-with-jest-axe.js.mdx',
'common/storybook-test-runner-axe-playwright.yarn.js.mdx',
'common/storybook-test-runner-axe-playwright.npm.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
When you execute your test script, it will run the accessibility audit along with any interaction tests you might have.
Add a new [configuration file](./test-runner.md#test-hook-api-experimental) inside your Storybook directory with the following inside:
![Accessibility testing with Jest Axe Core](./jest-accessibility-testing-optimized.png)
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-runner-a11y-config.js.mdx',
'common/storybook-test-runner-a11y-config.ts.mdx',
]}
/>
<!-- prettier-ignore-end -->
<div class="aside">
💡 `preRender` and `postRender` are convenient hooks that allow you to extend the test runner's default configuration. They are **experimental** and subject to changes. Read more about them [here](./test-runner.md#test-hook-api-experimental).
</div>
By default, Axe assumes that you're testing a page and checks whether you've specified an `<h1>` and `<main>`. However, most of your stories are for components and not pages. That's why we use the `context.title.includes('Page')` check to [enable/disable](https://github.com/abhinaba-ghosh/axe-playwright#context-optional) Axe's page-level rules.
When you execute the test runner (i.e., `yarn test-storybook`), it will run the accessibility audit and any [interaction tests](./interaction-testing.md) you might have configured for each component story.
![Accessibility testing with the test runner](./test-runner-a11y-optimized.png)
---
#### Whats the difference between browser-based and linter-based accessibility tests?
Browser-based accessibility tests, like found in Storybook, evaluates the rendered DOM because that gives you the highest accuracy. Auditing code that hasn't been compiled yet is one step removed from the real thing so you won't catch everything the user might experience.
Browser-based accessibility tests, like found in Storybook, evaluate the rendered DOM because that gives you the highest accuracy. Auditing code that hasn't been compiled yet is one step removed from the real thing, so you won't catch everything the user might experience.
#### Learn about other UI tests
- [Test runner](./test-runner.md) to automate test execution
- [Visual tests](./visual-testing.md) for appearance
- Accessibility tests for accessibility
- [Interaction tests](./interaction-testing.md) for user behavior simulation

View File

@ -21,13 +21,22 @@ Storybook has test addons for core frameworks React, Vue (2,3), and Angular. Thi
Run the following command to add Storybook's testing addon into your environment:
```shell
# With npm, don't forget to select only your framework
npm install --save-dev @storybook/testing-( react | vue | vue3 | angular)
<!-- prettier-ignore-start -->
# With yarn, don't forget to select only your framework
yarn add --dev @storybook/testing-( react | vue | vue3 | angular )
```
<CodeSnippets
paths={[
'common/storybook-testing-addon-install.yarn.js.mdx',
'common/storybook-testing-addon-install.npm.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
<div class="aside">
💡 When running the command to install the addon, don't forget to select **only** your framework.
</div>
### Optional configuration
@ -171,6 +180,7 @@ Once you execute Playwright, it opens a new browser window, loads Storybook's is
#### Learn about other UI tests
- [Test runner](./test-runner.md) to automate test execution
- [Visual tests](./visual-testing.md) for appearance
- [Accessibility tests](./accessibility-testing.md) for accessibility
- [Interaction tests](./interaction-testing.md) for user behavior simulation

View File

@ -6,17 +6,52 @@ As you build more complex UIs like pages, components become responsible for more
In a nutshell, you start by supplying the appropriate props for the initial state of a component. Then simulate user behavior such as clicks and form entries. Finally, check whether the UI and component state update correctly.
In Storybook, this familiar workflow happens in your browser. That makes it easier to debug failures because you're running tests in the same environment as you develop components—the browser.
![Storybook interaction testing](./storybook-interaction-tests.gif)
## Setup interactions addon
## How does component testing in Storybook work?
You can set up interaction testing in Storybook using the `play` function and [`@storybook/addon-interactions`](https://storybook.js.org/addons/@storybook/addon-interactions/).
You start by writing a [**story**](../writing-stories/introduction.md) to set up the component's initial state. Then simulate user behavior using the **play** function. Finally, use the Storybook **test-runner** to verify the DOM structure. Additionally, you can automate test execution via the [command line](./test-runner.md#cli-options) or in your [CI environment](./test-runner.md#set-up-ci-to-run-tests).
- The [`play`](../writing-stories/play-function.md) function is a small snippet of code that runs after a story finishes rendering. You can use this to test user workflows.
- The test is written using Storybook-instrumented versions of [Jest](https://jestjs.io/) and [Testing Library](https://testing-library.com/).
- [`@storybook/addon-interactions`](https://storybook.js.org/addons/@storybook/addon-interactions/) visualizes the test in Storybook and playback interface for convenient browser-based debugging.
- [`@storybook/test-runner`](https://github.com/storybookjs/test-runner) is a standalone utility—powered by [Playwright](https://playwright.dev/)—that executes all your interactions tests and catches broken stories.
- [`@storybook/addon-interactions`](/addons/@storybook/addon-interactions/) includes helper utilities and a playback interface that simulates user behavior in the browser. Its powered Testing Library and includes convenient instrumentation for debugging.
## Set up the interactions addon
Here's an example of how to set up interaction testing in Storybook with the `play` function:
To enable interaction testing with Storybook, you'll need to take additional steps to set it up properly. We recommend you go through the [test runner documentation](./test-runner.md) before proceeding with the rest of the required configuration.
Run the following command to install the interactions addon and related dependencies.
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-addon-interactions-addon-full-install.yarn.js.mdx',
'common/storybook-addon-interactions-addon-full-install.npm.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
Update your Storybook configuration (in `.storybook/main.js|ts`) to include the interactions addon and enable playback controls for debugging.
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-main-enable-interactive-debugger.js.mdx',
'common/storybook-main-enable-interactive-debugger.ts.mdx',
]}
/>
<!-- prettier-ignore-end -->
## Write an interaction test
The test itself is defined inside a `play` function connected to a story. Here's an example of how to set up an interaction test with Storybook and the `play` function:
<!-- prettier-ignore-start -->
@ -49,7 +84,7 @@ Once the story loads in the UI, it simulates the user's behavior and verifies th
## API for user-events
Under the hood, Storybooks interaction addon mirrors Testing Librarys `user-events` API. If youre familiar with [Testing Library](https://testing-library.com/) you should be at home in Storybook.
Under the hood, Storybooks interaction addon mirrors Testing Librarys `user-events` API. If youre familiar with [Testing Library](https://testing-library.com/), you should be at home in Storybook.
Below is an abridged API for user-event. For more, check out the [official user-event docs](https://testing-library.com/docs/ecosystem-user-event/).
@ -65,6 +100,17 @@ Below is an abridged API for user-event. For more, check out the [official user-
| `type` | Writes text inside inputs, or textareas <br/>`userEvent.type(await within(canvasElement).getByRole('my-input'),'Some text');` |
| `unhover` | Unhovers out of element <br/>`userEvent.unhover(await within(canvasElement).getByLabelText(/Example/i));` |
### Interactive debugger
If you check your interactions panel, you'll see the step-by-step flow. It also offers a handy set of UI controls to pause, resume, rewind, and step through each interaction.
<video autoPlay muted playsInline loop>
<source
src="addon-interactions-playback-controls-optimized.mp4"
type="video/mp4"
/>
</video>
### Permalinks for reproductions
The `play` function is executed after the story is rendered. If theres an error, itll be shown in the interaction addon panel to help with debugging.
@ -75,6 +121,33 @@ Since Storybook is a webapp, anyone with the URL can reproduce the error with th
Streamline interaction testing further by automatically [publishing Storybook](../sharing/publish-storybook.md) in pull requests. That gives teams a universal reference point to test and debug stories.
## Execute tests with the test-runner
Storybook only runs the interaction test when you're viewing a story. Therefore, you'd have to go through each story to run all your checks. As your Storybook grows, it becomes unrealistic to review each change manually. Storybook [test-runner](https://github.com/storybookjs/test-runner) automates the process by running all tests for you. To execute the test-runner, open a new terminal window and run the following command:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-runner-execute.yarn.js.mdx',
'common/storybook-test-runner-execute.npm.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
![Interaction test with test runner](./storybook-interaction-test-runner-loginform-optimized.png)
<div class="aside">
💡 If you need, you can provide additional flags to the test-runner. Read the [documentation](./test-runner.md#cli-options) to learn more.
</div>
## Automate
Once you're ready to push your code into a pull request, you'll want to automatically run all your checks using a Continuous Integration (CI) service before merging it. Read our [documentation](./test-runner.md#set-up-ci-to-run-tests) for a detailed guide on setting up a CI environment to run tests.
---
#### Whats the difference between interaction tests and visual tests?
@ -83,6 +156,7 @@ Interaction tests can be expensive to maintain when applied wholesale to every c
#### Learn about other UI tests
- [Test runner](./test-runner.md) to automate test execution
- [Visual tests](./visual-testing.md) for appearance
- [Accessibility tests](accessibility-testing.md) for accessibility
- Interaction tests for user behavior simulation

View File

@ -10,8 +10,11 @@ That means stories are a pragmatic starting point for your UI testing strategy.
The simplest testing method is manual “spot checking”. You run Storybook locally, then eyeball every story to verify its appearance and behavior. [Publish](../sharing/publish-storybook.md) your Storybook online to share reproductions and get teammates involved.
Storybook also comes with tools, test runners, and handy integrations with the larger JavaScript ecosystem to expand your UI test coverage. These docs detail how you can use Storybook for UI testing.
To test a component in isolation, you often have to mock data, dependencies or even network requests. Check out our guide on [mocking in Storybook](../writing-stories/build-pages-with-storybook.md#mocking-connected-components) for more info.
Storybook also comes with tools, [test runners](./test-runner.md), and [handy integrations](./importing-stories-in-tests.md) with the larger JavaScript ecosystem to expand your UI test coverage. These docs detail how you can use Storybook for UI testing.
- [**Test runner**](./test-runner.md) to automatically test your entire Storybook and catch broken stories.
- [**Visual tests**](./visual-testing.md) capture a screenshot of every story then compare it against baselines to detect appearance and integration issues
- [**Accessibility tests**](./accessibility-testing.md) catch usability issues related to visual, hearing, mobility, cognitive, speech, or neurological disabilities
- [**Interaction tests**](./interaction-testing.md) verify component functionality by simulating user behaviour, firing events, and ensuring that state is updated as expected

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

View File

@ -110,6 +110,7 @@ Visual tests capture images of stories and compare them against image baselines.
#### Learn about other UI tests
- [Test runner](./test-runner.md) to automate test execution
- [Visual tests](./visual-testing.md) for appearance
- [Accessibility tests](./accessibility-testing.md) for accessibility
- [Interaction tests](./interaction-testing.md) for user behavior simulation

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -0,0 +1,293 @@
---
title: 'Test runner'
---
Storybook test runner turns all of your stories into executable tests. It is powered by [Jest](https://jestjs.io/) and [Playwright](https://playwright.dev/).
- For those [without a play function](../writing-stories/introduction.md): it verifies whether the story renders without any errors.
- For those [with a play function](../writing-stories/play-function.md): it also checks for errors in the play function and all assertions passed.
These tests run in a live browser and can be executed via the [command line](#cli-options) or your [CI server](#set-up-ci-to-run-tests).
## Setup
The test runner is a standalone, framework-agnostic addon that runs parallel to your Storybook. You will need to take some additional steps to set it up properly. Detailed below is our recommendation to configure and execute it.
Run the following command to install the addon and the required dependencies.
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-runner-install.yarn.js.mdx',
'common/storybook-test-runner-install.npm.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
Update your `package.json` scripts and enable the test runner.
```json
{
"scripts": {
"test-storybook": "test-storybook"
}
}
```
Start your Storybook with:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'angular/storybook-run-dev.with-builder.js.mdx',
'common/storybook-run-dev.npm.js.mdx',
'common/storybook-run-dev.yarn.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
<div class="aside">
💡 Storybook's test runner requires a running Storybook instance to run all the existing tests.
</div>
Finally, open a new terminal window and run the test runner with:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-runner-execute.yarn.js.mdx',
'common/storybook-test-runner-execute.npm.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
## Configure
Test runner offers zero-config support for Storybook. However, you can run `test-storybook --eject` for more fine-grained control. It generates a `test-runner-jest.config.js` file at the root of your project, which you can modify. Additionally, you can extend the generated configuration file and provide [testEnvironmentOptions](https://github.com/playwright-community/jest-playwright#configuration) as the test runner also uses [jest-playwright](https://github.com/playwright-community/jest-playwright) under the hood.
### CLI Options
The test runner is powered by [Jest](https://jestjs.io/) and accepts all its [CLI options](https://jestjs.io/docs/cli) (e.g., `--watch`, `--maxWorkers`).
If you're already using any of those flags in your project, you should be able to migrate them into Storybook's test runner without any issues. Listed below are all the available flags and examples of using them.
| Options | Description |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `--help` | Output usage information <br/>`test-storybook --help` |
| `-s`, `--stories-json` | Run in stories json mode. Automatically detected (requires a compatible Storybook) <br/>`test-storybook --stories-json` |
| `--no-stories-json` | Disables stories json mode <br/>`test-storybook --no-stories-json` |
| `-c`, `--config-dir [dir-name]` | Directory where to load Storybook configurations from <br/>`test-storybook -c .storybook` |
| `--watch` | Run in watch mode <br/>`test-storybook --watch` |
| `--url` | Define the URL to run tests in. Useful for custom Storybook URLs <br/>`test-storybook --url http://the-storybook-url-here.com` |
| `--browsers` | Define browsers to run tests in. One or multiple of: chromium, firefox, webkit <br/>`test-storybook --browsers firefox chromium` |
| `--maxWorkers [amount]` | Specifies the maximum number of workers the worker-pool will spawn for running tests <br/>`test-storybook --maxWorkers=2` |
| `--no-cache` | Disable the cache <br/>`test-storybook --no-cache` |
| `--clearCache` | Deletes the Jest cache directory and then exits without running tests <br/>`test-storybook --clearCache` |
| `--verbose` | Display individual test results with the test suite hierarchy <br/>`test-storybook --verbose` |
| `-u`, `--updateSnapshot` | Use this flag to re-record every snapshot that fails during this test run <br/>`test-storybook -u` |
| `--eject` | Creates a local configuration file to override defaults of the test-runner <br/>`test-storybook --eject` |
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-runner-execute-with-flags.yarn.js.mdx',
'common/storybook-test-runner-execute-with-flags.npm.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
### Run tests against a deployed Storybook
By default, the test runner assumes that you're running it against a locally served Storybook on port `6006`. If you want to define a target URL to run against deployed Storybooks, you can use the `--url` flag or set the `TARGET_URL` environment variable. For example:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-runner-execute-with-url.yarn.js.mdx',
'common/storybook-test-runner-execute-with-url.npm.js.mdx',
'common/storybook-test-runner-execute-with-url.env-var.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
## Set up CI to run tests
You can also configure the test runner to run tests on a CI environment. Documented below are some recipes to help you get started.
### Run against deployed Storybooks
If you're already using a CI provider (e.g., [GitHub Actions](https://github.com/features/actions), [GitLab Pipelines](https://docs.gitlab.com/ee/ci/pipelines/), [CircleCI](https://circleci.com/)) to build and publish your Storybook, you can configure your environment and run the test runner against the deployed version. Here's a recipe that uses third-party libraries (i.e., [concurrently](https://www.npmjs.com/package/concurrently), [http-server](https://www.npmjs.com/package/http-server), and [wait-on](https://www.npmjs.com/package/wait-on)).
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-runner-local-build-workflow.yml.mdx',
]}
/>
<!-- prettier-ignore-end -->
### Run against deployed Storybooks via Github Actions deployment
If you're publishing your Storybook with services such as [Vercel](https://vercel.com/) or [Netlify](https://www.netlify.com/), they emit a `deployment_status` event in GitHub Actions. You can use it and set the `deployment_status.target_url` as the `TARGET_URL` environment variable. Here's how:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-runner-with-deploy-event-workflow.yml.mdx',
]}
/>
<!-- prettier-ignore-end -->
<div class="aside">
💡 The published Storybook must be publicly available for this example to work. We recommend running the test server using the recipe [above](#run-against-locally-built-storybooks) if it requires authentication.
</div>
### What's the difference between Chromatic and Test runner?
Test Runner is a generic testing tool that can run locally or on CI and be configured or extended to run all kinds of tests.
[Chromatic](https://www.chromatic.com/) is a cloud-based service that runs [visual](./visual-testing.md) and [interaction tests](./interaction-testing.md) (and soon accessibility tests) without setting up the test runner. It also syncs with your git provider and manages access control for private projects.
However, you might want to pair the test runner and Chromatic in some cases.
- Use it locally and Chromatic on your CI.
- Use Chromatic for visual and interaction tests and run other custom tests using the test runner.
## Advanced configuration
### Test hook API (experimental)
The test runner renders a story and executes its [play function](writing-stories/play-function.md) if one exists. However, certain behaviors are impossible to achieve via the play function, which executes in the browser. For example, if you want the test runner to take visual snapshots for you, this is possible via Playwright/Jest but must be executed in Node.
The test runner exports test hooks that can be overridden globally to enable use cases like visual or DOM snapshots. These hooks give you access to the test lifecycle before and after the story is rendered.
Listed below are the available hooks and an overview of how to use them.
| Hook | Description |
| ------------ | ----------------------------------------------------------------------------- |
| `setup` | Executes once before all the tests run<br/>`setup() {}` |
| `preRender` | Executes before a story is rendered<br/>`async preRender(page, context) {}` |
| `postRender` | Executes after the story is rendered<br/>`async postRender(page, context) {}` |
<div class="aside">
💡 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](../writing-stories/play-function.md).
</div>
To enable the hooks API, you'll need to add a new configuration file inside your Storybook directory and set them up as follows:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-runner-hooks-example.js.mdx',
'common/storybook-test-runner-hooks-example.ts.mdx',
]}
/>
<!-- prettier-ignore-end -->
<div class="aside">
💡 Except for the `setup` function, all other functions run asynchronously. Both `preRender` and `postRender` functions include two additional arguments, a [Playwright page](https://playwright.dev/docs/pages) and a context object which contains the `id`, `title`, and the `name` of the story.
</div>
When the test runner executes, your existing tests will go through the following lifecycle:
- The `setup` function is executed before all the tests run.
- The context object is generated containing the required information.
- Playwright navigates to the story's page.
- The `preRender` function is executed.
- The story is rendered, and any existing `play` functions are executed.
- The `postRender` function is executed.
### Stories.json mode
When testing a local Storybook, the test runner transforms your story files into tests. For a remote Storybook, it uses the Storybook's [stories.json](../configure/overview.md#feature-flags) file (a static index of all the stories) to run the tests. Suppose you run into a situation where the local and remote Storybooks appear out of sync, or you might not even have access to the code. In that case, the `stories.json` file is guaranteed to be the most accurate representation of the deployed Storybook you are testing. To test a local Storybook using this feature, use the `--stories-json` flag as follows:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-runner-with-stories-json.yarn.js.mdx',
'common/storybook-test-runner-with-stories-json.npm.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
<div class="aside">
💡 The `stories.json` mode is not compatible with watch mode.
</div>
To disable it, use the `--no-stories-json` flag:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-runner-disable-stories-json.yarn.js.mdx',
'common/storybook-test-runner-disable-stories-json.npm.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
---
## Troubleshooting
### The test runner seems flaky and keeps timing out
If your tests time out with the following message:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-runner-error.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
It might be that Playwright couldn't handle testing the number of stories you have in your project. Perhaps you have a large number of stories, or your CI environment has a really low RAM configuration. In such cases, you should limit the number of workers that run in parallel by adjusting your command as follows:
```json
{
"scripts": {
"test-storybook:ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook --quiet && npx http-server storybook-static --port 6006 --silent\" \"wait-on tcp:6006 && yarn test-storybook --maxWorkers=2\""
}
}
```
### Run the test runner in other CI environments
As the test runner is based on Playwright, you might need to use specific docker images or other configurations depending on your CI setup. In that case, you can refer to the [Playwright CI docs](https://playwright.dev/docs/ci) for more information.
#### Learn about other UI tests
- Test runner to automate test execution
- [Visual tests](./visual-testing.md) for appearance
- [Accessibility tests](./accessibility-testing.md) for accessibility
- [Interaction tests](./interaction-testing.md) for user behavior simulation
- [Snapshot tests](./snapshot-testing.md) for rendering errors and warnings
- [Import stories in other tests](./importing-stories-in-tests.md) for other tools

View File

@ -74,6 +74,7 @@ Snapshot tests compare the rendered markup of every story against known baseline
#### Learn about other UI tests
- [Test runner](./test-runner.md) to automate test execution
- Visual tests for appearance
- [Accessibility tests](./accessibility-testing.md) for accessibility
- [Interaction tests](./interaction-testing.md) for user behavior simulation