Merge pull request #28764 from storybookjs/kasper/introduce-run

Core: Introduce run over play in portable stories, and revert back play changes of 8.2
This commit is contained in:
Kasper Peulen 2024-07-31 16:33:13 +02:00 committed by GitHub
commit 3b65e7267e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 390 additions and 371 deletions

View File

@ -89,25 +89,8 @@ export function composeStory<TRenderer extends Renderer = Renderer, TArgs extend
normalizedComponentAnnotations
);
// TODO: Remove this in 9.0
// We can only use the renderToCanvas definition of the default config when testingLibraryRender is set
// This makes sure, that when the user doesn't do this, and doesn't provide its own renderToCanvas definition,
// we fall back to the < 8.1 behavior of the play function.
const fallback =
defaultConfig &&
!globalProjectAnnotations?.testingLibraryRender &&
!projectAnnotations?.testingLibraryRender;
const normalizedProjectAnnotations = normalizeProjectAnnotations<TRenderer>(
composeConfigs([
{
...defaultConfig,
renderToCanvas: fallback ? undefined : defaultConfig?.renderToCanvas,
},
globalProjectAnnotations,
projectAnnotations ?? {},
])
composeConfigs([defaultConfig ?? {}, globalProjectAnnotations, projectAnnotations ?? {}])
);
const story = prepareStory<TRenderer>(
@ -130,7 +113,7 @@ export function composeStory<TRenderer extends Renderer = Renderer, TArgs extend
loaded: {},
abortSignal: new AbortController().signal,
step: (label, play) => story.runStep(label, play, context),
canvasElement: globalThis?.document?.body,
canvasElement: null!,
canvas: {} as Canvas,
...story,
context: null!,
@ -149,6 +132,7 @@ export function composeStory<TRenderer extends Renderer = Renderer, TArgs extend
id: story.id,
name: story.name,
tags: story.tags,
showMain: () => {},
showError: (error) => {},
showException: (error) => {},
forceRemount: true,
@ -171,28 +155,23 @@ export function composeStory<TRenderer extends Renderer = Renderer, TArgs extend
let loadedContext: StoryContext<TRenderer> | undefined;
// TODO: Remove in 9.0
const backwardsCompatiblePlay = async (
extraContext?: Partial<StoryContext<TRenderer, Partial<TArgs>>>
) => {
const play = async (extraContext?: Partial<StoryContext<TRenderer, Partial<TArgs>>>) => {
const context = initializeContext();
context.canvasElement ??= globalThis?.document?.body;
if (loadedContext) {
context.loaded = loadedContext.loaded;
}
Object.assign(context, extraContext);
return story.playFunction!(context);
};
const newPlay = (extraContext?: Partial<StoryContext<TRenderer, Partial<TArgs>>>) => {
const run = (extraContext?: Partial<StoryContext<TRenderer, Partial<TArgs>>>) => {
const context = initializeContext();
Object.assign(context, extraContext);
return playStory(story, context);
return runStory(story, context);
};
const playFunction =
!story.renderToCanvas && story.playFunction
? backwardsCompatiblePlay
: !story.renderToCanvas && !story.playFunction
? undefined
: newPlay;
const playFunction = story.playFunction ? play : undefined;
const composedStory: ComposedStoryFn<TRenderer, Partial<TArgs>> = Object.assign(
function storyFn(extraArgs?: Partial<TArgs>) {
@ -226,6 +205,7 @@ export function composeStory<TRenderer extends Renderer = Renderer, TArgs extend
parameters: story.parameters as Parameters,
argTypes: story.argTypes as StrictArgTypes<TArgs>,
play: playFunction!,
run,
tags: story.tags,
}
);
@ -325,13 +305,24 @@ export function createPlaywrightTest<TFixture extends { extend: any }>(
// TODO At some point this function should live in prepareStory and become the core of StoryRender.render as well.
// Will make a follow up PR for that
async function playStory<TRenderer extends Renderer>(
async function runStory<TRenderer extends Renderer>(
story: PreparedStory<TRenderer>,
context: StoryContext<TRenderer>
) {
for (const callback of [...cleanups].reverse()) await callback();
cleanups.length = 0;
if (!context.canvasElement) {
const container = document.createElement('div');
globalThis?.document?.body?.appendChild(container);
context.canvasElement = container;
cleanups.push(() => {
if (globalThis?.document?.body?.contains(container)) {
globalThis?.document?.body?.removeChild(container);
}
});
}
context.loaded = await story.applyLoaders(context);
if (context.abortSignal.aborted) return;

View File

@ -44,7 +44,8 @@ export type ComposedStoryFn<
> = PartialArgsStoryFn<TRenderer, TArgs> & {
args: TArgs;
id: StoryId;
play: (context?: Partial<StoryContext<TRenderer, Partial<TArgs>>>) => Promise<void>;
play?: (context?: Partial<StoryContext<TRenderer, Partial<TArgs>>>) => Promise<void>;
run: (context?: Partial<StoryContext<TRenderer, Partial<TArgs>>>) => Promise<void>;
load: () => Promise<void>;
storyName: string;
parameters: Parameters;

View File

@ -53,7 +53,7 @@ describe('renders', () => {
});
it('should render component mounted in play function', async () => {
await MountInPlayFunction.play();
await MountInPlayFunction.run();
expect(screen.getByTestId('spy-data').textContent).toEqual('mockFn return value');
expect(screen.getByTestId('loaded-data').textContent).toEqual('loaded data');
@ -65,7 +65,7 @@ describe('renders', () => {
expect(getByTestId('spy-data').textContent).toEqual('mockFn return value');
expect(getByTestId('loaded-data').textContent).toEqual('loaded data');
// spy assertions happen in the play function and should work
await LoaderStory.play!();
await LoaderStory.run!();
});
});
@ -125,7 +125,7 @@ describe('CSF3', () => {
it('renders with play function without canvas element', async () => {
const CSF3InputFieldFilled = composeStory(stories.CSF3InputFieldFilled, stories.default);
await CSF3InputFieldFilled.play();
await CSF3InputFieldFilled.run();
const input = screen.getByTestId('input') as HTMLInputElement;
expect(input.value).toEqual('Hello world!');
@ -138,7 +138,7 @@ describe('CSF3', () => {
console.log(div.tagName);
document.body.appendChild(div);
await CSF3InputFieldFilled.play({ canvasElement: div });
await CSF3InputFieldFilled.run({ canvasElement: div });
const input = screen.getByTestId('input') as HTMLInputElement;
expect(input.value).toEqual('Hello world!');
@ -185,6 +185,6 @@ const testCases = Object.values(composeStories(stories)).map(
);
it.each(testCases)('Renders %s story', async (_storyName, Story) => {
if (_storyName === 'CSF2StoryWithLocale') return;
await Story.play();
await Story.run();
expect(document.body).toMatchSnapshot();
});

View File

@ -45,11 +45,16 @@ export function setProjectAnnotations(
// This will not be necessary once we have auto preset loading
export const INTERNAL_DEFAULT_PROJECT_ANNOTATIONS: ProjectAnnotations<ReactRenderer> = {
...reactProjectAnnotations,
renderToCanvas: ({
storyContext: { context, unboundStoryFn: Story, testingLibraryRender: render, canvasElement },
}) => {
if (render == null) throw new TestingLibraryMustBeConfiguredError();
const { unmount } = render(<Story {...context} />, { baseElement: context.canvasElement });
renderToCanvas: (renderContext, canvasElement) => {
if (renderContext.storyContext.testingLibraryRender == null) {
throw new TestingLibraryMustBeConfiguredError();
// Enable for 8.3
// return reactProjectAnnotations.renderToCanvas(renderContext, canvasElement);
}
const {
storyContext: { context, unboundStoryFn: Story, testingLibraryRender: render },
} = renderContext;
const { unmount } = render(<Story {...context} />, { container: context.canvasElement });
return unmount;
},
};

View File

@ -2,82 +2,48 @@
exports[`Renders CSF2Secondary story 1`] = `
<body>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
type="button"
>
label coming from story args!
<!---->
</button>
<div>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
type="button"
>
label coming from story args!
<!---->
</button>
</div>
</body>
`;
exports[`Renders CSF2StoryWithParamsAndDecorator story 1`] = `
<body>
<div
data-testid="local-decorator"
style="margin: 3em;"
>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
type="button"
<div>
<div
data-testid="local-decorator"
style="margin: 3em;"
>
foo
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
type="button"
>
foo
<!---->
</button>
<!---->
</button>
<!---->
</div>
<!---->
<!---->
</div>
<!---->
<!---->
</body>
`;
exports[`Renders CSF3Button story 1`] = `
<body>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
type="button"
>
foo
<!---->
</button>
</body>
`;
exports[`Renders CSF3ButtonWithRender story 1`] = `
<body>
<div>
<p
data-testid="custom-render"
>
I am a custom render function
</p>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
@ -86,88 +52,52 @@ exports[`Renders CSF3ButtonWithRender story 1`] = `
foo
<!---->
</button>
<!---->
</div>
</body>
`;
exports[`Renders CSF3ButtonWithRender story 1`] = `
<body>
<div>
<div>
<p
data-testid="custom-render"
>
I am a custom render function
</p>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
type="button"
>
foo
<!---->
</button>
<!---->
</div>
</div>
</body>
`;
exports[`Renders CSF3InputFieldFilled story 1`] = `
<body>
<input
data-testid="input"
formaction="http://localhost:3000/"
formmethod=""
/>
<div>
<input
data-testid="input"
formaction="http://localhost:3000/"
formmethod=""
/>
</div>
</body>
`;
exports[`Renders CSF3Primary story 1`] = `
<body>
<button
class="storybook-button storybook-button--large storybook-button--primary"
style=""
type="button"
>
foo
<!---->
</button>
</body>
`;
exports[`Renders LoaderStory story 1`] = `
<body>
<div>
<div
data-testid="loaded-data"
>
loaded data
</div>
<div
data-testid="spy-data"
>
mockFn return value
</div>
</div>
</body>
`;
exports[`Renders NewStory story 1`] = `
<body>
<div
data-testid="local-decorator"
style="margin: 3em;"
>
<button
class="storybook-button storybook-button--large storybook-button--primary"
style=""
@ -176,11 +106,53 @@ exports[`Renders NewStory story 1`] = `
foo
<!---->
</button>
<!---->
<!---->
</div>
</body>
`;
exports[`Renders LoaderStory story 1`] = `
<body>
<div>
<div>
<div
data-testid="loaded-data"
>
loaded data
</div>
<div
data-testid="spy-data"
>
mockFn return value
</div>
</div>
</div>
</body>
`;
exports[`Renders NewStory story 1`] = `
<body>
<div>
<div
data-testid="local-decorator"
style="margin: 3em;"
>
<button
class="storybook-button storybook-button--large storybook-button--primary"
style=""
type="button"
>
foo
<!---->
</button>
<!---->
<!---->
</div>
<!---->
<!---->
</div>
<!---->
<!---->
</body>
`;

View File

@ -70,7 +70,7 @@ describe('renders', () => {
expect(getByTestId('spy-data').textContent).toEqual('mockFn return value');
expect(getByTestId('loaded-data').textContent).toEqual('loaded data');
// spy assertions happen in the play function and should work
await LoaderStory.play!();
await LoaderStory.run!();
});
});
@ -120,7 +120,7 @@ describe('CSF3', () => {
it('renders with play function without canvas element', async () => {
const CSF3InputFieldFilled = composeStory(stories.CSF3InputFieldFilled, stories.default);
await CSF3InputFieldFilled.play();
await CSF3InputFieldFilled.run();
const input = screen.getByTestId('input') as HTMLInputElement;
expect(input.value).toEqual('Hello world!');
@ -132,7 +132,7 @@ describe('CSF3', () => {
const div = document.createElement('div');
document.body.appendChild(div);
await CSF3InputFieldFilled.play({ canvasElement: div });
await CSF3InputFieldFilled.run({ canvasElement: div });
const input = screen.getByTestId('input') as HTMLInputElement;
expect(input.value).toEqual('Hello world!');
@ -171,6 +171,6 @@ const testCases = Object.values(composeStories(stories)).map(
);
it.each(testCases)('Renders %s story', async (_storyName, Story) => {
if (_storyName === 'CSF2StoryWithLocale') return;
await Story.play();
await Story.run();
expect(document.body).toMatchSnapshot();
});

View File

@ -63,8 +63,16 @@ export function setProjectAnnotations(
// This will not be necessary once we have auto preset loading
export const INTERNAL_DEFAULT_PROJECT_ANNOTATIONS: ProjectAnnotations<SvelteRenderer> = {
...svelteProjectAnnotations,
renderToCanvas: ({ storyFn, storyContext: { testingLibraryRender: render, canvasElement } }) => {
if (render == null) throw new TestingLibraryMustBeConfiguredError();
renderToCanvas: (renderContext, canvasElement) => {
if (renderContext.storyContext.testingLibraryRender == null) {
throw new TestingLibraryMustBeConfiguredError();
// Enable for 8.3
// return svelteProjectAnnotations.renderToCanvas(renderContext, canvasElement);
}
const {
storyFn,
storyContext: { testingLibraryRender: render },
} = renderContext;
const { Component, props } = storyFn();
const { unmount } = render(Component, { props, target: canvasElement });
return unmount;

View File

@ -52,7 +52,7 @@ describe('renders', () => {
expect(getByTestId('spy-data').textContent).toEqual('mockFn return value');
expect(getByTestId('loaded-data').textContent).toEqual('loaded data');
// spy assertions happen in the play function and should work
await LoaderStory.play!();
await LoaderStory.run!();
});
});
@ -103,7 +103,7 @@ describe('CSF3', () => {
it('renders with play function', async () => {
const CSF3InputFieldFilled = composeStory(stories.CSF3InputFieldFilled, stories.default);
await CSF3InputFieldFilled.play();
await CSF3InputFieldFilled.run();
const input = screen.getByTestId('input') as HTMLInputElement;
expect(input.value).toEqual('Hello world!');
@ -145,7 +145,7 @@ describe('ComposeStories types', () => {
const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName, Story]);
it.each(testCases)('Renders %s story', async (_storyName, Story) => {
if (typeof Story === 'string' || _storyName === 'CSF2StoryWithLocale') return;
await Story.play();
await Story.run();
await new Promise((resolve) => setTimeout(resolve, 0));
expect(document.body).toMatchSnapshot();
});

View File

@ -53,9 +53,17 @@ export function setProjectAnnotations(
// This will not be necessary once we have auto preset loading
export const vueProjectAnnotations: ProjectAnnotations<VueRenderer> = {
...defaultProjectAnnotations,
renderToCanvas: ({ storyFn, storyContext: { testingLibraryRender: render, canvasElement } }) => {
if (render == null) throw new TestingLibraryMustBeConfiguredError();
const { unmount } = render(storyFn(), { baseElement: canvasElement });
renderToCanvas: (renderContext, canvasElement) => {
if (renderContext.storyContext.testingLibraryRender == null) {
throw new TestingLibraryMustBeConfiguredError();
// Enable for 8.3
// return defaultProjectAnnotations.renderToCanvas(renderContext, canvasElement);
}
const {
storyFn,
storyContext: { testingLibraryRender: render },
} = renderContext;
const { unmount } = render(storyFn(), { container: canvasElement });
return unmount;
},
};

View File

@ -2184,12 +2184,12 @@ __metadata:
linkType: hard
"@babel/runtime-corejs3@npm:^7.10.2":
version: 7.23.1
resolution: "@babel/runtime-corejs3@npm:7.23.1"
version: 7.25.0
resolution: "@babel/runtime-corejs3@npm:7.25.0"
dependencies:
core-js-pure: "npm:^3.30.2"
regenerator-runtime: "npm:^0.14.0"
checksum: 10c0/6e2c2b11779ff56c88b1f3a8742498640f7271ad4fcf9cfd24052bbb236a5e7c4c7c8d81cda751da3b4effa678736303deb78441c5752e63bfb90d6453fd870f
checksum: 10c0/7c9e7896749b5968bc6a7638cf1735e5d2dc791780f4f46daf15a45777780cd0485d1357e92f54b03f815269064dc84d771e83486d49e18b847ffa8cfb6a6afa
languageName: node
linkType: hard
@ -2220,7 +2220,16 @@ __metadata:
languageName: node
linkType: hard
"@babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.15, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.24.4, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
"@babel/runtime@npm:^7.10.2":
version: 7.25.0
resolution: "@babel/runtime@npm:7.25.0"
dependencies:
regenerator-runtime: "npm:^0.14.0"
checksum: 10c0/bd3faf246170826cef2071a94d7b47b49d532351360ecd17722d03f6713fd93a3eb3dbd9518faa778d5e8ccad7392a7a604e56bd37aaad3f3aa68d619ccd983d
languageName: node
linkType: hard
"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.15, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.24.4, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
version: 7.24.7
resolution: "@babel/runtime@npm:7.24.7"
dependencies:
@ -7615,7 +7624,14 @@ __metadata:
languageName: node
linkType: hard
"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1":
"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0":
version: 2.0.6
resolution: "@types/istanbul-lib-coverage@npm:2.0.6"
checksum: 10c0/3948088654f3eeb45363f1db158354fb013b362dba2a5c2c18c559484d5eb9f6fd85b23d66c0a7c2fcfab7308d0a585b14dadaca6cc8bf89ebfdc7f8f5102fb7
languageName: node
linkType: hard
"@types/istanbul-lib-coverage@npm:^2.0.1":
version: 2.0.4
resolution: "@types/istanbul-lib-coverage@npm:2.0.4"
checksum: 10c0/af5f6b64e788331ed3f7b2e2613cb6ca659c58b8500be94bbda8c995ad3da9216c006f1cfe6f66b321c39392b1bda18b16e63cef090a77d24a00b4bd5ba3b018
@ -7623,20 +7639,20 @@ __metadata:
linkType: hard
"@types/istanbul-lib-report@npm:*":
version: 3.0.1
resolution: "@types/istanbul-lib-report@npm:3.0.1"
version: 3.0.3
resolution: "@types/istanbul-lib-report@npm:3.0.3"
dependencies:
"@types/istanbul-lib-coverage": "npm:*"
checksum: 10c0/a2a002ee7ecd9079a2c06235d28d1bc77089c3d834eec7e6dac38986203634936f2a017812624acfbedabec4bddd933942f14ac93eba2dc57f581ad4f35bbf1d
checksum: 10c0/247e477bbc1a77248f3c6de5dadaae85ff86ac2d76c5fc6ab1776f54512a745ff2a5f791d22b942e3990ddbd40f3ef5289317c4fca5741bedfaa4f01df89051c
languageName: node
linkType: hard
"@types/istanbul-reports@npm:^3.0.0":
version: 3.0.2
resolution: "@types/istanbul-reports@npm:3.0.2"
version: 3.0.4
resolution: "@types/istanbul-reports@npm:3.0.4"
dependencies:
"@types/istanbul-lib-report": "npm:*"
checksum: 10c0/df6c9e6865006be06bae29f63d5240b96bc7041b18a8c6d66be5b5d92ef5c95675c7a605a603029065f4f8aece7dba7360349e9d0543f512417e64a707a3c4fa
checksum: 10c0/1647fd402aced5b6edac87274af14ebd6b3a85447ef9ad11853a70fd92a98d35f81a5d3ea9fcb5dbb5834e800c6e35b64475e33fcae6bfa9acc70d61497c54ee
languageName: node
linkType: hard
@ -8143,18 +8159,18 @@ __metadata:
linkType: hard
"@types/yargs-parser@npm:*":
version: 21.0.1
resolution: "@types/yargs-parser@npm:21.0.1"
checksum: 10c0/f1d723a4c4383a9c53b975820b7490186ca127237ca58eb2ee8f5eacdcdb195a81aeabd1d75560abdf22fc29f70e8bb103d7ab34c5ec49bc19196195a7bf3189
version: 21.0.3
resolution: "@types/yargs-parser@npm:21.0.3"
checksum: 10c0/e71c3bd9d0b73ca82e10bee2064c384ab70f61034bbfb78e74f5206283fc16a6d85267b606b5c22cb2a3338373586786fed595b2009825d6a9115afba36560a0
languageName: node
linkType: hard
"@types/yargs@npm:^15.0.0":
version: 15.0.16
resolution: "@types/yargs@npm:15.0.16"
version: 15.0.19
resolution: "@types/yargs@npm:15.0.19"
dependencies:
"@types/yargs-parser": "npm:*"
checksum: 10c0/07f0960062e66226ae602fccd62e351143291d778e1f4dd645c51111e62fbedafe2a976c223dcfa7ae052e989407b62e97a7472fc1d73536110cd05502c204a5
checksum: 10c0/9fe9b8645304a628006cbba2d1990fb015e2727274d0e3853f321a379a1242d1da2c15d2f56cff0d4313ae94f0383ccf834c3bded9fb3589608aefb3432fcf00
languageName: node
linkType: hard

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 KiB

After

Width:  |  Height:  |  Size: 568 KiB

View File

@ -5,7 +5,7 @@ import * as stories from '../stories/Button.stories';
const { Primary } = composeStories(stories);
test('Button snapshot', async () => {
await Primary.play();
await Primary.run();
expect(document.body.firstChild).toMatchSnapshot();
});
```
@ -21,7 +21,7 @@ import * as stories from '../stories/Button.stories';
const { Primary } = composeStories(stories);
test('Button snapshot', async () => {
await Primary.play();
await Primary.run();
expect(document.body.firstChild).toMatchSnapshot();
});
```
@ -37,7 +37,7 @@ import * as stories from '../stories/Button.stories';
const { Primary } = composeStories(stories);
test('Button snapshot', async () => {
await Primary.play();
await Primary.run();
expect(document.body.firstChild).toMatchSnapshot();
});
```
@ -53,7 +53,7 @@ import * as stories from '../stories/Button.stories';
const { Primary } = composeStories(stories);
test('Button snapshot', async () => {
await Primary.play();
await Primary.run();
expect(document.body.firstChild).toMatchSnapshot();
});
```

View File

@ -47,7 +47,7 @@ const { InvalidForm } = composeStories(stories);
test('Checks if the form is valid', async () => {
// Renders the composed story
await InvalidForm.play();
await InvalidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -71,7 +71,7 @@ const { InvalidForm } = composeStories(stories);
test('Checks if the form is valid', async () => {
// Renders the composed story
await InvalidForm.play();
await InvalidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -95,7 +95,7 @@ const { InvalidForm } = composeStories(stories);
it('Checks if the form is valid', async () => {
// Renders the composed story
await InvalidForm.play();
await InvalidForm.run();
await fireEvent.click(screen.getByText('Submit'));
@ -115,7 +115,7 @@ const { InvalidForm } = composeStories(stories);
test('Checks if the form is valid', async () => {
// Renders the composed story
await InvalidForm.play();
await InvalidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -139,7 +139,7 @@ const { InvalidForm } = composeStories(stories);
test('Checks if the form is valid', async () => {
// Renders the composed story
await InvalidForm.play();
await InvalidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',

View File

@ -51,7 +51,7 @@ describe('Stories Snapshots', () => {
stories.forEach(({ name, story }) => {
test(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));
// Defines the custom snapshot path location and file name
@ -131,7 +131,7 @@ describe("Stories Snapshots", () => {
stories.forEach(({ name, story }) => {
test(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));
// Defines the custom snapshot path location and file name
@ -191,7 +191,7 @@ describe('Stories Snapshots', () => {
stories.forEach(({ name, story }) => {
test(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));
// Defines the custom snapshot path location and file name
@ -262,7 +262,7 @@ describe('Stories Snapshots', () => {
stories.forEach(({ name, story }) => {
test(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));
// Defines the custom snapshot path location and file name

View File

@ -8,7 +8,7 @@ import * as FormStories from './LoginForm.stories';
const { InvalidForm, ValidForm } = composeStories(FormStories);
test('Tests invalid form state', async () => {
await InvalidForm.play();
await InvalidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -21,7 +21,7 @@ test('Tests invalid form state', async () => {
});
test('Tests filled form', async () => {
await ValidForm.play();
await ValidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -44,7 +44,7 @@ import * as FormStories from './LoginForm.stories';
const { InvalidForm, ValidForm } = composeStories(FormStories);
test('Tests invalid form state', async () => {
await InvalidForm.play();
await InvalidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -57,7 +57,7 @@ test('Tests invalid form state', async () => {
});
test('Tests filled form', async () => {
await ValidForm.play();
await ValidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -80,7 +80,7 @@ import * as FormStories from './LoginForm.stories';
const { InvalidForm, ValidForm } = composeStories(FormStories);
test('Tests invalid form state', async () => {
await InvalidForm.play();
await InvalidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -93,7 +93,7 @@ test('Tests invalid form state', async () => {
});
test('Tests filled form', async () => {
await ValidForm.play();
await ValidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -116,7 +116,7 @@ import * as FormStories from './LoginForm.stories';
const { InvalidForm, ValidForm } = composeStories(FormStories);
test('Tests invalid form state', async () => {
await InvalidForm.play();
await InvalidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -129,7 +129,7 @@ test('Tests invalid form state', async () => {
});
test('Tests filled form', async () => {
await ValidForm.play();
await ValidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',

View File

@ -11,7 +11,7 @@ test('onclick handler is called', () => {
const Primary = composeStory(PrimaryStory, meta);
const onClickSpy = jest.fn();
await Primary.play({ args: { ...Primary.args, onClick: onClickSpy } });
await Primary.run({ args: { ...Primary.args, onClick: onClickSpy } });
const buttonElement = screen.getByRole('button');
buttonElement.click();
@ -31,7 +31,7 @@ test('onclick handler is called', () => {
const Primary = composeStory(PrimaryStory, meta);
const onClickSpy = jest.fn();
await Primary.play({ args: { ...Primary.args, onClick: onClickSpy } });
await Primary.run({ args: { ...Primary.args, onClick: onClickSpy } });
const buttonElement = screen.getByRole('button');
buttonElement.click();

View File

@ -13,7 +13,7 @@ describe(options.suite, () => {
stories.forEach(({ name, story }) => {
// ...Previously existing code
testFn(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));

View File

@ -12,13 +12,13 @@ test('renders in English', async () => {
{ globals: { locale: 'en' } } // 👈 Project annotations to override the locale
);
await Primary.play();
await Primary.run();
});
test('renders in Spanish', async () => {
const Primary = composeStory(PrimaryStory, meta, { globals: { locale: 'es' } });
await Primary.play();
await Primary.run();
});
```
@ -36,12 +36,12 @@ test('renders in English', async () => {
{ globals: { locale: 'en' } } // 👈 Project annotations to override the locale
);
await Primary.play();
await Primary.run();
});
test('renders in Spanish', async () => {
const Primary = composeStory(PrimaryStory, meta, { globals: { locale: 'es' } });
await Primary.play();
await Primary.run();
});
```

View File

@ -67,7 +67,7 @@ describe(options.suite, () => {
const testFn = story.parameters.storyshots?.skip ? test.skip : test;
testFn(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));
expect(document.body.firstChild).toMatchSnapshot();
@ -155,7 +155,7 @@ describe(options.suite, () => {
const testFn = story.parameters.storyshots?.skip ? test.skip : test;
testFn(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));
expect(document.body.firstChild).toMatchSnapshot();

View File

@ -9,7 +9,7 @@ const { Primary } = composeStories(stories);
test('renders and executes the play function', async () => {
// Mount story and run interactions
await Primary.play();
await Primary.run();
});
```
@ -23,6 +23,6 @@ const { Primary } = composeStories(stories);
test('renders and executes the play function', async () => {
// Mount story and run interactions
await Primary.play();
await Primary.run();
});
```

View File

@ -11,14 +11,14 @@ import * as stories from './Button.stories';
const { Primary, Secondary } = composeStories(stories);
test('renders primary button with default args', async () => {
await Primary.play();
await Primary.run();
const buttonElement = screen.getByText('Text coming from args in stories file!');
expect(buttonElement).not.toBeNull();
});
test('renders primary button with overridden props', async () => {
// You can override props by passing them in the context argument of the play function
await Primary.play({ args: { ...Primary.args, children: 'Hello world' } });
await Primary.run({ args: { ...Primary.args, children: 'Hello world' } });
const buttonElement = screen.getByText(/Hello world/i);
expect(buttonElement).not.toBeNull();
});
@ -37,14 +37,14 @@ import * as stories from './Button.stories';
const { Primary, Secondary } = composeStories(stories);
test('renders primary button with default args', async () => {
await Primary.play();
await Primary.run();
const buttonElement = screen.getByText('Text coming from args in stories file!');
expect(buttonElement).not.toBeNull();
});
test('renders primary button with overridden props', async () => {
// You can override props by passing them in the context argument of the play function
await Primary.play({ args: { ...Primary.args, children: 'Hello world' } });
await Primary.run({ args: { ...Primary.args, children: 'Hello world' } });
const buttonElement = screen.getByText(/Hello world/i);
expect(buttonElement).not.toBeNull();
});
@ -63,14 +63,14 @@ import * as stories from './Button.stories';
const { Primary, Secondary } = composeStories(stories);
test('renders primary button with default args', async () => {
await Primary.play();
await Primary.run();
const buttonElement = screen.getByText('Text coming from args in stories file!');
expect(buttonElement).not.toBeNull();
});
test('renders primary button with overridden props', async () => {
// You can override props by passing them in the context argument of the play function
await Primary.play({ args: { ...Primary.args, children: 'Hello world' } });
await Primary.run({ args: { ...Primary.args, children: 'Hello world' } });
const buttonElement = screen.getByText(/Hello world/i);
expect(buttonElement).not.toBeNull();
});

View File

@ -9,14 +9,14 @@ import meta, { Primary as PrimaryStory } from './Button.stories';
const Primary = composeStory(PrimaryStory, meta);
test('renders primary button with default args', async () => {
await Primary.play();
await Primary.run();
const buttonElement = screen.getByText('Text coming from args in stories file!');
expect(buttonElement).not.toBeNull();
});
test('renders primary button with overridden props', async () => {
await Primary.play({ args: { ...Primary.args, label: 'Hello world' } });
await Primary.run({ args: { ...Primary.args, label: 'Hello world' } });
const buttonElement = screen.getByText(/Hello world/i);
expect(buttonElement).not.toBeNull();
@ -34,14 +34,14 @@ import meta, { Primary as PrimaryStory } from './Button.stories';
const Primary = composeStory(PrimaryStory, meta);
test('renders primary button with default args', async () => {
await Primary.play();
await Primary.run();
const buttonElement = screen.getByText('Text coming from args in stories file!');
expect(buttonElement).not.toBeNull();
});
test('renders primary button with overridden props', async () => {
await Primary.play({ args: { ...Primary.args, label: 'Hello world' } });
await Primary.run({ args: { ...Primary.args, label: 'Hello world' } });
const buttonElement = screen.getByText(/Hello world/i);
expect(buttonElement).not.toBeNull();
@ -59,14 +59,14 @@ import meta, { Primary as PrimaryStory } from './Button.stories';
const Primary = composeStory(PrimaryStory, meta);
test('renders primary button with default args', async () => {
await Primary.play();
await Primary.run();
const buttonElement = screen.getByText('Text coming from args in stories file!');
expect(buttonElement).not.toBeNull();
});
test('renders primary button with overridden props', async () => {
await Primary.play({ args: { ...Primary.args, label: 'Hello world' } });
await Primary.run({ args: { ...Primary.args, label: 'Hello world' } });
const buttonElement = screen.getByText(/Hello world/i);
expect(buttonElement).not.toBeNull();

View File

@ -10,7 +10,7 @@ describe(options.suite, () => {
stories.forEach(({ name, story }) => {
// ...Previously existing code
testFn(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));

View File

@ -12,13 +12,13 @@ test('renders in English', async () => {
{ globals: { locale: 'en' } } // 👈 Project annotations to override the locale
);
await Primary.play();
await Primary.run();
});
test('renders in Spanish', async () => {
const Primary = composeStory(PrimaryStory, meta, { globals: { locale: 'es' } });
await Primary.play();
await Primary.run();
});
```
@ -36,13 +36,13 @@ test('renders in English', async () => {
{ globals: { locale: 'en' } } // 👈 Project annotations to override the locale
);
await Primary.play();
await Primary.run();
});
test('renders in Spanish', async () => {
const Primary = composeStory(PrimaryStory, meta, { globals: { locale: 'es' } });
await Primary.play();
await Primary.run();
});
```
@ -60,12 +60,12 @@ test('renders in English', async () => {
{ globals: { locale: 'en' } } // 👈 Project annotations to override the locale
);
await Primary.play();
await Primary.run();
});
test('renders in Spanish', async () => {
const Primary = composeStory(PrimaryStory, meta, { globals: { locale: 'es' } });
await Primary.play();
await Primary.run();
});
```

View File

@ -69,7 +69,7 @@ describe(options.suite, () => {
const testFn = story.parameters.storyshots?.skip ? test.skip : test;
testFn(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));
@ -160,7 +160,7 @@ describe(options.suite, () => {
const testFn = story.parameters.storyshots?.skip ? test.skip : test;
testFn(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));

View File

@ -8,7 +8,7 @@ const { Primary } = composeStories(stories);
test('renders and executes the play function', async () => {
// Mount story and run interactions
await Primary.play();
await Primary.run();
});
```
@ -22,7 +22,7 @@ const { Primary } = composeStories(stories);
test('renders and executes the play function', async () => {
// Mount story and run interactions
await Primary.play();
await Primary.run();
});
```
@ -36,6 +36,6 @@ const { Primary } = composeStories(stories);
test('renders and executes the play function', async () => {
// Mount story and run interactions
await Primary.play();
await Primary.run();
});
```

View File

@ -8,7 +8,7 @@ import Meta, { ValidForm as ValidFormStory } from './LoginForm.stories';
const ValidForm = composeStory(ValidFormStory, Meta);
test('Validates form', async () => {
await ValidForm.play();
await ValidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -31,7 +31,7 @@ import Meta, { ValidForm as ValidFormStory } from './LoginForm.stories';
const ValidForm = composeStory(ValidFormStory, Meta);
test('Validates form', async () => {
await ValidForm.play();
await ValidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -54,7 +54,7 @@ import Meta, { ValidForm as ValidFormStory } from './LoginForm.stories';
const ValidForm = composeStory(ValidFormStory, Meta);
test('Validates form', async () => {
await ValidForm.play();
await ValidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',
@ -77,7 +77,7 @@ import Meta, { ValidForm as ValidFormStory } from './LoginForm.stories';
const ValidForm = composeStory(ValidFormStory, Meta);
test('Validates form', async () => {
await ValidForm.play();
await ValidForm.run();
const buttonElement = screen.getByRole('button', {
name: 'Submit',

View File

@ -117,7 +117,7 @@ describe('Stories Snapshots', () => {
stories.forEach(({ name, story }) => {
test(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));
expect(document.body.firstChild).toMatchSnapshot();
@ -176,7 +176,7 @@ describe('Stories Snapshots', () => {
stories.forEach(({ name, story }) => {
test(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));
expect(document.body.firstChild).toMatchSnapshot();
@ -245,7 +245,7 @@ describe('Stories Snapshots', () => {
stories.forEach(({ name, story }) => {
test(name, async () => {
await story.play();
await story.run();
// Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot.
await new Promise((resolve) => setTimeout(resolve, 1));
expect(document.body.firstChild).toMatchSnapshot();

View File

@ -83,13 +83,14 @@ sidebar:
| Property | Type | Description |
| ---------- | ----------------------------------------- | ------------------------------------------------------------------------------------- |
| storyName | `string` | The story's name |
| args | `Record<string, any>` | The story's [args](../../writing-stories/args.mdx) |
| argTypes | `ArgType` | The story's [argTypes](../arg-types.mdx) |
| id | `string` | The story's id |
| tags | `string[]` | The story's [tags](../../writing-stories/tags.mdx) |
| parameters | `Record<string, any>` | The story's [parameters](../parameters.mdx) |
| play | `(context) => Promise<void> \| undefined` | Mounts and executes the [play function](#3-play) of a given story |
| play | `(context) => Promise<void> \| undefined` | Executes the play function of a given story |
| run | `(context) => Promise<void> \| undefined` | [Mounts and executes the play function](#3-run) of a given story |
| storyName | `string` | The story's name |
| tags | `string[]` | The story's [tags](../../writing-stories/tags.mdx) |
## composeStory
@ -202,7 +203,7 @@ sidebar:
To preview your stories in Storybook, Storybook runs a story pipeline, which includes applying project annotations, loading data, rendering the story, and playing interactions. This is a simplified version of the pipeline:
![A flow diagram of the story pipeline. First, set project annotations. Collect annotations (decorators, args, etc) which are exported by addons and the preview file. Second, compose story. Create renderable elements based on the stories passed onto the API. Third, play. Mount the component and execute all the story lifecycle hooks.](../../_assets/api/story-pipeline.png)
![A flow diagram of the story pipeline. First, set project annotations. Collect annotations (decorators, args, etc) which are exported by addons and the preview file. Second, compose story. Create renderable elements based on the stories passed onto the API. Third, run. Mount the component and execute all the story lifecycle hooks, including the play function.](../../_assets/api/story-pipeline.png)
When you want to reuse a story in a different environment, however, it's crucial to understand that all these steps make a story. The portable stories API provides you with the mechanism to recreate that story pipeline in your external environment:
@ -216,11 +217,11 @@ sidebar:
The story is prepared by running [`composeStories`](#composestories) or [`composeStory`](#composestory). The outcome is a renderable component that represents the render function of the story.
### 3. Play
### 3. Run
Finally, stories can prepare data they need (e.g. setting up some mocks or fetching data) before rendering by defining [loaders](../../writing-stories/loaders.mdx), [beforeEach](../../writing-tests/interaction-testing.mdx#run-code-before-each-story) or by having all the story code in the play function when using the [mount](../../writing-tests/interaction-testing.mdx#run-code-before-the-component-gets-rendered). In portable stories, all of these steps will be executed when you run the `play` method of the composed story.
Finally, stories can prepare data they need (e.g. setting up some mocks or fetching data) before rendering by defining [loaders](../../writing-stories/loaders.mdx), [beforeEach](../../writing-tests/interaction-testing.mdx#run-code-before-each-story) or by having all the story code in the play function when using the [mount](../../writing-tests/interaction-testing.mdx#run-code-before-the-component-gets-rendered). In portable stories, all of these steps will be executed when you call the `run` method of the composed story.
👉 For this, you use the [`composeStories`](#composestories) or [`composeStory`](#composestory) API. The composed story will return a `play` method to be called.
👉 For this, you use the [`composeStories`](#composestories) or [`composeStory`](#composestory) API. The composed story will return a `run` method to be called.
{/* prettier-ignore-start */}

View File

@ -116,7 +116,7 @@ sidebar:
## setProjectAnnotations
This API should be called once, before the tests run, in [`playwright/index.ts`](https://playwright.dev/docs/test-components#step-1-install-playwright-test-for-components-for-your-respective-framework). This will make sure that when `mount` is called, the project annotations are taken into account as well.
These are the configurations needed in the setup file:
- preview annotations: those defined in `.storybook/preview.ts`
- addon annotations (optional): those exported by addons

View File

@ -89,13 +89,14 @@ sidebar:
| Property | Type | Description |
| ---------- | ----------------------------------------- | ------------------------------------------------------------------------------------- |
| storyName | `string` | The story's name |
| args | `Record<string, any>` | The story's [args](../../writing-stories/args.mdx) |
| argTypes | `ArgType` | The story's [argTypes](../arg-types.mdx) |
| id | `string` | The story's id |
| tags | `string[]` | The story's [tags](../../writing-stories/tags.mdx) |
| parameters | `Record<string, any>` | The story's [parameters](../parameters.mdx) |
| play | `(context) => Promise<void> \| undefined` | Mounts and executes the [play function](#3-play) of a given story |
| play | `(context) => Promise<void> \| undefined` | Executes the play function of a given story |
| run | `(context) => Promise<void> \| undefined` | [Mounts and executes the play function](#3-run) of a given story |
| storyName | `string` | The story's name |
| tags | `string[]` | The story's [tags](../../writing-stories/tags.mdx) |
## composeStory
@ -216,7 +217,7 @@ sidebar:
To preview your stories in Storybook, Storybook runs a story pipeline, which includes applying project annotations, loading data, rendering the story, and playing interactions. This is a simplified version of the pipeline:
![A flow diagram of the story pipeline. First, set project annotations. Collect annotations (decorators, args, etc) which are exported by addons and the preview file. Second, compose story. Create renderable elements based on the stories passed onto the API. Third, play. Mount the component and execute all the story lifecycle hooks.](../../_assets/api/story-pipeline.png)
![A flow diagram of the story pipeline. First, set project annotations. Collect annotations (decorators, args, etc) which are exported by addons and the preview file. Second, compose story. Create renderable elements based on the stories passed onto the API. Third, run. Mount the component and execute all the story lifecycle hooks, including the play function.](../../_assets/api/story-pipeline.png)
When you want to reuse a story in a different environment, however, it's crucial to understand that all these steps make a story. The portable stories API provides you with the mechanism to recreate that story pipeline in your external environment:
@ -230,11 +231,11 @@ sidebar:
The story is prepared by running [`composeStories`](#composestories) or [`composeStory`](#composestory). The outcome is a renderable component that represents the render function of the story.
### 3. Play
### 3. Run
Finally, stories can prepare data they need (e.g. setting up some mocks or fetching data) before rendering by defining [loaders](../../writing-stories/loaders.mdx), [beforeEach](../../writing-tests/interaction-testing.mdx#run-code-before-each-story) or by having all the story code in the play function when using the [mount](../../writing-tests/interaction-testing.mdx#run-code-before-the-component-gets-rendered). In portable stories, all of these steps will be executed when you run the `play` method of the composed story.
Finally, stories can prepare data they need (e.g. setting up some mocks or fetching data) before rendering by defining [loaders](../../writing-stories/loaders.mdx), [beforeEach](../../writing-tests/interaction-testing.mdx#run-code-before-each-story) or by having all the story code in the play function when using the [mount](../../writing-tests/interaction-testing.mdx#run-code-before-the-component-gets-rendered). In portable stories, all of these steps will be executed when you call the `run` method of the composed story.
👉 For this, you use the [`composeStories`](#composestories) or [`composeStory`](#composestory) API. The composed story will return a `play` method to be called.
👉 For this, you use the [`composeStories`](#composestories) or [`composeStory`](#composestory) API. The composed story will return a `run` method to be called.
{/* prettier-ignore-start */}

View File

@ -20,7 +20,7 @@ const runTests = (name: string, storiesModule: any) => {
const composedStories = composeStories(storiesModule);
Object.entries(composedStories).forEach(([name, Story]: [any, any]) => {
it(`renders ${name}`, async () => {
await Story.play?.();
await Story.run?.();
expect(document.body).toMatchSnapshot();
});
});

View File

@ -92,7 +92,7 @@ describe('CSF3', () => {
it('renders with play function', async () => {
const CSF3InputFieldFilled = composeStory(stories.CSF3InputFieldFilled, stories.default);
await CSF3InputFieldFilled.play();
await CSF3InputFieldFilled.run();
const input = screen.getByTestId('input') as HTMLInputElement;
expect(input.value).toEqual('Hello world!');
@ -117,6 +117,6 @@ it('should pass with decorators that need addons channel', () => {
// Batch snapshot testing
const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName, Story]);
it.each(testCases)('Renders %s story', async (_storyName, Story) => {
await Story.play();
await Story.run();
expect(document.body).toMatchSnapshot();
});

View File

@ -55,7 +55,7 @@ describe('renders', () => {
expect(getByTestId('spy-data').textContent).toEqual('mockFn return value');
expect(getByTestId('loaded-data').textContent).toEqual('loaded data');
// spy assertions happen in the play function and should work
await LoaderStory.play!();
await LoaderStory.run!();
});
});
@ -114,7 +114,7 @@ describe('CSF3', () => {
it('renders with play function without canvas element', async () => {
const CSF3InputFieldFilled = composeStory(stories.CSF3InputFieldFilled, stories.default);
await CSF3InputFieldFilled.play();
await CSF3InputFieldFilled.run();
const input = screen.getByTestId('input') as HTMLInputElement;
expect(input.value).toEqual('Hello world!');
@ -126,7 +126,7 @@ describe('CSF3', () => {
const div = document.createElement('div');
document.body.appendChild(div);
await CSF3InputFieldFilled.play({ canvasElement: div });
await CSF3InputFieldFilled.run({ canvasElement: div });
const input = screen.getByTestId('input') as HTMLInputElement;
expect(input.value).toEqual('Hello world!');
@ -141,6 +141,6 @@ const testCases = Object.values(composeStories(stories)).map(
);
it.each(testCases)('Renders %s story', async (_storyName, Story) => {
if (_storyName === 'CSF2StoryWithLocale') return;
await Story.play();
await Story.run();
expect(document.body).toMatchSnapshot();
});

View File

@ -2,22 +2,44 @@
exports[`Renders CSF2Secondary story 1`] = `
<body>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
type="button"
>
label coming from story args!
</button>
<div>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
type="button"
>
label coming from story args!
</button>
</div>
</body>
`;
exports[`Renders CSF2StoryWithParamsAndDecorator story 1`] = `
<body>
<div
style="margin: 3em;"
>
<div>
<div
style="margin: 3em;"
>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
type="button"
>
foo
</button>
</div>
</div>
</body>
`;
exports[`Renders CSF3Button story 1`] = `
<body>
<div>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
@ -26,91 +48,46 @@ exports[`Renders CSF2StoryWithParamsAndDecorator story 1`] = `
foo
</button>
</div>
</body>
`;
exports[`Renders CSF3Button story 1`] = `
<body>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
type="button"
>
foo
</button>
</body>
`;
exports[`Renders CSF3ButtonWithRender story 1`] = `
<body>
<div>
<p
data-testid="custom-render"
>
I am a custom render function
</p>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
type="button"
>
foo
<div>
<p
data-testid="custom-render"
>
I am a custom render function
</p>
</button>
<button
class="storybook-button storybook-button--medium storybook-button--secondary"
style=""
type="button"
>
foo
</button>
</div>
</div>
</body>
`;
exports[`Renders CSF3InputFieldFilled story 1`] = `
<body>
<input
data-testid="input"
/>
<div>
<input
data-testid="input"
/>
</div>
</body>
`;
exports[`Renders CSF3Primary story 1`] = `
<body>
<button
class="storybook-button storybook-button--large storybook-button--primary"
style=""
type="button"
>
foo
</button>
</body>
`;
exports[`Renders LoaderStory story 1`] = `
<body>
<div>
<div
data-testid="loaded-data"
>
loaded data
</div>
<div
data-testid="spy-data"
>
mockFn return value
</div>
</div>
</body>
`;
exports[`Renders NewStory story 1`] = `
<body>
<div
style="margin: 3em;"
>
<button
class="storybook-button storybook-button--large storybook-button--primary"
style=""
@ -119,9 +96,48 @@ exports[`Renders NewStory story 1`] = `
foo
</button>
</div>
</body>
`;
exports[`Renders LoaderStory story 1`] = `
<body>
<div>
<div>
<div
data-testid="loaded-data"
>
loaded data
</div>
<div
data-testid="spy-data"
>
mockFn return value
</div>
</div>
</div>
</body>
`;
exports[`Renders NewStory story 1`] = `
<body>
<div>
<div
style="margin: 3em;"
>
<button
class="storybook-button storybook-button--large storybook-button--primary"
style=""
type="button"
>
foo
</button>
</div>
</div>
</body>
`;