Storybookコンポーネントテスト
概要
Nightwatchは、公式の@nightwatch/storybook
プラグインを通じてStorybookと連携できます。このプラグインは、既存のReact用のStorybookプロジェクトでコンポーネントテストを実行するためのいくつかの重要な機能を提供します。
追加のテストを記述したり、ストーリーをインポートしたりする必要はありません。NightwatchはComponent Story Format(CSF)をサポートしているため、ストーリーを直接実行できます。
仕組み
Nightwatchは、既存のインタラクションテスト(play()
関数を使用)および、コンポーネントストーリーで定義されているアクセシビリティテストを検出して実行できます。
さらに、Nightwatchは、独自のAPIにアクセスできるtest()
関数を提供します。
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からインストールできます。
次に、nightwatch.conf.js
にプラグインを追加します
module.exports = {
plugins: [
//...
'@nightwatch/storybook'
]
}
使用法
このプラグインは、React用の既存のStorybookプロジェクトで使用できます。
Storybookのセットアップ
既存のReactプロジェクトで、以下を実行します。
詳細については、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 })
詳細については、以下を参照してください。
- Storybookのインタラクションテスト
- Storybookのアクセシビリティテスト
- コンポーネントストーリー形式(CSF)
例
基本的なForm.jsx
コンポーネントを考えると、以下はそのForm.stories.jsx
ストーリーがどのように見えるかを示しています。これはCSFで記述され、Nightwatchの機能で拡張されています。
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]
プレフィックスを使用)。このオプションは、この機能を無効にします。
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がサポートしている他のブラウザも使用できます。
特定のストーリーの実行
--story
CLI引数を使用すると、特定の.stories.jsx
ファイルから特定のストーリーを実行できます。
FilledForm
ストーリーのみを実行するとします。これにより、ストーリーがマウントされ、play()
関数とtest()
関数もそれに応じて実行されます。
ストーリーを並列実行する
テストワーカーを使用して並列実行するという既存のNightwatchオプションを使用し、実行速度を最適化するためにストーリーを並列実行すると便利な場合があります。実際、テストワーカーを使用した並列実行は、Nightwatch v2.4ではデフォルトで有効になっています。
ストーリーのプレビュー
Nightwatchには、プレビューモード(--preview
CLI引数を使用)で.stories.jsx
ファイルを実行する機能が用意されており、Storybookレンダラーのみを開き、実行を無期限に一時停止します。
Storybookレンダラーには、組み込みのホットモジュールリローディング(HMR)機能を使用してコンポーネントを自動的にリロードする機能があるため、これは開発中に役立ちます。
FilledForm
ストーリーをプレビューモードで起動するには、以下を実行します。
Nightwatchの組み込み並列処理を使用すると、FirefoxとChromeの両方でストーリーを開くことができます。
ストーリーのデバッグ
ストーリーのプレビューに加えて、Nightwatchを使用してストーリーをデバッグすることもできます。これを行うには、--debug
および--devtools
CLIフラグを有効にし、play()
関数内にブレークポイントを追加するためにdebugger
を使用します。
例
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コンソールでブレークポイントを観察します。
統合デバッグコンソールを使用して、Nightwatchからコマンドを発行することもできます。
アクセシビリティテスト
StorybookとNightwatchの両方が、内部的にDeque Systemsが開発し、NPMでaxe-core
ライブラリとして公開されている同じアクセシビリティテストツールに依存しています。
StorybookでA11yテストを開始するには、アドオンをインストールします。
main.js
ファイルにこの行を追加します(必要な場合は、Storybook設定ディレクトリ内にこのファイルを作成します)。
module.exports = {
addons: ['@storybook/addon-a11y']
};
詳細については、Storybookドキュメントを参照してください。
例
StorybookをセットアップするとプリインストールされるバンドルされたButton.jsx
コンポーネントと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"
アクセシビリティルールに失敗します。
結果を表示するには、以下を実行します。
Nightwatchからの出力は次のようになります。
(評価されたすべてのルールを含む)レポート全体を表示するには、ストーリーパラメータでverbose: true
を渡します。
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,
// ...
}
}
}