Playwright CT 中的可移植故事
(⚠️实验性)
¥(⚠️ Experimental)
Playwright CT 的可移植故事 API 是实验性的。Playwright CT 本身也是实验性的。在即将发布的版本中,任何一个库都可能发生重大更改。
¥The portable stories API for Playwright CT is experimental. Playwright CT itself is also experimental. Breaking changes might occur in either library in upcoming releases.
可移植故事是 Storybook stories,可用于外部环境,例如 Playwright 组件测试 (CT)。
¥Portable stories are Storybook stories which can be used in external environments, such as Playwright Component Tests (CT).
通常,Storybook 会自动编写一个故事及其 annotations,作为 故事管道 的一部分。在 Playwright CT 中使用故事时,你可以使用 createTest
函数,该函数扩展了 Playwright 的测试功能以添加自定义 mount
机制,为你处理故事管道。
¥Normally, Storybook composes a story and its annotations automatically, as part of the story pipeline. When using stories in Playwright CT, you can use the createTest
function, which extends Playwright's test functionality to add a custom mount
mechanism, to take care of the story pipeline for you.
你的项目必须使用 React 18+ 才能使用 Playwright CT 的可移植故事 API。
¥Your project must be using React 18+ to use the portable stories API with Playwright CT.
使用 Next.js
?可移植故事 API 尚未在带有 Playwright CT 的 Next.js 中得到支持。
¥Using Next.js
? The portable stories API is not yet supported in Next.js with Playwright CT.
createTest
(⚠️实验性)
¥(⚠️ Experimental)
除了使用 Playwright 自己的 test
函数,你还可以使用 Storybook 的特殊 createTest
函数到 扩展 Playwright 的基本装置 并覆盖 mount
函数来加载、渲染和播放故事。此功能是实验性的,可能会发生变化。
¥Instead of using Playwright's own test
function, you can use Storybook's special createTest
function to extend Playwright's base fixture and override the mount
function to load, render, and play the story. This function is experimental and is subject to changes.
import { createTest } from '@storybook/react/experimental-playwright';
import { test as base } from '@playwright/experimental-ct-react';
// See explanation below for `.portable` stories file
import stories from './Button.stories.portable';
const test = createTest(base);
test('renders primary button', async ({ mount }) => {
// The mount function will execute all the necessary steps in the story,
// such as loaders, render, and play function
await mount(<stories.Primary />);
});
test('renders primary button with overridden props', async ({ mount }) => {
// You can pass custom props to your component via JSX
const component = await mount(<stories.Primary label="label from test" />);
await expect(component).toContainText('label from test');
await expect(component.getByRole('button')).toHaveClass(/storybook-button--primary/);
});
你在 Playwright 测试文件中编写的代码由 Playwright 转换和编排,其中部分代码在 Node 中执行,而其他部分在浏览器中执行。
¥The code which you write in your Playwright test file is transformed and orchestrated by Playwright, where part of the code executes in Node, while other parts execute in the browser.
因此,你必须在与你自己的测试文件不同的文件中编写故事:
¥Because of this, you have to compose the stories in a separate file than your own test file:
// Button.stories.portable.ts
// Replace your-renderer with the renderer you are using (e.g. react, vue3)
import { composeStories } from '@storybook/your-renderer';
import * as stories from './Button.stories';
// This function will be executed in the browser
// and compose all stories, exporting them in a single object
export default composeStories(stories);
然后,你可以在 Playwright 测试文件中导入编写的故事,如上例所示。
¥You can then import the composed stories in your Playwright test file, as in the example above.
类型
¥Type
createTest(
baseTest: PlaywrightFixture
) => PlaywrightFixture
参数
¥Parameters
baseTest
(必需)
¥(Required)
类型:PlaywrightFixture
¥Type: PlaywrightFixture
要使用的基本测试函数,例如 Playwright 的 test
。
¥The base test function to use, e.g. test
from Playwright.
返回
¥Return
类型:PlaywrightFixture
¥Type: PlaywrightFixture
具有自定义 mount
机制的 Storybook 特定测试函数。
¥A Storybook-specific test function with the custom mount
mechanism.
setProjectAnnotations
在测试运行之前,应在 playwright/index.ts
中调用此 API 一次。这将确保在调用 mount
时,项目注释也会被考虑在内。
¥This API should be called once, before the tests run, in playwright/index.ts
. 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:
-
预览注释:
.storybook/preview.ts
中定义的参数¥preview annotations: those defined in
.storybook/preview.ts
-
插件注释(可选):插件导出的参数
¥addon annotations (optional): those exported by addons
-
beforeAll:在所有测试(更多信息)之前运行的代码
¥beforeAll: code that runs before all tests (more info)
import { test } from '@playwright/experimental-ct-react';
import { setProjectAnnotations } from '@storybook/react';
// 👇 Import the exported annotations, if any, from the addons you're using; otherwise remove this
import * as addonAnnotations from 'my-addon/preview';
import * as previewAnnotations from './.storybook/preview';
const annotations = setProjectAnnotations([
previewAnnotations,
addonAnnotations,
]);
// Supports beforeAll hook from Storybook
test.beforeAll(annotations.beforeAll);
有时故事可能需要插件的 decorator 或 loader 才能正确渲染。例如,插件可以应用装饰器,将你的故事封装在必要的路由上下文中。在这种情况下,你必须在项目注释集中包含该插件的 preview
导出。请参阅上面示例中的 addonAnnotations
。
¥Sometimes a story can require an addon's decorator or loader to render properly. For example, an addon can apply a decorator that wraps your story in the necessary router context. In this case, you must include that addon's preview
export in the project annotations set. See addonAnnotations
in the example above.
注意:如果插件没有自动应用装饰器或加载器本身,而是将它们导出以供你在 .storybook/preview.js|ts
中手动应用(例如,从 @storybook/addon-themes 使用 withThemeFromJSXProvider
),那么你不需要做任何其他事情。它们已经包含在上面示例的 previewAnnotations
中。
¥Note: If the addon doesn't automatically apply the decorator or loader itself, but instead exports them for you to apply manually in .storybook/preview.js|ts
(e.g. using withThemeFromJSXProvider
from @storybook/addon-themes), then you do not need to do anything else. They are already included in the previewAnnotations
in the example above.
类型
¥Type
(projectAnnotations: ProjectAnnotation | ProjectAnnotation[]) => ProjectAnnotation
参数
¥Parameters
projectAnnotations
(必需)
¥(Required)
类型:ProjectAnnotation | ProjectAnnotation[]
¥Type: ProjectAnnotation | ProjectAnnotation[]
一组项目 annotations(在 .storybook/preview.js|ts
中定义的)或一组项目注释,将应用于所有组合的故事。
¥A set of project annotations (those defined in .storybook/preview.js|ts
) or an array of sets of project annotations, which will be applied to all composed stories.
注释
¥Annotations
注释是应用于故事的元数据,如 args、decorators、loaders 和 播放函数。它们可以为特定故事、组件的所有故事或项目中的所有故事定义。
¥Annotations are the metadata applied to a story, like args, decorators, loaders, and play functions. They can be defined for a specific story, all stories for a component, or all stories in the project.
故事管道
¥Story pipeline
要预览你的故事,Storybook 会运行一个故事管道,其中包括应用项目注释、加载数据、渲染故事和播放交互。这是管道的简化版本:
¥To preview your stories, 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:
但是,当你想在不同的环境中重用故事时,了解所有这些步骤构成一个故事至关重要。可移植故事 API 为你提供了在外部环境中重新创建该故事管道的机制:
¥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:
1. 应用项目级注释
¥ Apply project-level annotations
注释 来自故事本身、故事的组件和项目。项目级注释是在你的 .storybook/preview.js
文件中和你正在使用的插件中定义的注释。在可移植故事中,这些注释不会自动应用 - 你必须自己应用它们。
¥Annotations come from the story itself, that story's component, and the project. The project-level annotations are those defined in your .storybook/preview.js
file and by addons you're using. In portable stories, these annotations are not applied automatically — you must apply them yourself.
👉 为此,你可以使用 setProjectAnnotations
API。
¥👉 For this, you use the setProjectAnnotations
API.
2. 准备、加载、渲染和播放
¥ Prepare, load, render, and play
故事管道包括准备故事、加载数据、渲染故事和 播放交互。在 Playwright CT 中的可移植故事中,mount
函数会为你处理这些步骤。
¥The story pipeline includes preparing the story, loading data, rendering the story, and playing interactions. In portable stories within Playwright CT, the mount
function takes care of these steps for you.
👉 为此,你可以使用 createTest
API。
¥👉 For this, you use the createTest
API.
如果你的播放函数包含断言(例如 expect
调用),则当这些断言失败时,你的测试将失败。
¥If your play function contains assertions (e.g. expect
calls), your test will fail when those assertions fail.
覆盖全局变量
¥Overriding globals
如果你的故事基于 globals 的行为不同(例如以英语或西班牙语渲染文本),你可以在编写故事时通过覆盖项目注释来在可移植故事中定义这些全局值:
¥If your stories behave differently based on globals (e.g. rendering text in English or Spanish), you can define those global values in portable stories by overriding project annotations when composing a story:
import { composeStory } from '@storybook/react';
import meta, { Primary } from './Button.stories';
export const PrimaryEnglish = composeStory(
Primary,
meta,
{ globals: { locale: 'en' } } // 👈 Project annotations to override the locale
);
export const PrimarySpanish = composeStory(Primary, meta, { globals: { locale: 'es' } });
然后,你可以使用 createTest
函数在你的 Playwright 测试文件中使用这些组合的故事。
¥You can then use those composed stories in your Playwright test file using the createTest
function.