概述

Nightwatch 可以通过我们的官方 @nightwatch/storybook 插件与 Storybook 集成,该插件为 React 现有 Storybook 项目运行组件测试提供了多种重要功能。

无需编写额外的测试并导入故事。Nightwatch 支持 组件故事格式 (CSF),因此它可以直接运行故事。

@nightwatch/storybook on Github

工作原理

Nightwatch 能够检测并运行任何现有的 交互测试 (使用 play() 函数) 和 可访问性测试,这些测试是在组件故事中定义的。

此外,Nightwatch 提供了一个 test() 函数,该函数可以访问其自己的 API。

test/form.stories.jsx
import Form from '../components/Form.jsx';

export default { title: 'Form Stories', component: Form }
const Template = (args) =< <Form {...args} /> export const FilledForm = Template.bind({});
// Runs in the browser context FilledForm.play = async ({ canvasElement }) =< {
};
// Runs in the Nightwatch context FilledForm.test = async (browser, { component }) =< {
}

安装

Nightwatch 的 Storybook 插件可以通过 NPM 安装:

npm i @nightwatch/storybook --save-dev

然后在您的 nightwatch.conf.js 中添加插件:

nightwatch.conf.js
module.exports = {
  plugins: [
    //...
    '@nightwatch/storybook'      
  ]
}

用法

该插件可以在 React 的 现有 Storybook 项目中使用。

设置 Storybook

在现有的 React 项目中,运行:

npx storybook init

有关更多详细信息,请访问 Storybook 的 安装指南

我们还建议安装一些必要的 Storybook 附加组件:

运行故事

默认情况下,Nightwatch 将在目标浏览器中挂载组件故事并执行基本的可见性断言。然后,根据下面定义的内容,它将:

  • 如果定义了 play() 函数,则运行任何交互测试;
  • 运行可访问性测试
  • 运行测试钩子(如果在 default 故事导出中定义了以下任何一个):
    • setup (browser)
    • teardown (browser)
    • preRender (browser, {id, title, name})
    • postRender (browser, {id, title, name})

所有测试钩子都是 async

此外,Nightwatch 提供了使用其自己的 test() 函数扩展组件故事的功能,如下所示:

  • test(browser, { component })

阅读更多内容:

示例

考虑一个基本的 Form.jsx 组件,以下是其 Form.stories.jsx 故事的外观,使用 CSF 编写并扩展了 Nightwatch 功能:

Form.stories.jsx
import { userEvent, within } from '@storybook/testing-library';
import Form from './Form.jsx';

export default { title: 'Form', component: Form,
async setup(browser) { console.log('setup hook', browser.capabilities) },
async preRender(browser) { console.log('preRender hook') },
async postRender(browser) { console.log('postRender hook') },
async teardown(browser) { console.log('teardown hook') }, }
const Template = (args) =< <Form {...args} />;
// Component story for an empty form export const EmptyForm = Template.bind({});
// Component story simulating filling in the form export const FilledForm = Template.bind({});
FilledForm.play = async ({ canvasElement }) =< {
// Starts querying the component from its root element const canvas = within(canvasElement);
// 👇 Simulate interactions with the component await userEvent.type(canvas.getByTestId('new-todo-input'), 'outdoors hike'); await userEvent.click(canvas.getByRole('button')); };
FilledForm.test = async (browser, { component }) =< { // 👇 Run commands and assertions in the Nightwatch context await expect(component).to.be.visible; }

配置

@nightwatch/storybook 插件支持一些配置选项。编辑您的 nightwatch.conf.js 并按如下方式配置它:

  • src_folders 默认情况下,Nightwatch 尝试使用故事书配置文件夹中 main.js 中定义的位置。这可以定义故事所在位置的特定位置。

以下选项需要在特定的 '@nightwatch/storybook' 字典下设置:

  • start_storybook – Nightwatch 是否应该自动管理 Storybook 服务器(默认值为 false
  • storybook_url – 如果 Storybook 在不同的端口/主机名上运行,则可以更改此选项(默认值为 https://127.0.0.1:6006/
  • storybook_config_dir - 默认值为 .storybook
  • hide_csf_errors - Nightwatch 尝试忽略 CSF 解析错误并显示警告;将其设置为 true 将隐藏这些警告(默认值为 false
  • show_browser_console - 默认情况下,当使用 Chrome 或 Edge 浏览器时,浏览器控制台日志将显示在 Nightwatch 控制台中(使用 [browser] 前缀);此选项将禁用此功能。
nightwatch.conf.js
module.exports = {
  src_folders: ['src/stories/*.stories.jsx'],
  
'@nightwatch/storybook': { start_storybook: false, storybook_url: 'https://127.0.0.1:6006/', storybook_config_dir: '.storybook', // default storybook config directory hide_csf_errors: false, show_browser_console: true } }

使用 Nightwatch 运行故事

之前的 Form.stories.jsx 示例包含两个故事,可以作为常规测试由 Nightwatch 运行。

为了获得目前最佳的开发人员体验,我们建议使用 Chrome,但是您也可以使用 Nightwatch 支持的任何其他浏览器。

npx nightwatch src/stories/Form.stories.jsx --env chrome

运行特定故事

您可以使用 --story CLI 参数从给定的 .stories.jsx 文件中运行特定故事。

假设您只想运行 FilledForm 故事。这将挂载它并相应地执行 play()test() 函数:

npx nightwatch src/stories/Form.stories.jsx --env chrome --story=FilledForm

并行运行故事

使用现有的 Nightwatch 选项使用测试工作程序并行运行,可以并行运行故事以优化执行速度。实际上,从 Nightwatch v2.4 开始,默认情况下启用了使用测试工作程序并行运行。

npx nightwatch ./src/stories/**.stories.jsx --env chrome --workers=4 --headless

预览故事

Nightwatch 提供了使用预览模式运行 .stories.jsx 文件的功能(使用 --preview CLI 参数),这将只打开 Storybook 渲染器并无限期地暂停执行。

这在开发过程中可能很有用,因为 Storybook 渲染器能够通过其内置的热模块替换 (HMR) 功能自动重新加载组件。

要以预览模式启动 FilledForm 故事,请运行:

npx nightwatch src/stories/Form.stories.jsx --env chrome --story=FilledForm --preview

您可以使用 Nightwatch 内置的并行性在 Firefox 和 Chrome 中打开故事:

npx nightwatch src/stories/Form.stories.jsx --env chrome,firefox --story=FilledForm --preview

调试故事

除了预览故事之外,还可以使用 Nightwatch 调试故事。为此,请启用 --debug--devtools CLI 标志,并使用 debuggerplay() 函数中添加断点。

示例

Form.stories.jsx
import { userEvent, within } from '@storybook/testing-library';
import Form from './Form.jsx';

export default { title: 'Form', component: Form, }
const Template = (args) =< <Form {...args} />
// Component story for an empty form export const EmptyForm = Template.bind({});
// Component story simulating filling in the form export const FilledForm = Template.bind({});
FilledForm.play = async ({ canvasElement }) =< {
// Starts querying the component from its root element const canvas = within(canvasElement);
debugger;
// 👇 Simulate interactions with the component await userEvent.type(canvas.getByTestId('new-todo-input'), 'outdoors hike'); await userEvent.click(canvas.getByRole('button')); };
FilledForm.test = async (browser, { component }) =< { // 👇 Run commands and assertions in the Nightwatch context await expect(component).to.be.visible; }

运行示例并观察 Chrome devtools 控制台中的断点。

npx nightwatch src/stories/Form.stories.jsx --env chrome --devtools --debug --story=FilledForm

Screenshot of the Chrome Devtools debugger paused at a breakpoint

您还可以使用 集成调试控制台 从 Nightwatch 中发出命令。

可访问性测试

Storybook 和 Nightwatch 内部都依赖于由 Deque Systems 开发并在 NPM 中发布的相同可访问性测试工具,发布为 axe-core 库。

要开始在 Storybook 中进行 A11y 测试,请安装附加组件:

npm i @storybook/addon-a11y --save-dev

将此行添加到您的 main.js 文件中(如果需要,请在您的 Storybook 配置目录中创建此文件)。

.storybook/main.js
module.exports = {
  addons: ['@storybook/addon-a11y']
};

更多详细信息可以在 Storybook 文档中找到:

示例

考虑捆绑的示例 Button.jsx 组件和 Button.stories.jsx,它们在您设置 Storybook 时预先安装。

为可访问性测试添加以下规则:

stories/Button.stories.jsx
import React from 'react';
import { Button } from './Button';

export default { title: "Example/Button", component: Button, argTypes: { backgroundColor: { control: "color" }, },
parameters: { a11y: { // Optional selector to inspect element: '#root',
// Show the individual axe-rules as Nightwatch assertions (can be verbose if there are many violations) runAssertions: false,
// Show the complete Acccessibilty test report (by default, only rule violations will be shown) verbose: false, config: { rules: [ { // The autocomplete rule will not run based on the CSS selector provided id: 'autocomplete-valid', selector: '*:not([autocomplete="nope"])', }, { // Setting the enabled option to false will disable checks for this particular rule on all stories. id: 'image-alt', enabled: false, }, { id: 'input-button-name', enabled: true }, { id: 'color-contrast', enabled: true } ], }, options: {}, manual: true } } };
const Template = (args) =< <Button {...args} />;
export const Primary = Template.bind({}); Primary.args = { primary: true, label: 'Button', };
export const Secondary = Template.bind({}); Secondary.args = { label: 'Button', };
export const Large = Template.bind({}); Large.args = { size: 'large', label: 'Button', };
export const Small = Template.bind({}); Small.args = { size: 'small', label: 'Button', };

Nightwatch 将自动从故事配置中获取 A11y 规则并使用它们运行自己的可访问性测试命令。

Button 组件故事之一将无法通过 Axe-core 库定义的 "color-contrast" 可访问性规则。

运行以下命令查看结果:

npx nightwatch src/stories/Button.stories.jsx -e chrome

Nightwatch 的输出应为:

  ️TEST FAILURE (2.947s):  
   - 1 assertions failed; 4 passed
  
✖ 1) Button.stories – "Primary" story (733ms)
→ ✖ NightwatchAssertError There are accessibility violations; please see the complete report for details.
Read More : https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md

Accessibility report for: example-button--primary.Primary
Accessibility violations for: example-button--primary.Primary ┌───────────────────────┬────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬────────────┐ │ ID │ Impact │ Description │ Nodes │ │ ───────────────────── │ ────────── │ │ ────────── │ │ color-contrast │ serious │ Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds │ 1 │ │ ───────────────────── │ ────────── │ │ ────────── │ │ Target │ Html │ Violations │ │ [".storybook-button"] │ │ │ │ │ ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

要查看整个报告(包括所有评估的规则),请在故事参数中传递 verbose: true

stories/Button.stories.jsx
import React from 'react';
import { Button } from './Button';

export default { parameters: { a11y: { // Show the complete Accessibility test report (by default, only rule violations will be shown) verbose: false, // ... } } }