Overview

Nightwatch can be integrated with Storybook through our official @nightwatch/storybook plugin, which provides several important capabilities for running component tests to an existing Storybook project for React.

There is no need to start writing additional tests and import stories in them. Nightwatch supports the Component Story Format (CSF) so it is able to run the stories directly.

@nightwatch/storybook on Github

How it works

Nightwatch is able to detect and run any existing interaction tests (using the play() function) and accessibility tests which are defined in the component story.

In addition, Nightwatch provides a test() function which has access to its own APIs.

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

Installation

The Storybook plugin for Nightwatch can be installed from NPM with:

npm i @nightwatch/storybook --save-dev

Then add the plugin in your nightwatch.conf.js:

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

Usage

The plugin can be used in an existing Storybook project for React.

Setup Storybook

In an existing React project, run:

npx storybook init

Head over to the Storybook installation guide for more details.

We also recommend installing a few essential Storybook addons:

Running stories

By default, Nightwatch will mount the component story in the target browser and do a basic visibility assertion. Then, depending on which of the below are defined, it will:

  • run any interaction tests if a play() function is defined;
  • run the accessibility tests
  • run the test hooks, if any of these are defined in the default story export:
    • setup (browser)
    • teardown (browser)
    • preRender (browser, {id, title, name})
    • postRender (browser, {id, title, name})

All test hooks are async.

In addition, Nightwatch provides the ability to extend the component story with its own test() function, as follows:

  • test(browser, { component })

Read more on:

Example

Considering a basic Form.jsx component, here's how its Form.stories.jsx story would look like, written in CSF and extended with Nightwatch functionality:

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

Configuration

The @nightwatch/storybook plugin supports a few configuration options. Edit your nightwatch.conf.js and configure it as follows:

  • src_folders By default Nightwatch tries to use the location defined in the main.js inside the storybook config folder. This can define the specific location(s) to where the stories are located.

The following options need to be set under the specific '@nightwatch/storybook' dictionary:

  • start_storybook – whether Nightwatch should manage the Storybook server automatically (default false)
  • storybook_url – can be changed if Storybook is running on a different port/hostname (default http://localhost:6006/)
  • storybook_config_dir - default is .storybook
  • hide_csf_errors - Nightwatch tries to ignore the CSF parsing errors and displays a warning; setting this to true will hide these warnings (default is false)
  • show_browser_console - By default when using Chrome or Edge browsers, the browser console logs will be displayed in the Nightwatch console (using the [browser] prefix); this options disables this functionality.
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 } }

Run stories with Nightwatch

The previous Form.stories.jsx example contains two stories and it can be run by Nightwatch as a regular test.

For the best developer experience available at the moment, we recommend to use Chrome, however you can use any of the other browsers that Nightwatch supports as well.

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

Running a specific story

You can run a specific story from a given .stories.jsx file by using the --story CLI argument.

Say you want to run only the FilledForm story. This will mount it and also execute the play() and test() functions accordingly:

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

Run stories in parallel

It may be useful to run the stories in parallel for optimizing the speed of execution using the existing Nightwatch option of running in parallel using test workers. In fact, running in parallel using test workers is enabled by default in Nightwatch v2.4.

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

Preview stories

Nightwatch provides the ability to run a .stories.jsx file in preview mode (using the --preview CLI argument) which would only open the Storybook renderer and pause the execution indefinitely.

This can be useful during development, since the Storybook renderer has the ability to automatically reload the component via its built-in Hot Module Replacement (HMR) functionality.

To launch the FilledForm story in preview mode, run:

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

You can use the Nightwatch built-in parallelism to open the story in both Firefox and Chrome:

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

Debug stories

In addition to previewing the story, it's also possible to use Nightwatch to debug the story. To do this, enable the --debug and --devtools CLI flags and use the debugger to add breakpoints inside the play() function.

Example:

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

Run the example and observe the breakpoint in the Chrome devtools console.

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

Screenshot of the Chrome Devtools debugger paused at a breakpoint

You can also use the integrated debug console to issue commands from Nightwatch.

Accessibility testing

Both Storybook and Nightwatch rely internally on the same accessibility testing tools developed by Deque Systems and published in NPM as the axe-core library.

To get started with in A11y testing in Storybook, install the addon:

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

Add this line to your main.js file (create this file inside your Storybook config directory if needed).

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

More details can be found on Storybook docs:

Example

Consider the bundled example Button.jsx component and Button.stories.jsx which come pre-installed when you setup Storybook.

Add the following rules for accessibility tests:

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 will automatically pick up the A11y rules from the story config and use them to run its own accessibility test commands.

One of the Button component story will fail the "color-contrast" accessibility rule as defined by the Axe-core library.

Run the following to see the result:

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

The output from Nightwatch should be:

  ️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"] β”‚ β”‚ β”‚ β”‚ β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

To view the entire report (which includes all the eveluated rules), pass verbose: true in the story parameters:

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, // ... } } }