Docs
Storybook Docs

测试运行器

Storybook 测试运行器将你的所有故事变成可执行测试。它由 JestPlaywright 提供支持。

¥Storybook test runner turns all of your stories into executable tests. It is powered by Jest and Playwright.

这些测试在实时浏览器中运行,可以通过 命令行 或你的 CI 服务器 执行。

¥These tests run in a live browser and can be executed via the command line or your CI server.

设置

¥Setup

测试运行器是一个独立的、与框架无关的实用程序,与你的 Storybook 并行运行。你需要采取一些额外步骤来正确设置它。下面详细介绍了我们对配置和执行它的建议。

¥The test-runner is a standalone, framework-agnostic utility 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 it.

npm install @storybook/test-runner --save-dev

更新你的 package.json 脚本并启用测试运行器。

¥Update your package.json scripts and enable the test runner.

{
  "scripts": {
    "test-storybook": "test-storybook"
  }
}

使用以下方式启动 Storybook:

¥Start your Storybook with:

npm run storybook

Storybook 的测试运行器需要本地运行的 Storybook 实例或已发布的 Storybook 来运行所有现有测试。

¥Storybook's test runner requires either a locally running Storybook instance or a published Storybook to run all the existing tests.

最后,打开一个新的终端窗口并运行测试运行器:

¥Finally, open a new terminal window and run the test-runner with:

npm run test-storybook

配置

¥Configure

测试运行器为 Storybook 提供零配置支持。但是,你可以运行 test-storybook --eject 进行更细粒度的控制。它会在项目的根目录生成一个 test-runner-jest.config.js 文件,你可以对其进行修改。此外,你可以扩展生成的配置文件并提供 testEnvironmentOptions,因为测试运行器也在后台使用 jest-playwright

¥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 as the test runner also uses jest-playwright under the hood.

CLI 选项

¥CLI Options

测试运行器由 Jest 提供支持,并接受其 CLI 选项 的子集(例如 --watch--maxWorkers)。如果你已经在项目中使用了任何这些标志,你应该能够将它们迁移到 Storybook 的测试运行器中而不会出现任何问题。下面列出了所有可用的标志及其使用示例。

¥The test-runner is powered by Jest and accepts a subset of its CLI options (for example, --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.

选项描述
--help输出使用信息
test-storybook --help
-s, --index-json在索引 json 模式下运行。自动检测(需要兼容的 Storybook)
test-storybook --index-json
--no-index-json禁用索引 json 模式
test-storybook --no-index-json
-c, --config-dir [dir-name]从哪里加载 Storybook 配置的目录
test-storybook -c .storybook
--watch在监视模式
test-storybook --watch 下运行
--watchAll监视文件更改并在发生更改时重新运行所有测试。
test-storybook --watchAll
--coverage在你的故事和组件
test-storybook --coverage 上运行 覆盖测试
--coverageDirectory写入覆盖率报告输出的目录
test-storybook --coverage --coverageDirectory coverage/ui/storybook
--url定义要在其中运行测试的 URL。适用于自定义 Storybook URL
test-storybook --url http://the-storybook-url-here.com
--browsers定义要在其中运行测试的浏览器。一个或多个:chromium、firefox、webkit
test-storybook --browsers firefox chromium
--maxWorkers [amount]指定工作池将为运行测试
test-storybook --maxWorkers=2 生成的最大工作器数量
--testTimeout [amount]定义测试在自动标记为失败之前可以运行的最长时间(以毫秒为单位)。适用于长时间运行的测试
test-storybook --testTimeout=60000
--no-cache禁用缓存
test-storybook --no-cache
--clearCache删除 Jest 缓存目录,然后退出而不运行测试
test-storybook --clearCache
--verbose使用测试套件层次结构显示单个测试结果
test-storybook --verbose
-u, --updateSnapshot使用此标志重新记录此测试运行期间失败的每个快照
test-storybook -u
--eject创建一个本地配置文件以覆盖测试运行器
test-storybook --eject 的默认值
--json以 JSON 格式打印测试结果。此模式将把所有其他测试输出和用户消息发送到 stderr。
test-storybook --json
--outputFile当还指定 --json 选项时,将测试结果写入文件。
test-storybook --json --outputFile results.json
--junit表示应在 junit 文件中报告测试信息。
test-storybook --**junit**
--ci与自动存储新快照的常规行为不同,它将无法通过测试并需要使用 --updateSnapshot 运行 Jest。
test-storybook --ci
--shard [index/count]需要 CI。将测试套件执行拆分为多台机器
test-storybook --shard=1/8
--failOnConsole使测试在浏览器控制台错误
test-storybook --failOnConsole 上失败
--includeTags实验性功能
定义要测试的故事子集(如果它们与启用的 tags 匹配)。
test-storybook --includeTags="test-only, pages"
--excludeTags实验性功能
如果故事与提供的 tags 匹配,则阻止测试它们。
test-storybook --excludeTags="no-tests, tokens"
--skipTags实验性功能
配置测试运行器以跳过与提供的 tags 匹配的故事的运行测试。
test-storybook --skipTags="skip-test, layout"
npm run test-storybook -- --watch

测试构建速度提高 2-4 倍

¥Run tests against a deployed Storybook

默认情况下,测试运行器假定你正在针对端口 6006 上的本地服务 Storybook 运行它。如果你想定义针对已部署的 Storybook 运行的目标 URL,你可以使用 --url 标志:

¥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:

npm run test-storybook -- --url https://the-storybook-url-here.com

或者,你可以设置 TARGET_URL 环境变量并运行测试运行器:

¥Alternatively, you can set the TARGET_URL environment variable and run the test-runner:

TARGET_URL=https://the-storybook-url-here.com yarn test-storybook

设置 CI 以运行测试

¥Set up CI to run tests

你还可以配置测试运行器以在 CI 环境中运行测试。下面记录了一些帮助你入门的秘诀。

¥You can also configure the test-runner to run tests on a CI environment. Documented below are some recipes to help you get started.

通过 Github Actions 部署针对已部署的 Storybooks 运行

¥Run against deployed Storybooks via Github Actions deployment

如果你使用 VercelNetlify 等服务发布 Storybook,它们会在 GitHub Actions 中发出 deployment_status 事件。你可以使用它并将 deployment_status.target_url 设置为 TARGET_URL 环境变量。操作方法如下:

¥If you're publishing your Storybook with services such as Vercel or Netlify, 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:

# .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@v4
      - uses: actions/setup-node@v4
        with:
          node-version-file: '.nvmrc'
      - 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 }}'

已发布的 Storybook 必须公开可用,此示例才能正常工作。如果需要身份验证,我们建议使用秘诀 below 运行测试服务器。

¥The published Storybook must be publicly available for this example to work. We recommend running the test server using the recipe below if it requires authentication.

针对运行未部署的 Storybook

¥Run against non-deployed Storybooks

你可以使用你的 CI 提供程序(例如,GitHub ActionsGitLab 管道CircleCI)来针对你构建的 Storybook 构建和运行测试运行器。这是一个依赖第三方库的秘诀,也就是说,concurrentlyhttp-serverwait-on 来构建 Storybook 并使用测试运行器运行测试。

¥You can use your CI provider (for example, GitHub Actions, GitLab Pipelines, CircleCI) to build and run the test runner against your built Storybook. Here's a recipe that relies on third-party libraries, that is to say, concurrently, http-server, and wait-on to build Storybook and run tests with the test-runner.

# .github/workflows/storybook-tests.yml
 
name: 'Storybook Tests'
on: push
jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version-file: '.nvmrc'
      - 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:127.0.0.1:6006 && yarn test-storybook"

默认情况下,Storybook 将 build 输出到 storybook-static 目录。如果你使用的是不同的构建目录,则需要相应地调整秘诀。

¥By default, Storybook outputs the build to the storybook-static directory. If you're using a different build directory, you'll need to adjust the recipe accordingly.

Chromatic 和 Test runner 有什么区别?

¥What's the difference between Chromatic and Test runner?

测试运行器是一种通用测试工具,可以在本地或 CI 上运行,并可配置或扩展以运行各种测试。

¥The 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 是一项基于云的服务,无需设置测试运行器即可运行 visual组件测试(以及即将推出的可访问性测试)。它还与你的 git 提供程序同步并管理私有项目的访问控制。

¥Chromatic is a cloud-based service that runs visual and component tests (and soon accessibility tests) without setting up the test runner. It also syncs with your git provider and manages access control for private projects.

但是,在某些情况下,你可能希望将测试运行器和 Chromatic 配对。

¥However, you might want to pair the test runner and Chromatic in some cases.

  • 在本地使用它并在你的 CI 上使用 Chromatic。

    ¥Use it locally and Chromatic on your CI.

  • 使用 Chromatic 进行视觉和组件测试,并使用测试运行器运行其他自定义测试。

    ¥Use Chromatic for visual and component tests and run other custom tests using the test runner.

高级配置

¥Advanced configuration

测试钩子 API

¥Test hook API

测试运行器渲染故事并执行其 播放函数(如果存在)。但是,某些行为无法通过在浏览器中执行的播放函数实现。例如,如果你希望测试运行器为你拍摄视觉快照,这可以通过 Playwright/Jest 实现,但必须在 Node 中执行。

¥The test-runner renders a story and executes its play function 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.

测试运行器导出可以全局覆盖的测试钩子,以启用可视化或 DOM 快照等用例。这些钩子使你可以在故事渲染之前和之后访问测试生命周期。下面列出了可用的钩子及其使用方法概述。

¥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.

钩子描述
prepare为测试准备浏览器
async prepare({ page, browserContext, testRunnerConfig }) {}
setup在所有测试运行之前执行一次
setup() {}
preVisit在首次访问故事并在浏览器中渲染之前执行
async preVisit(page, context) {}
postVisit在访问故事并完全渲染后执行
async postVisit(page, context) {}

这些测试钩子是实验性的,可能会发生重大变化。我们鼓励你在故事的 播放函数 中尽可能多地进行测试。

¥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.

要启用 hooks API,你需要在 Storybook 目录中添加一个新的配置文件并按如下方式设置它们:

¥To enable the hooks API, you'll need to add a new configuration file inside your Storybook directory and set them up as follows:

.storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner';
 
const config: TestRunnerConfig = {
  // Hook that is executed before the test runner starts running tests
  setup() {
    // Add your configuration here.
  },
  /* Hook to execute before a story is initially visited before being rendered in the browser.
   * 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 preVisit(page, context) {
    // Add your configuration here.
  },
  /* Hook to execute after a story is visited and fully 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 postVisit(page, context) {
    // Add your configuration here.
  },
};
 
export default config;

除了 setup 函数之外,所有其他函数都异步运行。preVisitpostVisit 函数都包含两个附加参数,Playwright 页面 和上下文对象,其中包含故事的 idtitlename

¥Except for the setup function, all other functions run asynchronously. Both preVisit and postVisit functions include two additional arguments, a Playwright page and a context object which contains the id, title, and the name of the story.

当测试运行器执行时,你现有的测试将经历以下生命周期:

¥When the test-runner executes, your existing tests will go through the following lifecycle:

  • setup 函数在所有测试运行之前执行。

    ¥The setup function is executed before all the tests run.

  • 生成包含所需信息的上下文对象。

    ¥The context object is generated containing the required information.

  • Playwright 导航到故事页面。

    ¥Playwright navigates to the story's page.

  • preVisit 函数已执行。

    ¥The preVisit function is executed.

  • 故事被渲染,并且任何现有的 play 函数都被执行。

    ¥The story is rendered, and any existing play functions are executed.

  • postVisit 函数已执行。

    ¥The postVisit function is executed.

(实验性)过滤测试

¥(Experimental) Filter tests

当你在 Storybook 上运行测试运行器时,它会默认测试每个故事。但是,如果你想过滤测试,则可以使用 tags 配置选项。Storybook 最初引入此功能是为了为故事生成 自动文档。但可以进一步扩展以配置测试运行器,使用类似的配置选项或通过 CLI 标志(例如 --includeTags--excludeTags--skipTags)根据提供的标签运行测试,仅适用于最新稳定版本(0.15 或更高版本)。下面列出了可用的选项及其使用方法概述。

¥When you run the test-runner on Storybook, it tests every story by default. However, if you want to filter the tests, you can use the tags configuration option. Storybook originally introduced this feature to generate automatic documentation for stories. But it can be further extended to configure the test-runner to run tests according to the provided tags using a similar configuration option or via CLI flags (e.g., --includeTags, --excludeTags, --skipTags), only available with the latest stable release (0.15 or higher). Listed below are the available options and an overview of how to use them.

选项描述
exclude防止故事与提供的标签匹配而被测试。
include定义仅当故事子集与启用的标签匹配时才进行测试。
skip如果故事与提供的标签匹配,则跳过对故事的测试。
.storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner';
 
const config: TestRunnerConfig = {
  tags: {
    include: ['test-only', 'pages'],
    exclude: ['no-tests', 'tokens'],
    skip: ['skip-test', 'layout'],
  },
};
 
export default config;

使用 CLI 标志运行测试优先于配置文件中提供的选项,并将覆盖配置文件中的可用选项。

¥Running tests with the CLI flags takes precedence over the options provided in the configuration file and will override the available options in the configuration file.

禁用测试

¥Disabling tests

如果你想阻止测试运行器测试特定的故事,你可以使用自定义标签配置你的故事,将其启用到测试运行器配置文件中,或者使用 --excludeTags CLI 标志运行测试运行器并将其排除在测试之外。当你想要排除尚未准备好进行测试或与你的测试无关的故事时,这很有用。例如:

¥If you want to prevent specific stories from being tested by the test-runner, you can configure your story with a custom tag, enable it to the test-runner configuration file or run the test-runner with the --excludeTags CLI flag and exclude them from testing. This is helpful when you want to exclude stories that are not yet ready for testing or are irrelevant to your tests. For example:

MyComponent.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { MyComponent } from './MyComponent';
 
const meta: Meta<typeof MyComponent> = {
  component: MyComponent,
  tags: ['no-tests'], // 👈 Provides the `no-tests` tag to all stories in this file
};
 
export default meta;
type Story = StoryObj<typeof MyComponent>;
 
export const ExcludeStory: Story = {
  //👇 Adds the `no-tests` tag to this story to exclude it from the tests when enabled in the test-runner configuration
  tags: ['no-tests'],
};

(选项

¥Run tests for a subset of stories

要允许测试运行器仅对特定故事或故事子集运行测试,你可以使用自定义标签配置故事,在测试运行器配置文件中启用它,或使用 --includeTags CLI 标志运行测试运行器并将它们包含在测试中。例如,如果你想基于 test-only 标签运行测试,你可以按如下方式调整配置:

¥To allow the test-runner only to run tests on a specific story or subset of stories, you can configure the story with a custom tag, enable it in the test-runner configuration file or run the test-runner with the --includeTags CLI flag and include them in your tests. For example, if you wanted to run tests based on the test-only tag, you can adjust your configuration as follows:

MyComponent.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { MyComponent } from './MyComponent';
 
const meta: Meta<typeof MyComponent> = {
  component: MyComponent,
  tags: ['test-only'], // 👈 Provides the `test-only` tag to all stories in this file
};
 
export default meta;
type Story = StoryObj<typeof MyComponent>;
 
export const IncludeStory: Story = {
  //👇 Adds the `test-only` tag to this story to be included in the tests when enabled in the test-runner configuration
  tags: ['test-only'],
};

为组件的故事应用标签应该在组件级别(使用 meta)或故事级别完成。Storybook 不支持跨故事导入标签,并且无法按预期工作。

¥Applying tags for the component's stories should either be done at the component level (using meta) or at the story level. Importing tags across stories is not supported in Storybook and won't work as intended.

跳过测试

¥Skip tests

如果你想跳过对特定故事或故事子集运行测试,你可以使用自定义标签配置你的故事,在测试运行器配置文件中启用它,或者使用 --skipTags CLI 标志运行测试运行器。使用此选项运行测试将导致测试运行器忽略并在测试结果中相应地标记它们,表明测试被暂时禁用。例如:

¥If you want to skip running tests on a particular story or subset of stories, you can configure your story with a custom tag, enable it in the test-runner configuration file, or run the test-runner with the --skipTags CLI flag. Running tests with this option will cause the test-runner to ignore and flag them accordingly in the test results, indicating that the tests are temporarily disabled. For example:

MyComponent.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { MyComponent } from './MyComponent';
 
const meta: Meta<typeof MyComponent> = {
  component: MyComponent,
  tags: ['skip-test'], // 👈 Provides the `skip-test` tag to all stories in this file
};
 
export default meta;
type Story = StoryObj<typeof MyComponent>;
 
export const SkipStory: Story = {
  //👇 Adds the `skip-test` tag to this story to allow it to be skipped in the tests when enabled in the test-runner configuration
  tags: ['skip-test'],
};

已部署 Storybooks 的身份验证

¥Authentication for deployed Storybooks

如果你使用需要身份验证来托管 Storybook 的安全托管提供商,则可能需要设置 HTTP 标头。这主要是因为测试运行器如何通过获取请求和 Playwright 检查实例的状态及其故事的索引。为此,你可以修改测试运行器配置文件以包含 getHttpHeaders 函数。此函数将获取调用和页面访问的 URL 作为输入,并返回包含需要设置的标头的对象。

¥If you use a secure hosting provider that requires authentication to host your Storybook, you may need to set HTTP headers. This is mainly because of how the test runner checks the status of the instance and the index of its stories through fetch requests and Playwright. To do this, you can modify the test-runner configuration file to include the getHttpHeaders function. This function takes the URL of the fetch calls and page visits as input and returns an object containing the headers that need to be set.

.storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner';
 
const config: TestRunnerConfig = {
  getHttpHeaders: async (url) => {
    const token = url.includes('prod') ? 'prod-token' : 'dev-token';
    return {
      Authorization: `Bearer ${token}`,
    };
  },
};
 
export default config;

助手

¥Helpers

测试运行器导出一些辅助程序,这些辅助程序可用于通过访问 Storybook 的内部结构(例如 argsparameters)使你的测试更具可读性和可维护性。下面列出了可用的辅助程序及其使用方法概述。

¥The test-runner exports a few helpers that can be used to make your tests more readable and maintainable by accessing Storybook's internals (e.g., args, parameters). Listed below are the available helpers and an overview of how to use them.

.storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner';
import { getStoryContext, waitForPageReady } from '@storybook/test-runner';
 
const config: TestRunnerConfig = {
  // Hook that is executed before the test runner starts running tests
  setup() {
    // Add your configuration here.
  },
  /* Hook to execute before a story is initially visited before being rendered in the browser.
   * 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 preVisit(page, context) {
    // Add your configuration here.
  },
  /* Hook to execute after a story is visited and fully 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 postVisit(page, context) {
    // Get the entire context of a story, including parameters, args, argTypes, etc.
    const storyContext = await getStoryContext(page, context);
 
    // This utility function is designed for image snapshot testing. It will wait for the page to be fully loaded, including all the async items (e.g., images, fonts, etc.).
    await waitForPageReady(page);
 
    // Add your configuration here.
  },
};
 
export default config;

使用测试运行器访问故事信息

¥Accessing story information with the test-runner

如果你需要访问有关故事的信息(例如其参数),测试运行器包含一个名为 getStoryContext 的辅助函数,你可以使用它来检索它。然后,你可以根据需要使用它来进一步自定义测试。例如,如果你需要配置 Playwright 的页面 视口大小 以使用故事参数中定义的视口大小,你可以按如下方式操作:

¥If you need to access information about the story, such as its parameters, the test-runner includes a helper function named getStoryContext that you can use to retrieve it. You can then use it to customize your tests further as needed. For example, if you need to configure Playwright's page viewport size to use the viewport size defined in the story's parameters, you can do so as follows:

.storybook/test-runner.js
import type { TestRunnerConfig } from '@storybook/test-runner';
import { getStoryContext } from '@storybook/test-runner';
 
const { MINIMAL_VIEWPORTS } = require('@storybook/addon-viewport');
 
const DEFAULT_VIEWPORT_SIZE = { width: 1280, height: 720 };
 
const config: TestRunnerConfig = {
  async preVisit(page, story) {
    // Accesses the story's parameters and retrieves the viewport used to render it
    const context = await getStoryContext(page, story);
    const viewportName = context.parameters?.viewport?.defaultViewport;
    const viewportParameter = MINIMAL_VIEWPORTS[viewportName];
 
    if (viewportParameter) {
      const viewportSize = Object.entries(viewportParameter.styles).reduce(
        (acc, [screen, size]) => ({
          ...acc,
          // Converts the viewport size from percentages to numbers
          [screen]: parseInt(size),
        }),
        {},
      );
      // Configures the Playwright page to use the viewport size
      page.setViewportSize(viewportSize);
    } else {
      page.setViewportSize(DEFAULT_VIEWPORT_SIZE);
    }
  },
};
 
export default config;

使用资源

¥Working with assets

如果你正在运行一组特定的测试(例如,图片快照测试),测试运行器会提供一个名为 waitForPageReady 的辅助函数,你可以使用它来确保页面在运行测试之前已完全加载并准备就绪。例如:

¥If you're running a specific set of tests (e.g., image snapshot testing), the test-runner provides a helper function named waitForPageReady that you can use to ensure the page is fully loaded and ready before running the test. For example:

.storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner';
 
import { waitForPageReady } from '@storybook/test-runner';
 
import { toMatchImageSnapshot } from 'jest-image-snapshot';
 
const customSnapshotsDir = `${process.cwd()}/__snapshots__`;
 
const config: TestRunnerConfig = {
  setup() {
    expect.extend({ toMatchImageSnapshot });
  },
  async postVisit(page, context) {
    // Awaits for the page to be loaded and available including assets (e.g., fonts)
    await waitForPageReady(page);
 
    // Generates a snapshot file based on the story identifier
    const image = await page.screenshot();
    expect(image).toMatchImageSnapshot({
      customSnapshotsDir,
      customSnapshotIdentifier: context.id,
    });
  },
};
 
export default config;

Index.json 模式

¥Index.json mode

测试运行器在测试本地 Storybook 时将你的故事文件转换为测试。对于远程 Storybook,它使用 Storybook 的 index.json(以前称为 stories.json)文件(所有故事的静态索引)来运行测试。

¥The test-runner transforms your story files into tests when testing a local Storybook. For a remote Storybook, it uses the Storybook's index.json (formerly stories.json) file (a static index of all the stories) to run the tests.

为什么?

¥Why?

假设你遇到本地和远程 Storybook 不同步的情况,或者你甚至可能无法访问代码。在这种情况下,index.json 文件保证是你正在测试的已部署 Storybook 的最准确表示。要使用此功能测试本地 Storybook,请使用 --index-json 标志,如下所示:

¥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 index.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 --index-json flag as follows:

npm run test-storybook -- --index-json

index.json 模式与 watch 模式不兼容。

¥The index.json mode is not compatible with the watch mode.

如果你需要禁用它,请使用 --no-index-json 标志:

¥If you need to disable it, use the --no-index-json flag:

npm run test-storybook -- --no-index-json

如何检查我的 Storybook 是否有 index.json 文件?

¥How do I check if my Storybook has a index.json file?

Index.json 模式需要 index.json 文件。打开浏览器窗口并导航到你部署的 Storybook 实例(例如,https://your-storybook-url-here.com/index.json)。你应该看到一个以 "v": 3 键开头的 JSON 文件,紧接着是另一个名为 "stories" 的键,其中包含故事 ID 到 JSON 对象的映射。如果是这种情况,你的 Storybook 支持 index.json 模式

¥Index.json mode requires a index.json file. Open a browser window and navigate to your deployed Storybook instance (for example, https://your-storybook-url-here.com/index.json). You should see a JSON file that starts with a "v": 3 key, immediately followed by another key called "stories", which contains a map of story IDs to JSON objects. If that is the case, your Storybook supports index.json mode.


故障排除

¥Troubleshooting

测试构建速度提高 2-4 倍

¥The test runner seems flaky and keeps timing out

如果你的测试超时并显示以下消息:

¥If your tests time out with the following message:

Timeout - Async callback was not invoked within the 15000 ms timeout specified by jest.setTimeout

可能是 Playwright 无法处理测试你项目中的故事数量。也许你有大量的故事,或者你的 CI 环境的 RAM 配置真的很低。在这种情况下,你应该通过调整命令来限制并行运行的工作程序数量,如下所示:

¥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:

{
  "scripts": {
    "test-storybook:ci": "yarn test-storybook --maxWorkers=2"
  }
}

CLI 中的错误输出太短

¥The error output in the CLI is too short

默认情况下,测试运行器会在 1000 个字符处截断错误输出,你可以在浏览器中的 Storybook 中直接检查完整输出。但是,如果你想更改该限制,你可以通过将 DEBUG_PRINT_LIMIT 环境变量设置为你选择的数字来实现,例如 DEBUG_PRINT_LIMIT=5000 yarn test-storybook

¥By default, the test runner truncates error outputs at 1000 characters, and you can check the full output directly in Storybook in the browser. However, if you want to change that limit, you can do so by setting the DEBUG_PRINT_LIMIT environment variable to a number of your choosing, for example, DEBUG_PRINT_LIMIT=5000 yarn test-storybook.

在其他 CI 环境中运行测试运行器

¥Run the test runner in other CI environments

由于测试运行器基于 Playwright,你可能需要根据你的 CI 设置使用特定的 docker 镜像或其他配置。在这种情况下,你可以参考 Playwright CI 文档 了解更多信息。

¥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 for more information.

按标签过滤的测试执行不正确

¥Tests filtered by tags are incorrectly executed

如果你已启用使用标签过滤测试并为 includeexclude 列表提供了类似的标签,则测试运行器将根据 exclude 列表执行测试并忽略 include 列表。为了避免这种情况,请确保提供给 includeexclude 列表的标签不同。

¥If you've enabled filtering tests with tags and provided similar tags to the include and exclude lists, the test-runner will execute the tests based on the exclude list and ignore the include list. To avoid this, make sure the tags provided to the include and exclude lists differ.

测试运行器不支持开箱即用的 Yarn PnP

¥The test runner doesn't support Yarn PnP out of the box

如果你在运行较新版本的 Yarn 且启用了 Plug'n'Play (PnP) 的项目中启用了测试运行器,则测试运行器可能无法按预期工作,并且在运行测试时可能会生成以下错误:

¥If you've enabled the test-runner in a project running on a newer version of Yarn with Plug'n'Play (PnP) enabled, the test-runner might not work as expected and may generate the following error when running tests:

PlaywrightError: jest-playwright-preset: Cannot find playwright package to use chromium

这是因为测试运行器使用社区维护的包 jest-playwright-preset,它仍然需要支持此功能。要解决此问题,你可以将 nodeLinker 设置切换为 node-modules 或将 Playwright 安装为项目中的直接依赖,然后通过 install 命令添加浏览器二进制文件。

¥This is due to the test-runner using the community-maintained package jest-playwright-preset that still needs to support this feature. To solve this, you can either switch the nodeLinker setting to node-modules or install Playwright as a direct dependency in your project, followed by adding the browser binaries via the install command.

了解其他 UI 测试

¥Learn about other UI tests