概觀

Nightwatch 可以透過我們的官方 @nightwatch/storybook 外掛程式與 Storybook 整合,該外掛程式為現有的 React Storybook 專案提供了幾個重要的執行元件測試的功能。

無需開始編寫額外的測試並在其中匯入 stories。Nightwatch 支援 Component Story Format (CSF),因此可以直接執行 stories。

@nightwatch/storybook on Github

運作方式

Nightwatch 能夠偵測並執行任何現有的 互動測試(使用 play() 函式)以及在元件 story 中定義的可訪問性測試

此外,Nightwatch 還提供了可以存取自身 API 的 test() 函式。

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 }) =< {
}

安裝

可以從 NPM 安裝 Nightwatch 的 Storybook 外掛程式

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 附加元件

執行 stories

預設情況下,Nightwatch 會在目標瀏覽器中掛載元件 story,並執行基本的可見性斷言。然後,根據定義的以下項目,它會

  • 如果定義了 play() 函式,則執行任何互動測試;
  • 執行可訪問性測試
  • 如果這些測試 hook 在 default story 匯出中定義,則執行這些測試 hook
    • setup (瀏覽器)
    • teardown (瀏覽器)
    • preRender (瀏覽器, {id, title, name})
    • postRender (瀏覽器, {id, title, name})

所有測試 hook 都是 async

此外,Nightwatch 還可以透過自己的 test() 函式擴充元件 story,如下所示

  • test(browser, { component })

閱讀更多關於

範例

考慮基本的 Form.jsx 元件,以下是如何在 CSF 中編寫其 Form.stories.jsx story,並使用 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 會嘗試使用 storybook 設定資料夾中 main.js 內定義的位置。這可以定義 story 所在的特定位置。

以下選項需要在特定 '@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 執行 stories

先前的 Form.stories.jsx 範例包含兩個 stories,而且可以由 Nightwatch 以一般測試方式執行。

為了獲得目前最佳的開發人員體驗,我們建議使用 Chrome,但您也可以使用 Nightwatch 支援的其他任何瀏覽器。

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

執行特定 story

您可以透過使用 --story CLI 引數,從指定的 .stories.jsx 檔案執行特定的 story。

假設您只想執行 FilledForm story。這會掛載它,也會相應地執行 play()test() 函式

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

平行執行 stories

使用測試工作程式平行執行現有的 Nightwatch 選項,以便最佳化執行速度可能很有用。事實上,在 Nightwatch v2.4 中預設已啟用使用測試工作程式平行執行。

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

預覽 stories

Nightwatch 提供了在預覽模式下執行 .stories.jsx 檔案的功能 (使用 --preview CLI 引數),這只會開啟 Storybook 渲染器並無限期地暫停執行。

這在開發期間可能很有用,因為 Storybook 渲染器能夠透過其內建的熱模組更換 (HMR) 功能自動重新載入元件。

若要在預覽模式中啟動 FilledForm story,請執行

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

您可以使用 Nightwatch 內建的平行處理功能,在 Firefox 和 Chrome 中開啟 story

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

偵錯 stories

除了預覽 story 之外,也可以使用 Nightwatch 偵錯 story。若要這麼做,請啟用 --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 開發人員工具主控台中的中斷點。

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 文件中找到

範例

考慮在您設定 Storybook 時預先安裝的捆綁範例 Button.jsx 元件和 Button.stories.jsx

新增以下可訪問性測試規則

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 會自動從 story 設定中選取 A11y 規則,並使用它們來執行自己的可訪問性測試指令。

其中一個 Button 元件 story 將會因為 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"] │ │ │ │ │ ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

若要檢視完整報告 (包含所有評估的規則),請在 story 參數中傳遞 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, // ... } } }