Skip to content

Commit a0e29f2

Browse files
Add Storybook interaction tests with WDIO tests (#386)
* feat: add new storybook method to wait for storybook component to be loaded * fix: fix types * feat: make url and clipselector optional * feat: add specs to the storybook specs * test: add new e2e test * test: added new uts * fix: change customcommand property * chore: add docs and changeset * chore: add new screenshot * Update packages/visual-service/src/service.ts Co-authored-by: Christian Bromann <[email protected]> --------- Co-authored-by: Christian Bromann <[email protected]>
1 parent 4e7edac commit a0e29f2

File tree

14 files changed

+710
-342
lines changed

14 files changed

+710
-342
lines changed

.changeset/friendly-jokes-relax.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
"@wdio/visual-service": minor
3+
---
4+
5+
Adding storybook interaction testing
6+
7+
### Storybook Interaction Testing
8+
9+
Storybook Interaction Testing allows you to interact with your component by creating custom scripts with WDIO commands to set a component into a certain state. For example, see the code snippet below:
10+
11+
```ts
12+
import { browser, expect } from "@wdio/globals";
13+
14+
describe("Storybook Interaction", () => {
15+
it("should create screenshots for the logged in state when it logs out", async () => {
16+
const componentId = "example-page--logged-in";
17+
await browser.waitForStorybookComponentToBeLoaded({ id: componentId });
18+
19+
await expect($("header")).toMatchElementSnapshot(
20+
`${componentId}-logged-in-state`
21+
);
22+
await $("button=Log out").click();
23+
await expect($("header")).toMatchElementSnapshot(
24+
`${componentId}-logged-out-state`
25+
);
26+
});
27+
28+
it("should create screenshots for the logged out state when it logs in", async () => {
29+
const componentId = "example-page--logged-out";
30+
await browser.waitForStorybookComponentToBeLoaded({ id: componentId });
31+
32+
await expect($("header")).toMatchElementSnapshot(
33+
`${componentId}-logged-out-state`
34+
);
35+
await $("button=Log in").click();
36+
await expect($("header")).toMatchElementSnapshot(
37+
`${componentId}-logged-in-state`
38+
);
39+
});
40+
});
41+
```
42+
43+
Two tests on two different components are executed. Each test first sets a state and then takes a screenshot. You will also notice that a new custom command has been introduced, which can be found [here](#new-custom-command).
44+
45+
The above spec file can be saved in a folder and added to the command line with the following command:
46+
47+
```sh
48+
npm run test.local.desktop.storybook.localhost -- --spec='tests/specs/storybook-interaction/*.ts'
49+
```
50+
51+
The Storybook runner will first automatically scan your Storybook instance and then add your tests to the stories that need to be compared. If you don't want the components that you use for interaction testing to be compared twice, you can add a filter to remove the "default" stories from the scan by providing the [`--skipStories`](#--skipstories) filter. This would look like this:
52+
53+
```sh
54+
npm run test.local.desktop.storybook.localhost -- --skipStories="/example-page.*/gm" --spec='tests/specs/storybook-interaction/*.ts'
55+
```
56+
57+
### New Custom Command
58+
59+
A new custom command called `browser.waitForStorybookComponentToBeLoaded({ id: 'componentId' })` will be added to the `browser/driver`-object that will automatically load the component and wait for it to be done, so you don't need to use the `browser.url('url.com')` method. It can be used like this
60+
61+
```ts
62+
import { browser, expect } from "@wdio/globals";
63+
64+
describe("Storybook Interaction", () => {
65+
it("should create screenshots for the logged in state when it logs out", async () => {
66+
const componentId = "example-page--logged-in";
67+
await browser.waitForStorybookComponentToBeLoaded({ id: componentId });
68+
69+
await expect($("header")).toMatchElementSnapshot(
70+
`${componentId}-logged-in-state`
71+
);
72+
await $("button=Log out").click();
73+
await expect($("header")).toMatchElementSnapshot(
74+
`${componentId}-logged-out-state`
75+
);
76+
});
77+
78+
it("should create screenshots for the logged out state when it logs in", async () => {
79+
const componentId = "example-page--logged-out";
80+
await browser.waitForStorybookComponentToBeLoaded({ id: componentId });
81+
82+
await expect($("header")).toMatchElementSnapshot(
83+
`${componentId}-logged-out-state`
84+
);
85+
await $("button=Log in").click();
86+
await expect($("header")).toMatchElementSnapshot(
87+
`${componentId}-logged-in-state`
88+
);
89+
});
90+
});
91+
```
92+
93+
The options are:
94+
95+
#### `clipSelector`
96+
97+
- **Type:** `string`
98+
- **Mandatory:** No
99+
- **Default:** `#storybook-root > :first-child` for Storybook V7 and `#root > :first-child:not(script):not(style)` for Storybook V6
100+
- **Example:**
101+
102+
```ts
103+
await browser.waitForStorybookComponentToBeLoaded({
104+
clipSelector: "#your-selector",
105+
id: "componentId",
106+
});
107+
```
108+
109+
This is the selector that will be used:
110+
111+
- to select the element to take the screenshot of
112+
- for the element to wait to be visible before a screenshot is taken
113+
114+
#### `id`
115+
116+
- **Type:** `string`
117+
- **Mandatory:** yes
118+
- **Example:**
119+
120+
```ts
121+
await browser.waitForStorybookComponentToBeLoaded({ '#your-selector', id: 'componentId' })
122+
```
123+
124+
Use the `id` of the story that can be found in the URL of the story. For example, the `id` in this URL `http://localhost:6006/?path=/story/example-page--logged-out` is `example-page--logged-out`
125+
126+
#### `timeout`
127+
128+
- **Type:** `number`
129+
- **Mandatory:** No
130+
- **Default:** 1100 milliseconds
131+
- **Example:**
132+
133+
```ts
134+
await browser.waitForStorybookComponentToBeLoaded({
135+
id: "componentId",
136+
timeout: 20000,
137+
});
138+
```
139+
140+
The max timeout we want to wait for a component to be visible after loading on the page
141+
142+
#### `url`
143+
144+
- **Type:** `string`
145+
- **Mandatory:** No
146+
- **Default:** `http://127.0.0.1:6006`
147+
- **Example:**
148+
149+
```ts
150+
await browser.waitForStorybookComponentToBeLoaded({
151+
id: "componentId",
152+
url: "https://your.url",
153+
});
154+
```
155+
156+
The URL where your Storybook instance is hosted.

0 commit comments

Comments
 (0)