返回博客

交互式故事 (Beta 版)

使用 play 函数模拟用户行为

loading
Varun Vachhar
@winkerVSbecks
最后更新

独立构建组件可让您对其进行压力测试以找出边缘情况。使用 Storybook,您可以为每种情况编写 story,方法是向组件提供 props。

但是并非所有组件变体都能仅通过 props 重现。某些 UI 状态只能通过用户交互到达,例如下拉菜单、模态框和隐藏的表单元素。过去,您需要手动与组件交互以检查这些状态是否看起来正确。

我很高兴地宣布交互式 story(beta 版),它允许您模拟用户行为并在 story 渲染后运行。它消除了手动处理组件的繁琐工作。

  • ✅ 在真实浏览器中运行
  • ⚡️ 无等待,无错误
  • 🐙 由 Testing Library 提供支持
  • 🛠 低维护
  • 🔍 快速可视化调试

用于交互的 Play 函数

Story 以结构化的方式隔离和捕获组件状态。在开发组件时,您可以快速地在 story 之间切换以验证外观和感觉。

每个 story 指定了重现特定状态所需的所有输入。您甚至可以模拟 context 和 API 调用。这使您能够处理大多数组件用例。

但是需要用户交互的状态呢?

例如,单击按钮以打开/关闭对话框,拖动列表项以重新排序,或填写表单以检查验证错误。要测试这些行为,您需要像用户一样与组件交互。

交互式 story 使您能够使用 play 函数自动化这些交互。这些是简短的代码片段,可以脚本化人类与组件交互的确切步骤。然后,它会在 story 渲染后立即执行。

由 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 中渲染它,那么您可以为其编写交互式 story。

交互式 story 使用与框架无关的 DOM API。这意味着,无论您使用什么框架,都可以编写 play 函数来操作 UI 并自动化用户行为。唯一的例外是 Web Components,因为 Testing-Library 尚不支持 shadow DOM。

使用 GUI 加快调试速度

大多数团队已经使用 Testing Library 为其组件编写功能测试。这些测试是使用 Jest 等测试运行器执行的。它们在 Node 中使用 JSDOM 运行。如果测试失败,您只会得到一堆 HTML 来进行调试。

使用 CLI 调试 UI 问题就像在您的 Playstation 5 上玩文字冒险游戏一样。它奏效了,但如果您能看到一些图形,那会更好。

交互式 story 使用 Storybook 特定的 Testing Library 版本。它在浏览器中运行,并为您可视化整个 play 函数。将其视为 Testing Library 的 GUI。

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

Storybook linter

交互式 story 和交互面板都由 Testing Library 的 instrumented 版本启用。这意味着您必须使用来自 @storybook/testing-library 的辅助函数,并确保您的 story 使用正确的语法编写。我们正在开发一个 ESLint 插件来自动强制执行这些检查。它将与 Storybook 6.4 一起发布。敬请期待!

让我们通过一个演示来查看整个工作流程。

交互式 story 示例

考虑 Taskbox 应用程序 — 一个类似于 Asana 的任务管理应用程序。它显示一个用户可以固定、存档和编辑的任务列表。

story 看起来像这样

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);
};

story 使用 MSW 插件来 mock /tasks API 调用。该 mock 数据用于渲染任务列表,并且交互在 play 函数内部进行。

今天就来试试并分享您的反馈

交互式 story 现在在 Storybook 6.4 beta 版中可用。此实验性版本为我们 8 月份宣布的更广泛的交互测试功能奠定了基础。Storybook 6.4 使我们有机会在 Storybook 6.5 完全发布之前测试和稳定此功能。试试看,并与我们分享您的反馈

通过在项目根目录中运行以下命令来安装它。

npx sb@next init

升级现有项目

npx sb upgrade --prerelease

然后安装与交互式 story 相关的依赖项

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'],
};

参与进来

交互式 story 使测试组件的功能方面变得更加容易。您可以通过 play 函数脚本化交互,Storybook 会自动为您运行它们。方便的交互面板会可视化 play 函数中的每次交互。

交互式 story 功能由Gert HengeveldDeen DennoYann BragaMichael ShilmanTom ColemanMichael ArestadDominic Nguyen 开发,并获得了整个 Storybook 社区的反馈。

如果 Storybook 使您的 UI 开发工作流程更轻松,请帮助 Storybook 变得更好。您可以贡献新功能、修复 bug 或改进文档。加入我们的 Discord 或直接在 Github 上参与。要获取项目更新和早期功能访问权限,请在下方订阅 Storybook 的邮件列表。

加入 Storybook 邮件列表

获取最新消息、更新和发布信息

7,468开发者及更多

我们正在招聘!

加入 Storybook 和 Chromatic 团队。构建被数十万开发人员在生产中使用的工具。远程优先。

查看职位

热门帖子

开始使用 Storybook 和 Next.js

用四个简单步骤将 Storybook 集成到 Next.js 中
loading
Michael Chan

使用 Storybook 记录你的设计系统的 4 种方法

如何一起展示 UI 组件、规范和使用指南
loading
Varun Vachhar

Storybook 按需架构

构建的 Storybooks 减小 3 倍,加载速度提升更快
loading
Tom Coleman
加入社区
7,468开发者及更多
原因为什么选择 Storybook组件驱动的 UI
文档指南教程更新日志遥测
社区插件参与进来博客
展示探索项目组件词汇表
开源软件
Storybook - Storybook 中文

特别感谢 Netlify CircleCI