EUI uses Loki for component visual regression testing. Loki compares snapshots of previously approved against current Storybook stories and ensures there are no unexpected differences.
Visual regression tests are being run automatically on every pull request.
Loki uses a Docker container running Chrome as a stable way to take screenshots. Please make sure you have Docker installed and running locally.
Before running Loki you need to start a local Storybook server in either development or production mode:
yarn storybook --no-open
Now you can run Loki to test for visual regressions:
yarn test-visual-regression
You can also run the test for specific stories only by using --storiesFilter
:
yarn test-visual-regression --storiesFilter EuiComboBox
To add baseline reference images run:
# all components
yarn test-visual-regression update
# specific component
yarn test-visual-regression update --storiesFilter EuiComboBox
Some stories cannot be tested for various reasons. You can set
{ loki: { skip: true } }
to the Story object to skip them.
Please leave a comment explaining why the story is skipped for future context.
Example:
const meta: Meta<MyComponentProps> = {
component: MyComponent,
parameters: {
loki: {
skip: true,
},
},
};
Stories of components that render text nodes or portalled elements need
to specify a custom Loki selector to tell it what we want to take
a screenshot of using the chromeSelector
property.
We provide a few predefined Loki selectors in the .storybook/loki.ts file.
Example:
const meta: Meta<MyComponentProps> = {
component: MyComponent,
parameters: {
loki: {
// LOKI_SELECTORS can be imported from .storybook/loki.ts
chromeSelector: LOKI_SELECTORS.portal,
},
},
};
You can use storybooks interactions
to run VRT testing on specific component states after user interaction.
Use lokiPlayDecorator
function to ensure VRT testing runs
- a) as defined (VRT only vs storybook & VRT)
- b) after interactions were run
import { userEvent, waitFor, within, expect } from '@storybook/test';
import { lokiPlayDecorator } from '/.storybook/loki'
const Playground = {
play: lokiPlayDecorator(async (context) => {
const { bodyElement, canvasElement, step } = context;
// for any content inside the story wrapper use canvasElement
const canvas = within(canvasElement);
// any content outside the story wrapper (e.g. added via portals) use bodyElement
const canvas = within(bodyElement);
/* NOTE: multi-steps breaks on going through steps on not found elements
for show/hide actions. It seems more reliable to use a single step for
these interactions as they are used for VRT only (so far). */
await step('show popover on click', async () => {
await userEvent.click(canvas.getByRole('combobox'));
await waitFor(() => {
expect(canvas.getByRole('listbox')).toBeVisible();
})
}
);
})
}