news 2026/9/10 23:11:28

Storybook Test Runner 辅助函数与测试钩子实战指南:getStoryContext 与 waitForPageReady 的完整用法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Storybook Test Runner 辅助函数与测试钩子实战指南:getStoryContext 与 waitForPageReady 的完整用法

Storybook Test Runner 辅助函数与测试钩子实战指南:getStoryContext 与 waitForPageReady 的完整用法

【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook

Storybook 的官方 Test Runner(@storybook/test-runner)把每一个 story 都变成可在真实浏览器中运行的自动化测试,而要让这些测试具备读取 story 内部数据、等待页面资源完全加载等高级能力,就需要借助它导出的测试钩子(Test Hook API)与辅助函数(Helpers)。本文基于仓库中 test-runner-helper-function.md 配置片段,结合完整文档 test-runner.mdx,系统讲解setup/preVisit/postVisit钩子以及getStoryContextwaitForPageReady两个辅助函数的原理、配置与实战用法,读完即可在自己的项目中写出可复制的测试定制方案。

Test Runner 与辅助函数在 Storybook 测试体系中的位置

Storybook Test Runner 是一个框架无关、与 Storybook 并行运行的独立工具,底层由 Jest 和 Playwright 驱动:

  • 对于没有 play function 的 story:它验证 story 是否能无错误地渲染;
  • 对于带有 play function 的 story:它额外检查 play function 中的错误,并确认所有断言均通过。

这些测试在真实的浏览器中运行,可通过命令行(CLI)或 CI 服务器执行。文档明确指出,在基于 Vite 的 Storybook 框架中,官方推荐使用更快、更现代的 Vitest 插件(Vitest addon) 替代 Test Runner,但 Test Runner 仍适用于 Webpack 等场景,且其钩子与辅助函数的设计思路在测试扩展中通用。

# 安装 Test Runner(开发依赖) npm install @storybook/test-runner --save-dev # pnpm: pnpm add --save-dev @storybook/test-runner # yarn: yarn add --dev @storybook/test-runner

安装后在package.json中添加脚本:

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

Test Runner 需要一个本地运行中或已发布的 Storybook 实例,先启动 Storybook,再在另一个终端窗口执行yarn test-storybook即可运行全部 story 测试。若需更细粒度的控制,可运行test-storybook --eject,它会在项目根目录生成可修改的test-runner-jest.config.js文件(Test Runner 底层使用 jest-playwright)。

Test Hook API:钩子与生命周期的完整清单

许多行为无法通过运行在浏览器内的 play function 实现——例如让 Test Runner 代为截取视觉快照,这类操作必须在 Node 进程中执行。为此 Test Runner 导出了可在全局覆写的测试钩子,让你能在 story 渲染的之前之后接入测试生命周期。可用钩子如下:

钩子说明签名
prepare为测试准备浏览器async prepare({ page, browserContext, testRunnerConfig }) {}
setup在所有测试运行前执行一次setup() {}
preVisit在 story 首次被访问、渲染于浏览器之前执行async preVisit(page, context) {}
postVisit在 story 被访问并完全渲染之后执行async postVisit(page, context) {}

这些测试钩子目前属于实验性 API,可能发生破坏性变更,官方建议尽可能在 story 的 play function 内完成测试逻辑。

要启用钩子 API,需要在 Storybook 目录(默认为.storybook/)下新建配置文件test-runner.jstest-runner.ts。除setup外,其余钩子均为异步函数;preVisitpostVisit额外接收两个参数:一个 Playwright 的page对象,以及一个包含 story 的idtitlename的 context 对象。

Test Runner 执行时,测试会经历如下生命周期:

  1. setup函数在所有测试运行前执行;
  2. 生成包含必要信息的 context 对象;
  3. Playwright 导航到 story 页面;
  4. 执行preVisit函数;
  5. story 被渲染,存在的 play function 被执行;
  6. 执行postVisit函数。

辅助函数全景:getStoryContext 与 waitForPageReady

Test Runner 导出了若干辅助函数(Helpers),用于访问 Storybook 内部数据(如argsparameters),让测试更可读、更易维护。核心配置片段 test-runner-helper-function.md 给出了完整的 JavaScript 与 TypeScript 两种写法,这里完整继承如下。

JavaScript 版本(.storybook/test-runner.js

const { getStoryContext, waitForPageReady } = require('@storybook/test-runner'); module.exports = { // 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. }, };

TypeScript 版本(.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;

两个辅助函数的分工如下:

  • getStoryContext(page, context):读取某个 story 的完整上下文,包括parametersargsargTypes等全部信息,返回值为 Promise,需await。它接收两个参数:当前 story 对应的 Playwrightpage对象,以及钩子传入的 context 对象。它常用于在preVisit阶段根据 story 的参数调整测试环境,或在postVisit阶段按 story 元数据生成自定义断言。
  • waitForPageReady(page):专为图像快照(image snapshot)测试设计。它会等待页面完全加载就绪,包括所有异步资源(如图片、字体等)。由于页面中字体、图片等资源加载完成前截图会得到不稳定的结果,该函数能显著提升快照测试的稳定性。

实战一:用 getStoryContext 让 Playwright 视口跟随 story 参数

preVisit钩子中调用getStoryContext,即可在渲染前读取 story 的parameters.viewport.defaultViewport,并据此调整 Playwright 页面的视口尺寸。完整示例见 test-runner-custom-page-viewport.md:

const { getStoryContext } = require('@storybook/test-runner'); const { MINIMAL_VIEWPORTS } = require('storybook/viewport'); const DEFAULT_VIEWPORT_SIZE = { width: 1280, height: 720 }; module.exports = { 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); } }, };

这个示例的关键点在于:MINIMAL_VIEWPORTS中定义的 viewport 尺寸以百分比字符串形式存储,因此需要通过parseInt转换为数字后才能传给page.setViewportSize。如果该 story 未定义defaultViewport,则回退到默认的1280 × 720。同理,你也可以基于context.parameters中的其他配置(如主题、语言环境)在preVisit阶段做任意环境定制。

实战二:用 waitForPageReady 打造稳定的图像快照测试

waitForPageReady最常见的应用场景是图像快照测试。在setup钩子中通过expect.extend注册toMatchImageSnapshot匹配器,再在postVisit中等待页面资源就绪后截图。完整示例见 test-runner-waitpageready.md:

const { waitForPageReady } = require('@storybook/test-runner'); const { toMatchImageSnapshot } = require('jest-image-snapshot'); const customSnapshotsDir = `${process.cwd()}/__snapshots__`; module.exports = { 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, }); }, };

这里用context.id作为快照标识符,保证每个 story 生成唯一命名的快照文件。快照默认存放于项目根目录的__snapshots__目录;如需自定义快照目录,可编写自定义的snapshot-resolver.js并在test-runner-jest.config.js中启用snapshotResolver选项。若你的项目使用了 Emotion、Angular 的ng属性等会生成基于哈希的 CSS 类名的 CSS-in-JS 方案,还可通过snapshotSerializers配置自定义快照序列化器(默认使用jest-serializer-html),在快照前将动态生成的属性替换为稳定的静态值,确保跨测试运行的一致性。

延伸配置:钩子之外的高频能力

除钩子与辅助函数外,Test Runner 还支持通过.storybook/test-runner.js导出的其他配置函数扩展行为:

  • getHttpHeaders(url):对需要认证托管的 Storybook 设置 HTTP 请求头。该函数接收 fetch 请求与页面访问的 URL,返回需要附加的 headers 对象。完整示例见 test-runner-auth.md,例如根据 URL 是否包含prod返回不同的Authorization: Bearer <token>
  • CLI 常用参数--url <地址>指定测试目标(默认本机 6006 端口,也可用TARGET_URL环境变量);--maxWorkers <数量>控制并行 worker 数;--failOnConsole令浏览器控制台报错时测试失败;--updateSnapshot/-u重录失败的快照;--eject生成本地配置文件。
  • 标签过滤:通过--includeTags--excludeTags--skipTags或配置文件中的include/exclude/skip选项,按 story 的 tags 精确控制测试范围(需 Test Runner 0.15 及以上)。CLI 标志优先于配置文件中的同名选项。
  • index.json 模式:对远端 Storybook,Test Runner 使用其index.json(原stories.json)静态索引运行测试,可通过--index-json强制开启、--no-index-json关闭(该模式与 watch 模式不兼容)。

常见问题与排障建议

  • 测试超时:若出现Timeout - Async callback was not invoked within the 15000 ms timeout,通常意味着 Playwright 无法并行处理过多 story,可在 CI 脚本中限制并行度,如yarn test-storybook --maxWorkers=2
  • CLI 错误输出过短:默认错误输出截断于 1000 字符,可通过DEBUG_PRINT_LIMIT=5000 yarn test-storybook调整上限。
  • Yarn PnP 兼容性:Test Runner 依赖社区维护的jest-playwright-preset,尚不完全支持 Yarn Plug'n'Play。可切换nodeLinkernode-modules,或将 Playwright 作为直接依赖安装并执行playwright install下载浏览器二进制。
  • 标签过滤冲突:若includeexclude提供了相同 tags,Test Runner 将按exclude执行并忽略include,请确保两者 tags 不重叠。

总结

getStoryContextwaitForPageReady是扩展 Storybook Test Runner 时最核心的两个辅助函数:前者打通了 Node 测试进程与 Storybook 内部数据(parametersargsargTypes)之间的桥梁,让测试可以依据每个 story 的元数据动态定制;后者为图像快照类测试提供了可靠的资源加载等待保障。配合setup/preVisit/postVisit钩子与getHttpHeaders等配置项,你可以将 Test Runner 扩展为覆盖交互、视口、图像快照、认证访问等各类场景的通用测试框架。相关完整配置片段与文档可继续查阅 test-runner.mdx 及 test-runner-custom-page-viewport.md、test-runner-waitpageready.md 等配套示例。

【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 23:10:58

基于深度学习的短视频推荐系统架构与优化实践

1. 项目背景与核心价值短视频平台已经成为当下最主流的内容消费形式之一。根据最新统计&#xff0c;头部平台日均视频上传量超过8000万条&#xff0c;用户平均每天观看时长达到90分钟。面对如此海量的内容&#xff0c;如何精准理解视频语义并实现个性化推荐&#xff0c;成为平台…

作者头像 李华
网站建设 2026/9/10 23:10:50

2026专业论文降AI率工具测评与使用指南

1. 论文降AI率工具的市场现状与核心痛点2026年的学术环境对论文原创性要求达到了前所未有的高度。全球超过87%的主流学术期刊已部署第三代AI检测系统&#xff0c;能够识别GPT-5等大模型生成的文本特征。我在高校科研处工作的五年间&#xff0c;亲眼见证学生论文因AI率超标被退稿…

作者头像 李华
网站建设 2026/9/10 23:09:21

Android邮箱注册与密码找回功能开发实践

1. Android应用邮箱注册与密码找回功能实现指南在移动应用开发中&#xff0c;用户账号体系是几乎所有应用的基础功能模块。邮箱注册密码找回的组合方案因其普适性和安全性&#xff0c;成为大多数Android应用的首选认证方式。本文将基于最新Android开发实践&#xff0c;详细解析…

作者头像 李华
网站建设 2026/9/10 23:09:02

深入解析 ESLint 文档站的 Rule 宏组件:从参数模型到渲染实现

深入解析 ESLint 文档站的 Rule 宏组件&#xff1a;从参数模型到渲染实现 【免费下载链接】eslint Find and fix problems in your JavaScript code. 项目地址: https://gitcode.com/GitHub_Trending/es/eslint 本篇文章围绕 ESLint 文档网站中的 rule 宏组件展开&#…

作者头像 李华
网站建设 2026/9/10 23:07:35

OpenEuler 2026安装指南与常见问题解决

1. 项目概述OpenEuler作为国产开源操作系统的代表&#xff0c;近年来在服务器、云计算和嵌入式领域获得了广泛应用。2026年发布的版本在硬件兼容性、性能优化和开发者工具链方面都有显著提升。对于初次接触这个系统的用户来说&#xff0c;掌握正确的安装方法是后续开发工作的基…

作者头像 李华
网站建设 2026/9/10 23:07:15

OI-wiki 基础算法篇:Timsort 混合稳定排序算法深度解析

OI-wiki 基础算法篇&#xff1a;Timsort 混合稳定排序算法深度解析 【免费下载链接】OI-wiki :star2: Wiki of OI / ICPC for everyone. &#xff08;某大型游戏线上攻略&#xff0c;内含炫酷算术魔法&#xff09; 项目地址: https://gitcode.com/GitHub_Trending/oi/OI-wiki…

作者头像 李华