
互动故事 (beta)
使用 play 函数模拟用户行为

在隔离环境中构建组件可以让你进行压力测试,以发现边缘情况。 使用 Storybook,你可以通过为组件提供 props 来为每种情况编写故事。
但并非所有组件变体都可以仅通过 props 重现。 某些 UI 状态只能通过用户交互才能达到,例如下拉菜单、模态框和隐藏的表单元素。 过去,你需要手动与组件交互来检查这些状态是否看起来正确。
我很高兴地宣布 **互动故事 (beta)**,它允许你模拟用户行为在故事渲染后运行。 它消除了手动调整组件的繁琐工作。
- ✅ 在真实浏览器中运行
- ⚡️ 无需等待且无抖动
- 🐙 由 Testing Library 驱动
- 🛠 低维护成本
- 🔍 快速可视化调试
用于交互的 Play 函数
故事以结构化的方式隔离和捕获组件状态。 在开发组件时,你可以快速浏览各个故事以验证外观和感觉。
每个故事都指定了重现特定状态所需的所有输入。 你甚至可以模拟上下文和 API 调用。 这使你可以处理组件的大多数用例。
但是需要用户交互的状态呢?
例如,单击按钮以打开/关闭对话框、拖动列表项以重新排序或填写表单以检查验证错误。 要测试这些行为,你必须像用户一样与组件交互。

互动故事使你能够使用 play 函数自动化这些交互。 这些是代码片段,脚本化了人类与组件交互的确切步骤。 然后在故事渲染后立即执行。
由 Testing Library 驱动
这些交互是使用 Storybook 仪器化的 Testing Library 版本编写的。 这为你提供了熟悉的、开发者友好的语法来与 DOM 交互,但带有额外的遥测功能来帮助调试。 这是一个基本示例,演示了单击按钮以打开对话框。
import React from 'react';
import { within, fireEvent } from '@storybook/testing-library';
import { DeleteCustomerDialog } from './DeleteCustomerDialog';
export default {
component: DeleteCustomerDialog,
title: 'DeleteCustomerDialog',
};
export const OpenDialog = () => <DeleteCustomerDialog />;
OpenDialog.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
await fireEvent.click(
canvas.getByRole('button', { name: 'Delete Customer' })
);
};
如果你可以在 Storybook 中渲染它,那么你可以为其编写一个互动故事。
互动故事使用框架无关的 DOM API。 这意味着,无论你使用哪个框架,你都可以编写一个 play 函数来操作 UI 并自动化用户行为。 唯一的注意事项是 Web Components,因为 Testing-Library 尚不支持 shadow DOM。
使用 GUI 更快地调试
大多数团队已经使用 Testing Library 为其组件编写功能测试。 这些测试使用像 Jest 这样的测试运行器执行。 它们在 Node 中使用 JSDOM 运行。 如果测试失败,你得到的只是一堆 HTML 来调试。
使用 CLI 调试 UI 问题就像在 Playstation 5 上玩文字冒险游戏。 它有效,但如果你能看到一些图形会好得多。

互动故事使用 Storybook 特定的 Testing Library 版本。 它在浏览器中运行,并为你可视化整个 play 函数。 可以将其视为 Testing Library 的 GUI。

如果故事失败,交互面板会突出显示损坏的步骤。 然后,你可以直接在浏览器中使用你最喜欢的开发者工具调试 UI。 而不是在 JSDOM 中使用不透明的 CLI。 能够看到和检查 UI 使调试变得轻而易举。

Storybook 代码检查器
互动故事和交互面板都由仪器化的 Testing Library 版本启用。 这意味着你必须使用来自 @storybook/testing-library
的辅助函数,并确保你的故事是使用正确的语法编写的。 我们正在开发一个 ESLint 插件来自动强制执行这些检查。 它将与 Storybook 6.4 一起发布。 敬请关注!
让我们通过一个演示来了解整个工作流程。
互动故事示例
考虑 Taskbox 应用程序——一个类似于 Asana 的任务管理应用程序。 它显示用户可以固定、存档和编辑的任务列表。

故事看起来像这样
import React from 'react';
import { rest } from 'msw';
import { within, fireEvent, findByRole } from '@storybook/testing-library';
import { InboxScreen } from './InboxScreen';
import { Default as TaskListDefault } from './components/TaskList.stories';
export default {
component: InboxScreen,
title: 'InboxScreen',
};
const Template = (args) => <InboxScreen {...args} />;
export const Default = Template.bind({});
Default.parameters = {
msw: [
rest.get('/tasks', (req, res, ctx) => {
return res(ctx.json(TaskListDefault.args));
}),
],
};
export const UpdateTasks = Template.bind({});
UpdateTasks.parameters = { ...Default.parameters };
UpdateTasks.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
const getTask = (name) => canvas.findByRole('listitem', { name });
// Pin
const itemToPin = await getTask('Export logo');
const pinButton = await findByRole(itemToPin, 'button', { name: 'pin' });
await fireEvent.click(pinButton);
// Archive
const itemToArchive = await getTask('QA dropdown');
const archiveCheckbox = await findByRole(itemToArchive, 'checkbox');
await fireEvent.click(archiveCheckbox);
// Edit
const itemToEdit = await getTask('Fix bug in input error state');
const taskInput = await findByRole(itemToEdit, 'textbox');
await fireEvent.change(taskInput, {
target: { value: 'Fix bug in the textarea error state' },
});
// Delete
const itemToDelete = await getTask('Build a date picker');
const deleteButton = await findByRole(itemToDelete, 'button', {
name: 'delete',
});
await fireEvent.click(deleteButton);
};
该故事使用 MSW 插件来模拟 /tasks
API 调用。 该模拟数据用于渲染任务列表,交互存在于 play 函数内部。

今天就试试并分享你的反馈
互动故事现已在 Storybook 6.4 beta 版中提供。 这个实验性版本为我们在 8 月份宣布的更广泛的交互测试功能奠定了基础。 Storybook 6.4 让我们有机会在 Storybook 6.5 的完整发布之前测试和稳定此功能。 试用一下,并与我们分享你的反馈。
通过在项目根目录运行以下命令来安装它。
npx sb@next init
要升级现有项目
npx sb upgrade --prerelease
然后安装与互动故事相关的依赖项
yarn add -D @storybook/addon-interactions @storybook/testing-library
并在 .storybook/main.js
中启用调试器。 请注意,@storybook/addon-interactions
必须在 @storybook/addon-actions
或 @storybook/addon-essentials
之后列出。
// .storybook/main.js
module.exports = {
addons: ['@storybook/addon-interactions'],
};
参与进来
互动故事使测试组件的功能方面变得更容易。 你通过 play 函数编写交互脚本,Storybook 会为你自动运行它们。 方便的交互面板可视化 play 函数中的每个交互。
互动故事功能由 Gert Hengeveld、Deen Denno、Yann Braga、Michael Shilman、Tom Coleman、Michael Arestad 和 Dominic Nguyen 开发,并获得了整个 Storybook 社区的反馈。
如果 Storybook 使你的 UI 开发工作流程更轻松,请帮助 Storybook 变得更好。 你可以贡献新功能、修复错误或改进文档。 在 Discord 上加入我们,或者直接参与 Github。 要获取项目更新和抢先体验功能,请在下面注册 Storybook 的邮件列表。
互动故事 beta 版已上线!
— Storybook (@storybookjs) 2021年11月4日
在你的故事中模拟用户行为(单击、键入、悬停)。 无需单独的测试文件或运行器。
✅ 在真实浏览器中运行
⚡️ 无需等待且无抖动
🐙 由 @TestingLib 驱动
🛠 低维护成本
🔍 可视化调试 https://#/6ZJgOeraeE pic.twitter.com/ZmlUviFz1R