Eyes-Storybook
Applitools Eyes SDK for Storybook.
Table of contents
Installation
Install npm package
Install Eyes-Storybook as a local dev dependency in your tested project:
npm install --save-dev @applitools/eyes-storybook
Applitools API key
In order to authenticate via the Applitools server, you need to supply the Eyes-Storybook SDK with the API key you got from Applitools. Read more about how to obtain the API key here.
To do this, set the environment variable APPLITOOLS_API_KEY
to the API key before running your tests.
For example, on Linux/Mac:
export APPLITOOLS_API_KEY=<your_key>
And on Windows:
set APPLITOOLS_API_KEY=<your_key>
Usage
After completing the installation and defining the API key, you will be able to run Eyes-Storybook from the command line and let it take screenshots of all your stories.
If your project is using the default storybook config folder (i.e. <project_folder>/.storybook
), then run the following command:
npx eyes-storybook
Configuring local storybook server
Normally, Eyes-Storybook starts a storybook dev server in an available port between 9000-9010 for the duration of the tests. It's possible to pass arguments to Eyes-Storybook to configure the local storybook server:
--storybook-port OR -p
: Port to run storybook (passed as -p
to start-storybook
).--storybook-host OR -h
: Host to run storybook (passed as -h
to start-storybook
).--storybook-config-dir OR -c
: Directory where to load Storybook configurations from (passed as -c
to start-storybook
)--storybook-static-dir OR -s
: Directory where to load static files from, comma-separated list (passed as -s
to start-storybook
)
Standalone server
As noted in the previous section, Eyes-Storybook starts a storybook dev server. If you wish to start the server outside of Eyes-Storybook, or test a production build that's available at a certain URL, then just specify the URL for the storybook in the command line (or in the configuration file, see Advanced configuration below).
For example:
npx eyes-storybook -u http://localhost:6006
Or for a production storybook:
npx eyes-storybook -u http://react.carbondesignsystem.com/
Command line arguments
The full list of command line arguments can be viewed by running npx eyes-storybook --help
:
Usage: eyes-storybook.js [options]
Options:
--help Show help [boolean]
--version, -v Show the version number [boolean]
--conf, -f Path to applitools.config.js config file [string]
--storybook-url, -u URL to storybook [string]
--storybookPort, -p, --storybook-port Port to run Storybook [number]
--storybookHost, -h, --storybook-host Host to run Storybook [string]
--storybookConfigDir, -c, --storybook-config-dir Path to Storybook's config folder (defaults to .storybook) [string]
--storybookStaticDir, --storybook-static-dir Path to Storybook's static files folder [string]
--showStorybookOutput, --show-storybook-output Whether or not you want to see Storybook output [boolean]
--readStoriesTimeout, --read-stories-timeout The time to wait until all stories are read, before starting the visual tests [number]
--startStorybookServerTimeout The time to wait until the storybook server is built and serving (in seconds) [number]
--exitcode, -e If tests failed close with non-zero exit code [boolean]
Concurrency
The default level of concurrency for free accounts is 5
. This means that only up to 5 visual tests can run in parallel, and therefore the execution might be slow.
If your account does support a higher level of concurrency, it's possible to pass a different value by specifying it in the property testConcurrency
in the applitools.config.js file (see Advanced configuration section below).
If you are interested in speeding up your visual tests, contact sdr@applitools.com to get a trial account and faster tests with more concurrency.
Advanced configuration
In addition to command-line arguments, it's possible to define the following configuration parameteres for tests by using environment variables or the applitools.config.js file:
Property name | Default value | Description |
---|
storybookUrl | undefined | URL to storybook (also available as command-line argument). |
storybookPort | 9000 | Port to run Storybook (also available as command-line argument). |
storybookHost | localhost | Host to run Storybook (also available as command-line argument). |
storybookConfigDir | .storybook | Path to Storybook's config folder (also available as command-line argument). |
storybookStaticDir | undefined | Path to Storybook's static files folder (also available as command-line argument). |
showStorybookOutput | undefined | Whether or not you want to see Storybook output (also available as command-line argument). |
viewportSize | { width: 1024, height: 600} | The size of the puppeteer browser's window. This is the browser window which renders the stories originally (and opens at the size provided in the viewportSize parameter), and then a DOM snapshot is uploaded to the server, which renders this snapshot on all the browsers and sizes provided in the browser parameter. |
exitcode | true | When set to true - if there were visual differences or other errors, close with non-zero exit code. When set to false - always close with zero exit code (i.e. never fail the build). When set to nodiffs - close with non-zero exit code only for errors and not for visual differences. (also available as command-line argument: --exitcode true/false/nodiffs ) |
browser | { width: 1024, height: 768, name: 'chrome' } | The size and browser of the generated screenshots. For more info and possible values, see the browser section below. |
showLogs | false | Whether or not you want to see logs of the Eyes-Storybook plugin. |
batch | undefined | An object which describes different aspects of the batch. The following lines in this table depict the various ways to configure the batch. |
batch.id | random | Provides ability to group tests into batches. Read more about batches here. |
batch.name | The name of the first test in the batch | Provides a name to the batch (for display purpose only). |
batch.sequenceName | undefined | Name for managing batch statistics. |
batch.notifyOnCompletion | false | If true batch completion notifications are sent. |
batch.properties | undefined | Custom properties for the entire batch. The format is an array of objects with name/value properties. For example: [{name: 'My prop', value:'My value'}] . |
baselineEnvName | undefined | The name of the environment of the baseline. |
envName | undefined | A name for the environment in which the application under test is running. |
ignoreCaret | false | Whether to ignore or the blinking caret or not when comparing images. |
matchLevel | undefined | The test-wide match level to use when checking application screenshot with the expected output. Possible values are Strict , Exact , Layout and Content . Read more about match levels here. |
branchName | undefined | The name of the branch. |
baselineBranchName | undefined | The name of the baseline branch. |
parentBranchName | undefined | Sets the branch under which new branches are created. |
proxy | undefined | Sets the proxy settings to be used in network requests to Eyes server. This can be either a string to the proxy URI, or an object containing the URI, username and password.
For example:
{url: 'https://myproxy.com:443', username: 'my_user', password: 'my_password', isHttpOnly: false} or:
"https://username:password@myproxy.com:443" |
saveFailedTests | false | Set whether or not failed tests are saved by default (saved as baseline). |
saveNewTests | true | Set whether or not new tests are saved by default (saved as baseline). |
serverUrl | Default Eyes server URL | The URL of Eyes server |
compareWithParentBranch | false | |
ignoreBaseline | false | |
runInDocker | false | If you are having issues running the SDK in docker, set this flag to true . See more info below |
puppeteerOptions | undefined | Options to send to puppeteer.launch . This is a low-level configuration and should be used with great care. Example use { args: ['--no-sandbox'], headless: false, devtools: true} |
puppeteerExtraHTTPHeaders | undefined | Options to send to page.setExtraHTTPHeaders . See docs |
jsonFilePath | undefined | Directory path of a results file. If set, then a JSON file is created in this directory, the file is created with the name eyes.json and contains the Eyes test results. |
tapFilePath | undefined | Directory path of a results file. If set, then a TAP file is created in this directory, the file is created with the name eyes.tap and contains the Eyes test results. |
xmlFilePath | undefined | Directory path of a results file. If set, then a XUnit XML file is created in this directory, the file is created with the name eyes.xml and contains the Eyes test results. |
waitBeforeCapture | undefined | Selector, function or timeout. If number then the argument is treated as time in milliseconds to wait before all screenshots. If string then the argument is treated as a selector for elements to wait for before all screenshots. If function , then the argument is treated as a predicate to wait for before all screenshots.
For per component configuration see waitBeforeCapture. Note that we use Puppeteer's page.waitForTimeout(), page.waitForSelector(), page.waitForXPath() and page.waitForFunction(), checkout it's API for more details. |
include | true | A predicate function, a string or a regular expression specifying which stories should be visually tested. Visual baselines will be created only for the components specified. The function receives an object with name , kind , storyTitle , index and parameters properties. For example (exclude all stories with a name that start with [SKIP]):
({name, kind, storyTitle, index, parameters}) => !/^\[SKIP\]/.test(name) For more information, see per component configuration - include. |
variations | undefined | Specifies additional variations for all or some of the stories. For example, RTL. For more information, see per component configuration - variations. |
dontCloseBatches | false | If true, batches are not closed for notifyOnCompletion. |
testConcurrency | 5 | The maximum number of tests that can run concurrently. The default value is the allowed amount for free accounts. For paid accounts, set this number to the quota set for your account. |
readStoriesTimeout | 60000 | The amount of time (in milliseconds) Eyes-Storybook waits for storybook to load. For old storybook versions 2 and 3, this is also the time it takes for Eyes-Storybook to acknowledge it is working on those versions. So it is recommended to make this value small (e.g. 3000) when working with Storybook version 2 or 3. |
ignoreDisplacements | false | Sets whether Test Manager should intially display mismatches for image features that have only been displaced, as opposed to real mismatches. This can also be specified per-story, see per component configuration - ignoreDisplacements |
properties | undefined | Adds custom properties for each test. These show up in Test Manager, and tests can be grouped by custom properties. By default, Eyes-Storybook adds 2 custom properties for each test: the Component name and State of each component. Adding more properties via this config param will not override these two properties. |
ignoreRegions | undefined | An array of regions to ignore when comparing the checkpoint screenshot with the baseline screenshot. For more information, see per component configuration - ignoreRegions |
floatingRegions | undefined | An array of regions to consider as floating when comparing the checkpoint screenshot with the baseline screenshot. For more information, see per component configuration - floatingRegions |
layoutRegions | undefined | An array of regions to consider as match level Layout when comparing the checkpoint screenshot with the baseline screenshot. For more information, see per component configuration - layoutRegions |
strictRegions | undefined | An array of regions to consider as match level Strict when comparing the checkpoint screenshot with the baseline screenshot. For more information, see per component configuration - strictRegions |
contentRegions | undefined | An array of regions to consider as match level Content when comparing the checkpoint screenshot with the baseline screenshot. For more information, see per component configuration - contentRegions |
accessibilityRegions | undefined | An array of regions to validate accessibility when comparing the checkpoint screenshot with the baseline screenshot. Validation is according to the configured accessibilityValidation . For more information, see per component configuration - contentRegions |
accessibilityValidation | undefined | An object that specifies the accessibility level and guidelines version to use for the screenshots. Possible values for level are None , AA and AAA , and possible values for guidelinesVersion are WCAG_2_0 and WCAG_2_1 . For example: {level: 'AA', guidelinesVersion: 'WCAG_2_0'} . For more information, see per component configuration - accessibilityValidation |
layoutBreakpoints | undefined | When set to true , a snapshot of the DOM will be taken once for each browser/device size in the browser configuration. For optimization purposes, an array of numbers can be passed. The DOM snapshot will be taken once for every width in the array. For more information, see per component configuration - layoutBreakpoints |
sendDom | true | A flag to specify whether a capture of DOM and CSS should be taken when rendering the screenshot. The default value is true. This should only be modified to troubleshoot unexpected behavior, and not for normal production use. For more information, see per component configuration - sendDom |
visualGridOptions | undefined | An object that specifies options to configure renderings on the Ultrafast grid. See more information per component configuration - visualGridOptions |
There are 2 ways to specify test configuration:
- Environment variables
- The
applitools.config.js
file
The list above is also the order of precedence, which means that if you specify a property as an environment variable, it will override the value defined for the same property in the applitools.config.js
file.
Method 1: Environment variables
The name of the corresponding environment variable is in uppercase, with the APPLITOOLS_
prefix, and separating underscores instead of camel case:
APPLITOOLS_APP_NAME
APPLITOOLS_SHOW_LOGS
APPLITOOLS_BATCH_ID
APPLITOOLS_BATCH_NAME
APPLITOOLS_BATCH_SEQUENCE_NAME
APPLITOOLS_PROXY
APPLITOOLS_NOTIFY_ON_COMPLETION
...
Method 2: The applitools.config.js
file
It's possible to have a file called applitools.config.js
at the current working directory (the directory you are at when running the eyes-storybook
script). In this file specify the desired configuration, as an exported CommonJS module. For example:
module.exports = {
appName: 'My app',
showLogs: true,
batchName: 'My batch'
...
}
Configuring the browser
Eyes-Storybook will take a screenshot of the page as specified in the browser
configuration parameter.
Possible values are:
chrome
firefox
edgechromium
edgelegacy
ie10
ie11
safari
chrome-one-version-back
chrome-two-versions-back
firefox-one-version-back
firefox-two-versions-back
safari-one-version-back
safari-two-versions-back
edgechromium-one-version-back
edgechromium-two-versions-back
Previous browser versions
*-one-version-back
and *-two-versions-back
are relative to the version of the same browser. For example, if chrome
refers to version 79, then chrome-one-version-back
will be Chrome 78 and chrome-two-versions-back
will be Chrome 77.
Getting a screenshot of multiple browsers in parallel
It's also possible to send an array of browsers, for example in the applitools.config.js
file:
module.exports = {
browser: [
{width: 800, height: 600, name: 'firefox'},
{width: 1024, height: 768, name: 'chrome'},
{width: 1024, height: 768, name: 'ie11'}
]
}
Device emulation
To enable chrome's device emulation, it's possible to send a device name and screen orientation, for example:
module.exports = {
browser: {
deviceName: 'iPhone X',
screenOrientation: 'landscape',
name: 'chrome'
}
}
Possible values for screen orientation are landscape
and portrait
, and if no value is specified, the default is portrait
.
The list of device names is available at https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-api/src/enums/DeviceName.ts
In addition, it's possible to use chrome's device emulation with custom viewport sizes, pixel density and mobile mode, by passing deviceScaleFactor
and mobile
in addition to width
and height
. For example:
module.exports = {
browser: {
width: 800,
height: 600,
deviceScaleFactor: 3,
mobile: true,
name: 'chrome'
}
}
iOS device
module.exports = {
browser: {
iosDeviceInfo: {
deviceName: 'iPhone XR',
screenOrientation: 'landscape',
iosVersion: 'latest'
},
}
}
The list of devices is available at https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-api/src/enums/IosDeviceName.ts
Possible values for iosVersion
are:
'latest'
- the latest iOS version that's supported by the UFG'latest-1'
- one version prior to the latest versionundefined
- the UFG's default
Faking IE Browser
Some pages render differently on Internet Explorer and as such, it may be important to take the dom-snapshot while running on a fake IE browser using the ultrafast grid.
Using the fakeIE
flag - you can render the stories on a chrome masquerading as IE.
We do this by simulating the userAgent
and documentMode
of the page - making the page believe it is being rendered on IE.
There is a small performance impact when using fake IE tests - since the browser needs to fake IE for each story it renders.
Per component configuration
Only supported in Storybook version >= 4
There are two ways to provide configuration for a specific story, or a group of stories.
-
As an argument to the story - It's possible to pass a third argument to storybook's .add
function to customize each story. An eyes
property on the parameters object can be specified with configuration properties.
-
In the global configuration file, applitools.config.js
- If a function is specified for one of the properties below, it will be called for each story, and will be passed the story's metadata, of the structure {name, kind, parameters}
, where name
is the name of the component, kind
is the string built by storybook for the category, e.g. Forms|Input/Text
, and parameters
are the third argument to storybook's .add
function. The function should return the configuration value for the specific property+story.
Specifying a value locally in the story takes precedence over the global config value.
The following properties are supported:
include
global
When provided globally, include
is a function that receives the story's kind
and name
, storyTitle
and parameters
.
All these properties except storyTitle
come from storybook
and they represent the hierarchy of the stories:
kind
- The stories directory and section, where applicable. Nested directory structures are allowed in storybook
. These will be suffixed by /
, while a section - that can hold many directories will be suffixed with a |
.
For example:
- Story named
Button
in the Components
directory - its kind
will be Components
. - Story named
Button
in the Components
directory under the App
section - its kind
will be App|Components
. - Story named
RadioButton
in the Radio
subdirectory of the Components
directory under the App
section - its kind
will be App|Components/Radio
.
name
- the story name.
For example:
- Story named
Button
in the Components
directory - its name
will be Button
.
parameters
- custom parameters that can specified in the story.
storyTitle
is generated by the SDK and is used as the test name. Therefore it is easy to find and filter by it.
More information can be found in the Storybook docs - Naming components and hierarchy.
You can filter by kind
, name
, storyTitle
, index
, parameters
, a combination of them or any logic that will result in a boolean
. For example:
module.exports = {
...
include: ({kind}) => {
return kind === 'App|Components/Radio'
}
...
}
When passing a storyTitle
as a string
only this story will be tested. For example:
module.exports = {
...
include: "Button: with text",
...
}
When passing a storyTitle
as a Regex
only matching stories will be tested. For example:
module.exports = {
...
include: /Button: */,
...
}
The include
method can also be used for test sharding, to split the stories between multiple machines:
module.exports = {
...
include: ({index}) => {
return index % totalShards === currShard - 1
}
...
}
NOTE you can use regular expressions or any other method you'd like, as long as you return a boolean
from this function
component
When false
, the component will not be visually tested. For example:
storiesOf('Some kind', module)
.add(
'Some story',
() => <div>I am visually perfect!</div>,
{eyes: {include: false}}
)
variations
An array of object values, which specifies which variations to add for this story. For each value, an additional visual test will be executed for the component. Each variation could contain queryParams
and properties
fields to specifies custom query parameters and eyes properties respectfully. Variation tests will have the same name.
This can accommodate many use cases, for example @storybook/addon-contexts
. With addons like this it is possible to render components in a different way depends on query parameters in URL. For Example, here's a storybook that handles an RTL variation:
const isRTL = new URL(window.location).searchParams.get('eyes-variation') === 'RTL';
if (isRTL) {
document.documentElement.setAttribute('dir', 'rtl')
}
storiesOf('Components that support RTL', module)
.add(
'Some story',
() => <div>
<span>I am visually perfect!</span>
<span>{isRTL ? ' and rendered right to left as well :)' : ''}</span>
</div>,
{eyes: {variations: [{queryParams: {'eyes-variation': 'RTL'}, properties: {name: 'isRTL', value: 'true'}, {properties: {name: 'isRTL', value: 'false'}}]}}
)
waitBeforeCapture
Selector or timeout, see advanced configuration for more details.
storiesOf('Components with a waitBeforeCapture', module)
.add(
'Some story',
() => <span id="container" class="loading"></span>,
{eyes: { waitBeforeCapture: '#container.ready' }}
);
Note that the predicate option for waitBeforeCapture
is currently not available in the per component configuration.
properties
Adds custom properties for each test. These show up in Test Manager, and tests can be grouped by custom properties. By default, Eyes-Storybook adds 2 custom properties for each test: the Component name and State of each component. Adding more properties via this config param will not override these two properties.
For example:
storiesOf('Components with custom properties', module)
.add(
'Some story',
() => <span id="container" class="loading"></span>,
{eyes: { properties: [
{name: 'some prop #1', value: 'some value #1'},
{name: 'some prop #2', value: 'some value #2'},
] }}
);
ignoreRegions
A single or an array of regions to ignore when checking for visual differences. For example:
storiesOf('Components with ignored region', module)
.add(
'Some story',
() =>
<div>
<span>I am visually perfect!</span>
<span className="ignore-this">this should be ignored</span>
</div>,
{eyes: {
ignoreRegions: [
{selector: '.ignore-this'},
{left: 10, top: 20, width: 200, height: 80}
]}
}
)
floatingRegions
An array of regions to consider as floating when comparing the checkpoint screenshot with the baseline screenshot. For example:
storiesOf('Components with floating region', module)
.add(
'Some story',
() =>
<div>
<span>I am visually perfect!</span>
<span className="floating-region">this should be floating</span>
</div>,
{eyes: {
floatingRegions: [
{
selector: '.floating-region',
maxUpOffset: 10,
maxDownOffset: 20,
maxLeftOffset: 30,
maxRightOffset: 40,
},
{
left: 10,
top: 20,
width: 200,
height: 80,
maxUpOffset: 10,
maxDownOffset: 20,
maxLeftOffset: 30,
maxRightOffset: 40,
}
]}
}
)
layoutRegions
An array of regions to consider as match level Layout when comparing the checkpoint screenshot with the baseline screenshot. For example:
storiesOf('Components with layout region', module)
.add(
'Some story',
() =>
<div>
<span>I am visually perfect!</span>
<span className="layout-region">this should be compared with layout match level</span>
</div>,
{eyes: {
layoutRegions: [
{selector: '.layout-region'},
{left: 10, top: 20, width: 200, height: 80}
]}
}
)
contentRegions
An array of regions to consider as match level Content when comparing the checkpoint screenshot with the baseline screenshot. For example:
storiesOf('Components with content region', module)
.add(
'Some story',
() =>
<div>
<span>I am visually perfect!</span>
<span className="content-region">this should be compared with content match level</span>
</div>,
{eyes: {
contentRegions: [
{selector: '.content-region'},
{left: 10, top: 20, width: 200, height: 80}
]}
}
)
strictRegions
An array of regions to consider as match level Strict when comparing the checkpoint screenshot with the baseline screenshot. For example:
storiesOf('Components with strict region', module)
.add(
'Some story',
() =>
<div>
<span>I am visually perfect!</span>
<span className="strict-region">this should be compared with strict match level</span>
</div>,
{eyes: {
strictRegions: [
{selector: '.strict-region'},
{left: 10, top: 20, width: 200, height: 80}
]}
}
)
accessibilityRegions
A single or an array of regions for accessibility checking. For example:
storiesOf('Components with accessibility regions', module)
.add(
'Some story',
() =>
<div>
<span>I am visually perfect!</span>
<span className="check-me">this should be tested for accessibility</span>
</div>,
{eyes: {
accessibilityRegions: [
{accessibilityType: 'RegularText', selector: '.check-me'},
{accessibilityType: 'RegularText', left: 10, top: 20, width: 200, height: 80}
]
}}
)
});
Possible accessibilityType values are: IgnoreContrast
,RegularText
,LargeText
,BoldText
and GraphicalObject
.
accessibilityValidation
The level and guidelines version that should be used when validation accesibility regions. For example:
storiesOf('Components with accessibility regions', module)
.add(
'Some story',
() => <div>
<span>I am visually perfect!</span>
<span className="check-me">this should be tested for accessibility</span>
</div>,
{eyes: {
accessibilityValidation: {
level: 'AA',
guidelinesVersion: 'WCAG_2_0'
}
}}
)
});
Possible values for level
are: AA
and AAA
.
Possible values for guidelinesVersion
are: WCAG_2_0
and WCAG_2_1
.
ignoreDisplacements
Sets whether Test Manager should intially display mismatches for image features that have only been displaced, as opposed to real mismatches. For example:
storiesOf('Components with ignoreDisplacements', module)
.add(
'Some story',
() => <div>
<span>I am visually perfect!</span>
</div>,
{eyes: {
ignoreDisplacements: true
}}
)
});
sendDom
storiesOf('Components', module)
.add(
'Some story ',
() =>
<div>Some Story</div>, {
eyes: {
sendDom: false
}
})
visualGridOptions
An object that specifies options to configure renderings on the Ultrafast grid.
Available options:
polyfillAdoptedStyleSheets
: Creates a polyfill when the DOM contains adoptedStyleSheets
(reference) for browsers that don't support it (It is currently supported only in Chrome). When true
, those browsers will successfully include the css as inline style tags. When false
, the css will not be included. When undefined
, an error will be thrown with a message stating that this feature is not supported in the desired browser.ieV2
: Use IE environment v2 in the UFG.
storiesOf('Components', module)
.add(
'Some story ',
() =>
<div>Some Story</div>, {
eyes: {
visualGridOptions: {
polyfillAdoptedStyleSheets: true,
ieV2: true
}
}
})
scriptHooks
A set of scripts to be run by the browser during the rendering. It is intended to be used as a means to alter the page's state and structure at the time of rendering.
An object with the following properties:
beforeCaptureScreenshot
A script that runs after the page is loaded but before taking the screenshot. For example:
storiesOf('Components', module)
.add(
'Some story',
() =>
<div>Some Story</div>, {
eyes: {
scriptHooks: {
beforeCaptureScreenshot: "document.body.style.backgroundColor = 'gold'"
}
}
})
runBefore
and runAfter
functions
The runBefore
function can be used to perform any action prior to taking the story snapshot. The runAfter
method should be used to cleanup any side-effect that runBefore
creates, if there are any.
For example, if runBefore
adds a class to the body
element, this class should be removed in runAfter
. This is because the browser tab's window is not reloaded between stories.
Note: rootEl
also needs to be cleaned up, so any modification that was done on this element in runBefore
should be reverted in runAfter
.
runBefore
An asynchronous function that will be evaluated before the story's screenshot is taken. This is the place to perform any interaction with the story using DOM API's.
For performing various DOM interactions, we recommend checking out dom-testing-library. It provides utilities to interact, query and wait for conditions on the DOM.
For example, a component that renders a popover could trigger the opening of the popover and wait for content to appear:
import {wait, within, fireEvent} from '@testing-library/dom';
storiesOf('UI components', module)
.add('Popover', () => <Popover />, {
eyes: {
runBefore({rootEl, story}) {
fireEvent.click(within(rootEl).getByText('Open'))
return wait(() => within(rootEl).getByText('Close'))
}
},
})
runAfter
An asynchronous function that is evaluated after the story's screenshot is taken. This is the place to perform any clean ups that could change the way the next story renders.
For example, reverting back to the original background color that was changed by a previous component:
.add('background color', () => (
<div style={{fontSize: '30px'}}>Component with runBefore hook that modifies the background color</div>
), {
eyes: {
runBefore({rootEl, story}) {
window.originalBackgoundColor = document.querySelector("html").style.backgroundColor;
document.querySelector("html").style.backgroundColor = 'fuchsia';
},
runAfter({rootEl, story}){
document.querySelector("html").style.backgroundColor = window.originalBackgoundColor;
delete window.originalBackgoundColor;
}
}
})
layoutBreakpoints
storiesOf('Components', module)
.add(
'Some story with breakpoints for all browser and device sizes',
() =>
<div>Some Story</div>, {
eyes: {
layoutBreakpoints: true
}
})
.add(
'Some story with breakpoints for desktop and mobile',
() =>
<div>Some Story</div>, {
eyes: {
layoutBreakpoints: [500, 1200]
}
})
Running Eyes-Storybook in Docker
When running the SDK in docker, there might be issues related to properly launching the internal chrome browser via puppeteer. If you seem to have such issues, set runInDocker: true
in your config file. This will pass the internal chrome browser special arguments, as described here.
If you still have issues, you might need to follow the instructions to use your own chromium browser in the docker container, and point that to the SDK's puppeteer. Follow the instructions here and set the executablePath
via the puppeteerOptions
. For example, applitools.config.js
:
module.exports = {
puppeteerOptions: {
executablePath: '/usr/bin/chromium-browser'
}
}
Dealing with dynamic data
Sometimes components render dynamic data, such as dates, or random data. This creates a challenge when testing these components. The way we recommend to address this issue is to insert code into your storybook which normalizes the data (uses fixed dates, or a specific seed), when it is being run in an automated environment.
Eyes storybook makes it possible for components to be aware that they are being tested. There will be a specific query parameter on the URL of the story's iframe: ?eyes-storybook=true
.
This way it's possible to write a story like this:
const isBeingTested =
new URL(window.location).searchParams.get('eyes-storybook')
const SOME_FIXED_DATE = 354060000000
const date = new Date(isBeingTested ? SOME_FIXED_DATE : undefined)
storiesOf('Some kind', module).add('Date', () => <div>{date}</div>)
Storybook interactions Play functionality
Since version 3.28.0, there is a support for Storybooks' Interactions Play functionality: Stories who use 'Play', screenshot will be taken automatically after the 'Play' flow is done.
This will replace the existing point of taking screenshots, it should not affect any new or existing Stories which are not using the 'Play' functionality.
Tests that use the waitBeforeCapture
property, the wait period will begin after the 'Play' flow is done.
You can read more at Storybook interactions documentation:
https://storybook.js.org/docs/react/essentials/interactions