概要

Nightwatchは、公式の@nightwatch/storybookプラグインを通じてStorybookと連携できます。このプラグインは、既存のReact用のStorybookプロジェクトでコンポーネントテストを実行するためのいくつかの重要な機能を提供します。

追加のテストを記述したり、ストーリーをインポートしたりする必要はありません。NightwatchはComponent Story Format(CSF)をサポートしているため、ストーリーを直接実行できます。

@nightwatch/storybook on Github

仕組み

Nightwatchは、既存のインタラクションテストplay()関数を使用)および、コンポーネントストーリーで定義されているアクセシビリティテストを検出して実行できます。

さらに、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 }) =< {
}

インストール

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はデフォルトで、storybook設定フォルダ内のmain.jsで定義された場所を使用しようとします。これは、ストーリーが配置されている特定の場所を定義できます。

以下のオプションは、特定の'@nightwatch/storybook'辞書の下に設定する必要があります。

  • start_storybook – NightwatchがStorybookサーバーを自動的に管理するかどうか(デフォルトはfalse
  • storybook_url – Storybookが別のポート/ホスト名で実行されている場合は変更できます(デフォルトはhttp://localhost: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: 'http://localhost:6006/', storybook_config_dir: '.storybook', // default storybook config directory hide_csf_errors: false, show_browser_console: true } }

Nightwatchでストーリーを実行する

前のForm.stories.jsxの例には2つのストーリーが含まれており、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には、プレビューモード(--preview CLI引数を使用)で.stories.jsxファイルを実行する機能が用意されており、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フラグを有効にし、play()関数内にブレークポイントを追加するためにdebuggerを使用します。

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ドキュメントを参照してください。

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は、ストーリー設定からA11yルールを自動的に取得し、それらを使用して独自のアクセシビリティテストコマンドを実行します。

Buttonコンポーネントストーリーの1つは、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, // ... } } }