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

Chore: (Docs) updates testing examples #19757

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions docs/api/csf.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,9 @@ A good use case for the `play` function is a form component. With previous Story
'react/login-form-with-play-function.ts.mdx',
'angular/login-form-with-play-function.ts.mdx',
'vue/login-form-with-play-function.2.js.mdx',
'vue/login-form-with-play-function.ts-2.ts.mdx',
'vue/login-form-with-play-function.3.js.mdx',
'vue/login-form-with-play-function.ts-3.ts.mdx',
'svelte/login-form-with-play-function.js.mdx',
]}
/>
Expand Down
4 changes: 3 additions & 1 deletion docs/essentials/interactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ Make sure to import the Storybook wrappers for Jest and Testing Library rather t
<CodeSnippets
paths={[
'common/storybook-interactions-play-function.js.mdx',
'common/storybook-interactions-play-function.ts.mdx',
'common/storybook-interactions-play-function.mdx.mdx',
]}
/>

Expand All @@ -71,4 +73,4 @@ The above example uses the `canvasElement` to scope your element queries to the

While you can refer to the [Testing Library documentation](https://testing-library.com/docs/) for details on how to use it, there's an important detail that's different when using the Storybook wrapper: **method invocations must be `await`-ed**. It allows you to step back and forth through your interactions using the debugger.

Any `args` that have been marked as an Action, either using the [argTypes annotation](./actions.md#action-argtype-annotation) or the [argTypesRegex](./actions.md#automatically-matching-args), will be automatically converted to a [Jest mock function](https://jestjs.io/docs/mock-function-api) (spy). This allows you to make assertions about calls to these functions.
Any `args` that have been marked as an Action, either using the [argTypes annotation](./actions.md#action-argtype-annotation) or the [argTypesRegex](./actions.md#automatically-matching-args), will be automatically converted to a [Jest mock function](https://jestjs.io/docs/mock-function-api) (spy). This allows you to make assertions about calls to these functions.
31 changes: 14 additions & 17 deletions docs/snippets/angular/login-form-with-play-function.mdx.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,19 @@ import { LoginForm } from './LoginForm.component';

<Meta title="Form" component={LoginForm} />

<!--
👇 Render functions are a framework specific feature to allow you control on how the component renders.
See https://storybook.js.org/docs/7.0/angular/api/csf
to learn how to use render functions.
-->
export const Template = (args) => ({ props: args });

<Canvas>
<Story
name="Empty Form"
render={(args) => ({
props: args,
})} />
<Story name="Empty Form">
{Template.bind({})}
</Story>
<!--
See https://storybook.js.org/docs/angular/writing-stories/play-function#working-with-the-canvas
to learn more about using the canvasElement to query the DOM
-->
<Story
name="Filled Form"
play={ async ({ canvasElement }) => {
play={async ({ canvasElement }) => {
// Starts querying the component from its root element
const canvas = within(canvasElement);

Expand All @@ -34,7 +32,7 @@ import { LoginForm } from './LoginForm.component';

await userEvent.type(canvas.getByTestId('password'), 'a-random-password');

// See https://storybook.js.org/docs/7.0/angular/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
Expand All @@ -43,9 +41,8 @@ import { LoginForm } from './LoginForm.component';
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
}}
render={(args) => ({
props: args,
})} />
}}>
{Template.bind({})}
</Story>
</Canvas>
```
```
52 changes: 29 additions & 23 deletions docs/snippets/angular/login-form-with-play-function.ts.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
```ts
// LoginForm.stories.ts
/// LoginForm.stories.ts

import type { Meta, Story } from '@storybook/angular';
import { Meta, Story } from '@storybook/angular';

import { userEvent, within } from '@storybook/testing-library';

Expand All @@ -11,34 +11,40 @@ import { LoginForm } from './LoginForm.component';

export default {
/* 👇 The title prop is optional.
* See https://storybook.js.org/docs/7.0/angular/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
* See https://storybook.js.org/docs/angular/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'Form',
component: LoginForm,
} as Meta;

export const EmptyForm: Story = {};
const Template: Story = (args) => ({
props: args,
});

export const FilledForm: Story = {
play: async ({ canvasElement }) => {
// Starts querying the component from its root element
const canvas = within(canvasElement);
export const EmptyForm = Template.bind({});

// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), '[email protected]');
/*
* See https://storybook.js.org/docs/angular/writing-stories/play-function#working-with-the-canvas
* to learn more about using the canvasElement to query the DOM
*/
export const FilledForm = Template.bind({});
FilledForm.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);

await userEvent.type(canvas.getByTestId('password'), 'a-random-password');
// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), '[email protected]');

// See https://storybook.js.org/docs/7.0/react/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
await userEvent.type(canvas.getByTestId('password'), 'a-random-password');

// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!'
)
).toBeInTheDocument();
},
// 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();
};
```
```
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
```md
<!-- MyComponent.stories.mdx -->

import { Meta, Story } from '@storybook/addon-docs';
import { Meta, Story } from '@storybook/addon-docs';

import { screen, userEvent } from '@storybook/testing-library';
import { userEvent, within } from '@storybook/testing-library';

import { MyComponent } from './MyComponent.component';

<Meta title="QueryMethods" component={MyComponent} />

<!--
👇 Render functions are a framework specific feature to allow you control on how the component renders.
See https://storybook.js.org/docs/7.0/angular/api/csf
to learn how to use render functions.
-->
export const Template = (args) => ({ props: args });

<!--
See https://storybook.js.org/docs/angular/writing-stories/play-function#working-with-the-canvas
to learn more about using the canvasElement to query the DOM
-->
<Story
name="ExampleWithRole"
play={async () => {
// See https://storybook.js.org/docs/7.0/angular/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(screen.getByRole('button', { name: / button label/i }));
}}
render={(args) => ({
props: args,
})} />
play={async ({ canvasElement }) => {
const canvas = within(canvasElement);

// 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', { name: / button label/i }));
}}>
{Template.bind({})}
</Story>
```
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
```ts
// MyComponent.stories.ts

import type { Meta, Story } from '@storybook/angular';
import { Meta, Story } from '@storybook/addon-docs';

import { screen, userEvent } from '@storybook/testing-library';
import { userEvent, within } from '@storybook/testing-library';

import { MyComponent } from './MyComponent.component';

export default {
/* 👇 The title prop is optional.
* See https://storybook.js.org/docs/7.0/angular/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
* See https://storybook.js.org/docs/angular/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'QueryMethods',
component: MyComponent,

} as Meta;

export const ExampleWithRole: Story = {
play: async () => {
// See https://storybook.js.org/docs/7.0/angular/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(screen.getByRole('button', { name: / button label/i }));
},
const Template: Story = (args) => ({
props: args,
});

/*
* See https://storybook.js.org/docs/angular/writing-stories/play-function#working-with-the-canvas
* to learn more about using the canvasElement to query the DOM
*/
export const ExampleWithRole = Template.bind({});
ExampleWithRole.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);

// 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', { name: / button label/i }));
};
```
Original file line number Diff line number Diff line change
@@ -1,39 +1,48 @@
```ts
// MyComponent.stories.ts

import type { Meta, Story } from '@storybook/angular';
import { Meta, Story } from '@storybook/angular';

import { screen, userEvent } from '@storybook/testing-library';
import { userEvent, within } from '@storybook/testing-library';

import { MyComponent } from './MyComponent.component';

export default {
/* 👇 The title prop is optional.
* See https://storybook.js.org/docs/7.0/angular/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
* See https://storybook.js.org/docs/angular/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'MyComponent',
component: MyComponent,
component: MyComponent,
} as Meta;

export const FirstStory: Story = {
play: async () => {
userEvent.type(screen.getByTestId('an-element'), 'example-value');
},
const Template: Story = (args) => ({
props: args,
});

/*
* See https://storybook.js.org/docs/angular/writing-stories/play-function#working-with-the-canvas
* to learn more about using the canvasElement to query the DOM
*/
export const FirstStory = Template.bind({});
FirstStory.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
userEvent.type(canvas.getByTestId('an-element'), 'example-value');
};

export const SecondStory: Story = {
play: async () => {
await userEvent.type(screen.getByTestId('other-element'), 'another value');
},
export const SecondStory = Template.bind({});
SecondStory.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.type(canvas.getByTestId('other-element'), 'another value');
};

export const CombinedStories: Story = {
play: async () => {
// Runs the FirstStory and Second story play function before running this story's play function
await FirstStory.play();
await SecondStory.play();
await userEvent.type(screen.getByTestId('another-element'), 'random value');
},
export const CombinedStories = Template.bind({});
CombinedStories.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);

// Runs the FirstStory and Second story play function before running this story's play function
await FirstStory.play({ canvasElement });
await SecondStory.play({ canvasElement });
await userEvent.type(canvas.getByTestId('another-element'), 'random value');
};
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,27 @@

import { Meta, Story } from '@storybook/addon-docs';

import { screen, userEvent } from '@storybook/testing-library';
import { userEvent, within } from '@storybook/testing-library';

import { MyComponent } from './MyComponent.component';

<Meta title="Async Query Methods" component={MyComponent} />

<!--
👇 Render functions are a framework specific feature to allow you control on how the component renders.
See https://storybook.js.org/docs/7.0/angular/api/csf
to learn how to use render functions.
-->
export const Template = (args) => ({ props: args });

<!--
See https://storybook.js.org/docs/angular/writing-stories/play-function#working-with-the-canvas
to learn more about using the canvasElement to query the DOM
-->
<Story
name="AsyncExample"
play={async () => {
play={async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Other steps

// Waits for the component to be rendered before querying the element
await screen.findByRole('button', { name: / button label/i }));
}}
render={(args) => ({
props: args,
})} />
await canvas.findByRole('button', { name: / button label/i }));
}}>
{Template.bind({})}
</Story>
```
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import type { Meta, Story } from '@storybook/angular';

import { screen, userEvent } from '@storybook/testing-library';
import { userEvent, within } from '@storybook/testing-library';

import { MyComponent } from './MyComponent.component';

Expand All @@ -16,12 +16,21 @@ export default {
component: MyComponent,
} as Meta;

export const AsyncExample: Story = {
play: async () => {
// Other steps
const Template: Story = (args) => ({
props: args,
});

// Waits for the component to be rendered before querying the element
await screen.findByRole('button', { name: / button label/i }));
},
/*
* See https://storybook.js.org/docs/angular/writing-stories/play-function#working-with-the-canvas
* to learn more about using the canvasElement to query the DOM
*/
export const AsyncExample = Template.bind({});
AsyncExample.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);

// Other steps

// Waits for the component to be rendered before querying the element
await canvas.findByRole('button', { name: / button label/i }));
};
```
Loading