@applitools/eyes-storybook 3.26.1 → 3.27.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +9 -17
- package/dist/getClientAPI.js +19 -1
- package/dist/getStories.js +19 -1
- package/dist/renderStoryWithClientAPI.js +21 -3
- package/dist/runRunAfterScript.js +19 -1
- package/dist/runRunBeforeScript.js +19 -1
- package/package.json +12 -12
- package/src/addVariationStories.js +104 -0
- package/src/browser/getClientAPI.js +19 -1
- package/src/browser/renderStoryWithClientAPI.js +2 -2
- package/src/eyesStorybook.js +4 -4
- package/src/getStoryBaselineName.js +9 -3
- package/src/getStoryData.js +23 -23
- package/src/getStoryTitle.js +3 -4
- package/src/getStoryUrl.js +12 -5
- package/src/renderStory.js +6 -6
- package/src/storybookConnector.js +1 -1
- package/src/addParametrizedStories.js +0 -81
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,35 @@
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
## 3.27.3 - 2022/1/26
|
|
7
|
+
|
|
8
|
+
- avoid unwanted page reloading due to unknown query parameters
|
|
9
|
+
- updated to @applitools/driver@1.4.9 (from 1.4.7)
|
|
10
|
+
- updated to @applitools/eyes-sdk-core@13.0.0 (from 12.24.9)
|
|
11
|
+
- updated to @applitools/logger@1.0.9 (from 1.0.8)
|
|
12
|
+
- updated to @applitools/visual-grid-client@15.8.62 (from 15.8.55)
|
|
13
|
+
|
|
14
|
+
## 3.27.2 - 2021/12/23
|
|
15
|
+
|
|
16
|
+
- updated to @applitools/driver@1.4.7 (from 1.4.5)
|
|
17
|
+
- updated to @applitools/eyes-sdk-core@12.24.9 (from 12.24.7)
|
|
18
|
+
- updated to @applitools/logger@1.0.8 (from 1.0.7)
|
|
19
|
+
- updated to @applitools/visual-grid-client@15.8.55 (from 15.8.53)
|
|
20
|
+
|
|
21
|
+
## 3.27.1 - 2021/12/21
|
|
22
|
+
|
|
23
|
+
- add support for storybook api 6.4
|
|
24
|
+
- updated to @applitools/driver@1.4.5 (from 1.3.5)
|
|
25
|
+
- updated to @applitools/eyes-sdk-core@12.24.7 (from 12.24.5)
|
|
26
|
+
- updated to @applitools/logger@1.0.7 (from 1.0.6)
|
|
27
|
+
- updated to @applitools/test-server@1.0.8 (from 1.0.7)
|
|
28
|
+
- updated to @applitools/visual-grid-client@15.8.53 (from 15.8.49)
|
|
29
|
+
|
|
30
|
+
## 3.27.0 - 2021/12/6
|
|
31
|
+
|
|
32
|
+
- support custom properties and query params
|
|
33
|
+
- remove `queryParams` api from story and global config
|
|
34
|
+
|
|
6
35
|
## 3.26.1 - 2021/11/28
|
|
7
36
|
|
|
8
37
|
- add custom test properties to stories with query params
|
package/README.md
CHANGED
|
@@ -28,7 +28,6 @@ Applitools Eyes SDK for [Storybook](http://storybook.js.org).
|
|
|
28
28
|
- [global](#global)
|
|
29
29
|
- [component](#component)
|
|
30
30
|
- [`variations`](#variations)
|
|
31
|
-
- [`queryParams`](#queryparams)
|
|
32
31
|
- [`waitBeforeCapture`](#waitbeforecapture)
|
|
33
32
|
- [`properties`](#properties)
|
|
34
33
|
- [`ignoreRegions`](#ignoreregions)
|
|
@@ -157,9 +156,12 @@ In addition to command-line arguments, it's possible to define the following con
|
|
|
157
156
|
| `exitcode` | false | If tests failed close with non-zero exit code (also available as command-line argument). |
|
|
158
157
|
| `browser` | { width: 800, height: 600, name: 'chrome' } | The size and browser of the generated screenshots. For more info and possible values, see the [browser section below](#configuring-the-browser).|
|
|
159
158
|
| `showLogs` | false | Whether or not you want to see logs of the Eyes-Storybook plugin. |
|
|
160
|
-
| `
|
|
161
|
-
| `
|
|
162
|
-
| `
|
|
159
|
+
| `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. |
|
|
160
|
+
| `batch.id` | random | Provides ability to group tests into batches. Read more about batches [here](https://applitools.com/docs/topics/working-with-test-batches/how-to-group-tests-into-batches.html). |
|
|
161
|
+
| `batch.name` | The name of the first test in the batch | Provides a name to the batch (for display purpose only). |
|
|
162
|
+
| `batch.sequenceName` | undefined | Name for managing batch statistics. |
|
|
163
|
+
| `batch.notifyOnCompletion` | false | If `true` batch completion notifications are sent. |
|
|
164
|
+
| `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'}]`. |
|
|
163
165
|
| `baselineEnvName` | undefined | The name of the environment of the baseline. |
|
|
164
166
|
| `envName` | undefined | A name for the environment in which the application under test is running. |
|
|
165
167
|
| `ignoreCaret` | false | Whether to ignore or the blinking caret or not when comparing images. |
|
|
@@ -181,8 +183,6 @@ In addition to command-line arguments, it's possible to define the following con
|
|
|
181
183
|
| `waitBeforeCapture` | undefined | Selector, function or timeout.<br/>If ```number``` then the argument is treated as time in milliseconds to wait before all screenshots.<br/>If ```string``` then the argument is treated as a selector for elements to wait for before all screenshots.<br/>If ```function```, then the argument is treated as a predicate to wait for before all screenshots.<br/><hr/>For per component configuration see [waitBeforeCapture.](#waitBeforeCapture)<br/>Note that we use Puppeteer's [page.waitForTimeout()](https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#pagewaitfortimeoutmilliseconds), [page.waitForSelector()](https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#pagewaitforselectorselector-options), [page.waitForXPath()](https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#pagewaitforxpathxpath-options) and [page.waitForFunction()](https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#pagewaitforfunctionpagefunction-options-args), checkout it's API for more details. |
|
|
182
184
|
| `include` | true | A predicate function, a string or a regular expression specifying which stories should be visually tested.<br/>Visual baselines will be created only for the components specified.<br/>The function receives an object with ```name```, ```kind```, ```storyTitle``` and ```parameters``` properties.<br/>For example (exclude all stories with a name that start with [SKIP]):<br/>```({name, kind, storyTitle, parameters}) => !/^\[SKIP\]/.test(name)```<br/>For more information, see [per component configuration - include](#include). |
|
|
183
185
|
| `variations` | undefined | Specifies additional variations for all or some of the stories. For example, RTL. For more information, see [per component configuration - variations](#variations).|
|
|
184
|
-
| `queryParam` | undefined | Specifies additional query parameters for all or some of the stories. For more information, see [per component configuration - queryParams](#queryParams).|
|
|
185
|
-
| `notifyOnCompletion` | false | If `true` batch completion notifications are sent. |
|
|
186
186
|
| `dontCloseBatches` | false | If true, batches are not closed for notifyOnCompletion.|
|
|
187
187
|
| `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. |
|
|
188
188
|
| `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. |
|
|
@@ -425,10 +425,9 @@ storiesOf('Some kind', module)
|
|
|
425
425
|
|
|
426
426
|
### `variations`
|
|
427
427
|
|
|
428
|
-
An array of
|
|
428
|
+
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.
|
|
429
429
|
|
|
430
|
-
This can accommodate many use cases, for example
|
|
431
|
-
It's now possible for the component to render its variation version when the relevant URL param is present. For Example, here's a storybook that handles an RTL variation:
|
|
430
|
+
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:
|
|
432
431
|
|
|
433
432
|
```js
|
|
434
433
|
const isRTL = new URL(window.location).searchParams.get('eyes-variation') === 'RTL';
|
|
@@ -445,17 +444,10 @@ storiesOf('Components that support RTL', module)
|
|
|
445
444
|
<span>I am visually perfect!</span>
|
|
446
445
|
<span>{isRTL ? ' and rendered right to left as well :)' : ''}</span>
|
|
447
446
|
</div>,
|
|
448
|
-
{eyes: {variations: ['RTL']}}
|
|
447
|
+
{eyes: {variations: [{queryParams: {'eyes-variation': 'RTL'}, properties: {name: 'isRTL', value: 'true'}, {properties: {name: 'isRTL', value: 'false'}}]}}
|
|
449
448
|
)
|
|
450
449
|
```
|
|
451
450
|
|
|
452
|
-
### `queryParams`
|
|
453
|
-
|
|
454
|
-
An array of object values, which specifies `name` and `value` of query parameter which will be added to the story. For each value, an additional visual test will be executed for the component. It will have the same name display name, but still generate a different baseline.
|
|
455
|
-
|
|
456
|
-
This can accommodate many use cases, for example `@storybook/addon-contexts`.
|
|
457
|
-
With addons like this it is possible to render components in a different way depends on query parameters in URL.
|
|
458
|
-
|
|
459
451
|
### `waitBeforeCapture`
|
|
460
452
|
|
|
461
453
|
Selector or timeout, see [advanced configuration](#advanced-configuration) for more details.
|
package/dist/getClientAPI.js
CHANGED
|
@@ -7,6 +7,7 @@ function __getClientAPI(...args) {
|
|
|
7
7
|
v4: 'v4',
|
|
8
8
|
v5: 'v5',
|
|
9
9
|
v5_2: 'v5_2',
|
|
10
|
+
v6_4: 'v6_4',
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
function getClientAPI() {
|
|
@@ -19,7 +20,9 @@ function __getClientAPI(...args) {
|
|
|
19
20
|
function getStorybookVersion() {
|
|
20
21
|
const addons = frameWindow.__STORYBOOK_ADDONS;
|
|
21
22
|
|
|
22
|
-
if (frameWindow.
|
|
23
|
+
if (frameWindow.__STORYBOOK_PREVIEW__) {
|
|
24
|
+
return API_VERSIONS.v6_4;
|
|
25
|
+
} else if (frameWindow.__STORYBOOK_STORY_STORE__) {
|
|
23
26
|
return API_VERSIONS.v5_2;
|
|
24
27
|
} else if (frameWindow.__STORYBOOK_CLIENT_API__ && frameWindow.__STORYBOOK_CLIENT_API__.raw) {
|
|
25
28
|
return API_VERSIONS.v5;
|
|
@@ -81,6 +84,21 @@ function __getClientAPI(...args) {
|
|
|
81
84
|
};
|
|
82
85
|
break;
|
|
83
86
|
}
|
|
87
|
+
|
|
88
|
+
case API_VERSIONS.v6_4: {
|
|
89
|
+
api = {
|
|
90
|
+
getStories: () => {
|
|
91
|
+
return clientAPI.raw();
|
|
92
|
+
},
|
|
93
|
+
selectStory: async i => {
|
|
94
|
+
frameWindow.__STORYBOOK_PREVIEW__.urlStore.setSelection({
|
|
95
|
+
storyId: clientAPI.raw()[i].id,
|
|
96
|
+
});
|
|
97
|
+
await frameWindow.__STORYBOOK_PREVIEW__.renderSelection();
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
return {version, ...api};
|
package/dist/getStories.js
CHANGED
|
@@ -7,6 +7,7 @@ function __getStories(...args) {
|
|
|
7
7
|
v4: 'v4',
|
|
8
8
|
v5: 'v5',
|
|
9
9
|
v5_2: 'v5_2',
|
|
10
|
+
v6_4: 'v6_4',
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
function getClientAPI() {
|
|
@@ -19,7 +20,9 @@ function __getStories(...args) {
|
|
|
19
20
|
function getStorybookVersion() {
|
|
20
21
|
const addons = frameWindow.__STORYBOOK_ADDONS;
|
|
21
22
|
|
|
22
|
-
if (frameWindow.
|
|
23
|
+
if (frameWindow.__STORYBOOK_PREVIEW__) {
|
|
24
|
+
return API_VERSIONS.v6_4;
|
|
25
|
+
} else if (frameWindow.__STORYBOOK_STORY_STORE__) {
|
|
23
26
|
return API_VERSIONS.v5_2;
|
|
24
27
|
} else if (frameWindow.__STORYBOOK_CLIENT_API__ && frameWindow.__STORYBOOK_CLIENT_API__.raw) {
|
|
25
28
|
return API_VERSIONS.v5;
|
|
@@ -81,6 +84,21 @@ function __getStories(...args) {
|
|
|
81
84
|
};
|
|
82
85
|
break;
|
|
83
86
|
}
|
|
87
|
+
|
|
88
|
+
case API_VERSIONS.v6_4: {
|
|
89
|
+
api = {
|
|
90
|
+
getStories: () => {
|
|
91
|
+
return clientAPI.raw();
|
|
92
|
+
},
|
|
93
|
+
selectStory: async i => {
|
|
94
|
+
frameWindow.__STORYBOOK_PREVIEW__.urlStore.setSelection({
|
|
95
|
+
storyId: clientAPI.raw()[i].id,
|
|
96
|
+
});
|
|
97
|
+
await frameWindow.__STORYBOOK_PREVIEW__.renderSelection();
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
return {version, ...api};
|
|
@@ -7,6 +7,7 @@ function __renderStoryWithClientAPI(...args) {
|
|
|
7
7
|
v4: 'v4',
|
|
8
8
|
v5: 'v5',
|
|
9
9
|
v5_2: 'v5_2',
|
|
10
|
+
v6_4: 'v6_4',
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
function getClientAPI() {
|
|
@@ -19,7 +20,9 @@ function __renderStoryWithClientAPI(...args) {
|
|
|
19
20
|
function getStorybookVersion() {
|
|
20
21
|
const addons = frameWindow.__STORYBOOK_ADDONS;
|
|
21
22
|
|
|
22
|
-
if (frameWindow.
|
|
23
|
+
if (frameWindow.__STORYBOOK_PREVIEW__) {
|
|
24
|
+
return API_VERSIONS.v6_4;
|
|
25
|
+
} else if (frameWindow.__STORYBOOK_STORY_STORE__) {
|
|
23
26
|
return API_VERSIONS.v5_2;
|
|
24
27
|
} else if (frameWindow.__STORYBOOK_CLIENT_API__ && frameWindow.__STORYBOOK_CLIENT_API__.raw) {
|
|
25
28
|
return API_VERSIONS.v5;
|
|
@@ -81,6 +84,21 @@ function __renderStoryWithClientAPI(...args) {
|
|
|
81
84
|
};
|
|
82
85
|
break;
|
|
83
86
|
}
|
|
87
|
+
|
|
88
|
+
case API_VERSIONS.v6_4: {
|
|
89
|
+
api = {
|
|
90
|
+
getStories: () => {
|
|
91
|
+
return clientAPI.raw();
|
|
92
|
+
},
|
|
93
|
+
selectStory: async i => {
|
|
94
|
+
frameWindow.__STORYBOOK_PREVIEW__.urlStore.setSelection({
|
|
95
|
+
storyId: clientAPI.raw()[i].id,
|
|
96
|
+
});
|
|
97
|
+
await frameWindow.__STORYBOOK_PREVIEW__.renderSelection();
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
return {version, ...api};
|
|
@@ -112,11 +130,11 @@ function __renderStoryWithClientAPI(...args) {
|
|
|
112
130
|
|
|
113
131
|
var getClientAPI_1 = getClientAPI;
|
|
114
132
|
|
|
115
|
-
function renderStoryWithClientAPI(index) {
|
|
133
|
+
async function renderStoryWithClientAPI(index) {
|
|
116
134
|
let api;
|
|
117
135
|
try {
|
|
118
136
|
api = getClientAPI_1();
|
|
119
|
-
api.selectStory(index);
|
|
137
|
+
await api.selectStory(index);
|
|
120
138
|
} catch (ex) {
|
|
121
139
|
return {message: ex.message, version: api ? api.version : undefined};
|
|
122
140
|
}
|
|
@@ -7,6 +7,7 @@ function __runRunAfterScript(...args) {
|
|
|
7
7
|
v4: 'v4',
|
|
8
8
|
v5: 'v5',
|
|
9
9
|
v5_2: 'v5_2',
|
|
10
|
+
v6_4: 'v6_4',
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
function getClientAPI() {
|
|
@@ -19,7 +20,9 @@ function __runRunAfterScript(...args) {
|
|
|
19
20
|
function getStorybookVersion() {
|
|
20
21
|
const addons = frameWindow.__STORYBOOK_ADDONS;
|
|
21
22
|
|
|
22
|
-
if (frameWindow.
|
|
23
|
+
if (frameWindow.__STORYBOOK_PREVIEW__) {
|
|
24
|
+
return API_VERSIONS.v6_4;
|
|
25
|
+
} else if (frameWindow.__STORYBOOK_STORY_STORE__) {
|
|
23
26
|
return API_VERSIONS.v5_2;
|
|
24
27
|
} else if (frameWindow.__STORYBOOK_CLIENT_API__ && frameWindow.__STORYBOOK_CLIENT_API__.raw) {
|
|
25
28
|
return API_VERSIONS.v5;
|
|
@@ -81,6 +84,21 @@ function __runRunAfterScript(...args) {
|
|
|
81
84
|
};
|
|
82
85
|
break;
|
|
83
86
|
}
|
|
87
|
+
|
|
88
|
+
case API_VERSIONS.v6_4: {
|
|
89
|
+
api = {
|
|
90
|
+
getStories: () => {
|
|
91
|
+
return clientAPI.raw();
|
|
92
|
+
},
|
|
93
|
+
selectStory: async i => {
|
|
94
|
+
frameWindow.__STORYBOOK_PREVIEW__.urlStore.setSelection({
|
|
95
|
+
storyId: clientAPI.raw()[i].id,
|
|
96
|
+
});
|
|
97
|
+
await frameWindow.__STORYBOOK_PREVIEW__.renderSelection();
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
return {version, ...api};
|
|
@@ -7,6 +7,7 @@ function __runRunBeforeScript(...args) {
|
|
|
7
7
|
v4: 'v4',
|
|
8
8
|
v5: 'v5',
|
|
9
9
|
v5_2: 'v5_2',
|
|
10
|
+
v6_4: 'v6_4',
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
function getClientAPI() {
|
|
@@ -19,7 +20,9 @@ function __runRunBeforeScript(...args) {
|
|
|
19
20
|
function getStorybookVersion() {
|
|
20
21
|
const addons = frameWindow.__STORYBOOK_ADDONS;
|
|
21
22
|
|
|
22
|
-
if (frameWindow.
|
|
23
|
+
if (frameWindow.__STORYBOOK_PREVIEW__) {
|
|
24
|
+
return API_VERSIONS.v6_4;
|
|
25
|
+
} else if (frameWindow.__STORYBOOK_STORY_STORE__) {
|
|
23
26
|
return API_VERSIONS.v5_2;
|
|
24
27
|
} else if (frameWindow.__STORYBOOK_CLIENT_API__ && frameWindow.__STORYBOOK_CLIENT_API__.raw) {
|
|
25
28
|
return API_VERSIONS.v5;
|
|
@@ -81,6 +84,21 @@ function __runRunBeforeScript(...args) {
|
|
|
81
84
|
};
|
|
82
85
|
break;
|
|
83
86
|
}
|
|
87
|
+
|
|
88
|
+
case API_VERSIONS.v6_4: {
|
|
89
|
+
api = {
|
|
90
|
+
getStories: () => {
|
|
91
|
+
return clientAPI.raw();
|
|
92
|
+
},
|
|
93
|
+
selectStory: async i => {
|
|
94
|
+
frameWindow.__STORYBOOK_PREVIEW__.urlStore.setSelection({
|
|
95
|
+
storyId: clientAPI.raw()[i].id,
|
|
96
|
+
});
|
|
97
|
+
await frameWindow.__STORYBOOK_PREVIEW__.renderSelection();
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
return {version, ...api};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applitools/eyes-storybook",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.27.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=8.6.0"
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"changelog": "git changelog -x -p -f v$npm_package_version > History.md && git add ./History.md && git commit -am 'changelog'",
|
|
35
35
|
"changelog:init": "git config changelog.format \"* %s [[%h]($(echo $npm_package_repository_url|cut -d+ -f2|cut -d. -f1-2)/commit/%H)]\"",
|
|
36
36
|
"changelog:install": "sudo apt-get install git-extras",
|
|
37
|
+
"gh:publish": "gh workflow run publish-storybook.yml --ref $(git rev-parse --abbrev-ref HEAD)",
|
|
37
38
|
"postinstall": "node src/postinstall",
|
|
38
39
|
"preversion": "bongo preversion",
|
|
39
40
|
"version": "bongo version",
|
|
@@ -58,14 +59,14 @@
|
|
|
58
59
|
"directory": "packages/eyes-storybook"
|
|
59
60
|
},
|
|
60
61
|
"dependencies": {
|
|
61
|
-
"@applitools/driver": "1.
|
|
62
|
-
"@applitools/eyes-sdk-core": "
|
|
62
|
+
"@applitools/driver": "1.4.9",
|
|
63
|
+
"@applitools/eyes-sdk-core": "13.0.0",
|
|
63
64
|
"@applitools/functional-commons": "1.6.0",
|
|
64
|
-
"@applitools/logger": "1.0.
|
|
65
|
+
"@applitools/logger": "1.0.9",
|
|
65
66
|
"@applitools/monitoring-commons": "1.0.19",
|
|
66
67
|
"@applitools/spec-driver-puppeteer": "1.1.0",
|
|
67
|
-
"@applitools/test-server": "1.0.
|
|
68
|
-
"@applitools/visual-grid-client": "15.8.
|
|
68
|
+
"@applitools/test-server": "1.0.8",
|
|
69
|
+
"@applitools/visual-grid-client": "15.8.62",
|
|
69
70
|
"boxen": "4.2.0",
|
|
70
71
|
"chalk": "3.0.0",
|
|
71
72
|
"detect-port": "1.3.0",
|
|
@@ -77,13 +78,12 @@
|
|
|
77
78
|
"yargs": "15.4.1"
|
|
78
79
|
},
|
|
79
80
|
"devDependencies": {
|
|
80
|
-
"@applitools/scripts": "^1.0
|
|
81
|
-
"@applitools/sdk-release-kit": "0.13.
|
|
82
|
-
"@applitools/sdk-shared": "0.9.
|
|
81
|
+
"@applitools/scripts": "^1.1.0",
|
|
82
|
+
"@applitools/sdk-release-kit": "0.13.11",
|
|
83
|
+
"@applitools/sdk-shared": "0.9.11",
|
|
83
84
|
"@applitools/snaptdout": "^1.0.1",
|
|
84
|
-
"@applitools/utils": "1.2.
|
|
85
|
-
"@storybook/
|
|
86
|
-
"@storybook/react": "^5.2.8",
|
|
85
|
+
"@applitools/utils": "1.2.11",
|
|
86
|
+
"@storybook/react": "^6.4.0",
|
|
87
87
|
"@testing-library/dom": "^5.6.1",
|
|
88
88
|
"babel-core": "^6.26.3",
|
|
89
89
|
"babel-loader": "^8.0.6",
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function addVariationStories({stories, config}) {
|
|
4
|
+
const defaultStories = [];
|
|
5
|
+
const variationStories = {};
|
|
6
|
+
for (const story of stories) {
|
|
7
|
+
const variations = getStoryVariations(story, config);
|
|
8
|
+
if (variations) {
|
|
9
|
+
for (const variation of variations) {
|
|
10
|
+
const variationKey = stringifyVariation(variation);
|
|
11
|
+
const variationStoriesArray =
|
|
12
|
+
variationStories[variationKey] || (variationStories[variationKey] = []);
|
|
13
|
+
const variationStory = {...story};
|
|
14
|
+
if (variation.queryParams || variation.properties) {
|
|
15
|
+
variationStory.parameters = addVariation(story.parameters, variation);
|
|
16
|
+
}
|
|
17
|
+
variationStoriesArray.push(variationStory);
|
|
18
|
+
}
|
|
19
|
+
} else {
|
|
20
|
+
defaultStories.push(story);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return Object.values(variationStories).reduce((allStories, variationStories) => {
|
|
25
|
+
return allStories.concat(variationStories);
|
|
26
|
+
}, defaultStories);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getStoryVariations(story, config) {
|
|
30
|
+
if (story.parameters && story.parameters.eyes && story.parameters.eyes.variations) {
|
|
31
|
+
if (!Array.isArray(story.parameters.eyes.variations)) {
|
|
32
|
+
throw new Error('variations should be an array');
|
|
33
|
+
}
|
|
34
|
+
return normalizeVariations(story.parameters.eyes.variations);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (config.variations) {
|
|
38
|
+
if (Array.isArray(config.variations)) {
|
|
39
|
+
return normalizeVariations(config.variations);
|
|
40
|
+
} else if (typeof config.variations === 'function') {
|
|
41
|
+
const variations = config.variations(story);
|
|
42
|
+
if (variations) {
|
|
43
|
+
if (!Array.isArray(variations)) {
|
|
44
|
+
throw new Error('global variations should be a function that returns array');
|
|
45
|
+
}
|
|
46
|
+
return normalizeVariations(variations);
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
throw new Error('global variations should be an array or a function that returns array');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function normalizeVariations(variations) {
|
|
55
|
+
let shouldHasDefault = false; // default variation is one without query params
|
|
56
|
+
const normalizedVariations = variations.map(variation => {
|
|
57
|
+
if (typeof variation === 'string') {
|
|
58
|
+
shouldHasDefault = true;
|
|
59
|
+
return {
|
|
60
|
+
queryParams: {'eyes-variation': variation},
|
|
61
|
+
properties: [{name: 'eyes-variation', value: variation}],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (variation.queryParams && (!variation.properties || variation.properties.length === 0)) {
|
|
65
|
+
return {
|
|
66
|
+
...variation,
|
|
67
|
+
properties: Object.entries(variation.queryParams).map(([name, value]) => ({name, value})),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return variation;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// if it should has default variation and it is not already there
|
|
74
|
+
if (shouldHasDefault && !normalizedVariations.some(variation => !variation.queryParams)) {
|
|
75
|
+
normalizedVariations.unshift({});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return normalizedVariations;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function stringifyVariation(variation) {
|
|
82
|
+
if (!variation.queryParams) return '';
|
|
83
|
+
return Object.keys(variation.queryParams)
|
|
84
|
+
.sort()
|
|
85
|
+
.map(name => `${name}=${variation.queryParams[name]}`)
|
|
86
|
+
.join('&');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function addVariation(parameters, variation) {
|
|
90
|
+
parameters = {...parameters};
|
|
91
|
+
parameters.eyes = {...parameters.eyes};
|
|
92
|
+
if (variation.queryParams) {
|
|
93
|
+
parameters.eyes.queryParams = variation.queryParams;
|
|
94
|
+
}
|
|
95
|
+
if (variation.properties) {
|
|
96
|
+
parameters.eyes.properties = [
|
|
97
|
+
...(parameters.eyes.properties || []),
|
|
98
|
+
...(variation.properties || []),
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
return parameters;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = addVariationStories;
|
|
@@ -4,6 +4,7 @@ const API_VERSIONS = {
|
|
|
4
4
|
v4: 'v4',
|
|
5
5
|
v5: 'v5',
|
|
6
6
|
v5_2: 'v5_2',
|
|
7
|
+
v6_4: 'v6_4',
|
|
7
8
|
};
|
|
8
9
|
|
|
9
10
|
function getClientAPI() {
|
|
@@ -16,7 +17,9 @@ function getClientAPI() {
|
|
|
16
17
|
function getStorybookVersion() {
|
|
17
18
|
const addons = frameWindow.__STORYBOOK_ADDONS;
|
|
18
19
|
|
|
19
|
-
if (frameWindow.
|
|
20
|
+
if (frameWindow.__STORYBOOK_PREVIEW__) {
|
|
21
|
+
return API_VERSIONS.v6_4;
|
|
22
|
+
} else if (frameWindow.__STORYBOOK_STORY_STORE__) {
|
|
20
23
|
return API_VERSIONS.v5_2;
|
|
21
24
|
} else if (frameWindow.__STORYBOOK_CLIENT_API__ && frameWindow.__STORYBOOK_CLIENT_API__.raw) {
|
|
22
25
|
return API_VERSIONS.v5;
|
|
@@ -78,6 +81,21 @@ function getClientAPI() {
|
|
|
78
81
|
};
|
|
79
82
|
break;
|
|
80
83
|
}
|
|
84
|
+
|
|
85
|
+
case API_VERSIONS.v6_4: {
|
|
86
|
+
api = {
|
|
87
|
+
getStories: () => {
|
|
88
|
+
return clientAPI.raw();
|
|
89
|
+
},
|
|
90
|
+
selectStory: async i => {
|
|
91
|
+
frameWindow.__STORYBOOK_PREVIEW__.urlStore.setSelection({
|
|
92
|
+
storyId: clientAPI.raw()[i].id,
|
|
93
|
+
});
|
|
94
|
+
await frameWindow.__STORYBOOK_PREVIEW__.renderSelection();
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
81
99
|
}
|
|
82
100
|
|
|
83
101
|
return {version, ...api};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const getClientAPI = require('./getClientAPI');
|
|
2
2
|
|
|
3
|
-
function renderStoryWithClientAPI(index) {
|
|
3
|
+
async function renderStoryWithClientAPI(index) {
|
|
4
4
|
let api;
|
|
5
5
|
try {
|
|
6
6
|
api = getClientAPI();
|
|
7
|
-
api.selectStory(index);
|
|
7
|
+
await api.selectStory(index);
|
|
8
8
|
} catch (ex) {
|
|
9
9
|
return {message: ex.message, version: api ? api.version : undefined};
|
|
10
10
|
}
|
package/src/eyesStorybook.js
CHANGED
|
@@ -10,7 +10,7 @@ const makeRenderStories = require('./renderStories');
|
|
|
10
10
|
const makeGetStoryData = require('./getStoryData');
|
|
11
11
|
const ora = require('ora');
|
|
12
12
|
const filterStories = require('./filterStories');
|
|
13
|
-
const
|
|
13
|
+
const addVariationStories = require('./addVariationStories');
|
|
14
14
|
const browserLog = require('./browserLog');
|
|
15
15
|
const memoryLog = require('./memoryLog');
|
|
16
16
|
const getIframeUrl = require('./getIframeUrl');
|
|
@@ -116,12 +116,12 @@ async function eyesStorybook({
|
|
|
116
116
|
);
|
|
117
117
|
|
|
118
118
|
const filteredStories = filterStories({stories, config});
|
|
119
|
-
const
|
|
119
|
+
const storiesIncludingVariations = addVariationStories({
|
|
120
120
|
stories: filteredStories,
|
|
121
121
|
config,
|
|
122
122
|
});
|
|
123
123
|
|
|
124
|
-
logger.log(`starting to run ${
|
|
124
|
+
logger.log(`starting to run ${storiesIncludingVariations.length} stories`);
|
|
125
125
|
|
|
126
126
|
const getStoryData = makeGetStoryData({
|
|
127
127
|
logger,
|
|
@@ -158,7 +158,7 @@ async function eyesStorybook({
|
|
|
158
158
|
setRenderIE,
|
|
159
159
|
setTransitioningIntoIE,
|
|
160
160
|
configs,
|
|
161
|
-
stories:
|
|
161
|
+
stories: storiesIncludingVariations,
|
|
162
162
|
pagePool,
|
|
163
163
|
logger,
|
|
164
164
|
timeItAsync,
|
|
@@ -3,11 +3,17 @@ const getStoryTitle = require('./getStoryTitle');
|
|
|
3
3
|
|
|
4
4
|
function getStoryBaselineName({name, kind, parameters}) {
|
|
5
5
|
const storyTitle = getStoryTitle({name, kind, parameters});
|
|
6
|
-
const
|
|
6
|
+
const queryParams = parameters && parameters.eyes && parameters.eyes.queryParams;
|
|
7
|
+
if (!queryParams) return storyTitle;
|
|
8
|
+
const customQueryParams = {...queryParams};
|
|
9
|
+
delete customQueryParams['eyes-variation'];
|
|
10
|
+
if (Object.keys(customQueryParams).length === 0) return storyTitle;
|
|
7
11
|
|
|
8
|
-
|
|
12
|
+
const queryParamsString = Object.entries(customQueryParams)
|
|
13
|
+
.map(([name, value]) => `${name}=${value}`)
|
|
14
|
+
.join('&');
|
|
9
15
|
|
|
10
|
-
return `${storyTitle} [${
|
|
16
|
+
return `${storyTitle} [${queryParamsString}]`;
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
module.exports = getStoryBaselineName;
|
package/src/getStoryData.js
CHANGED
|
@@ -15,19 +15,9 @@ function makeGetStoryData({logger, takeDomSnapshots, waitBeforeCapture, reloadPa
|
|
|
15
15
|
|
|
16
16
|
const eyesParameters = story.parameters && story.parameters.eyes;
|
|
17
17
|
if (story.isApi && !reloadPagePerStory) {
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
? eyesParameters.queryParams.map(queryParam => queryParam.name)
|
|
22
|
-
: undefined,
|
|
23
|
-
);
|
|
24
|
-
if (
|
|
25
|
-
(!actualQueryParam && !expectedQueryParam) ||
|
|
26
|
-
(actualQueryParam &&
|
|
27
|
-
expectedQueryParam &&
|
|
28
|
-
actualQueryParam.value === expectedQueryParam.name &&
|
|
29
|
-
actualQueryParam.value === expectedQueryParam.value)
|
|
30
|
-
) {
|
|
18
|
+
const currentUrl = page.url();
|
|
19
|
+
const expectedQueryParams = eyesParameters ? eyesParameters.queryParams : undefined;
|
|
20
|
+
if (urlQueryParamsEquals(currentUrl, expectedQueryParams)) {
|
|
31
21
|
const err = await page.evaluate(renderStoryWithClientAPI, story.index);
|
|
32
22
|
err && handleRenderStoryError(err);
|
|
33
23
|
} else {
|
|
@@ -84,19 +74,29 @@ function makeGetStoryData({logger, takeDomSnapshots, waitBeforeCapture, reloadPa
|
|
|
84
74
|
}
|
|
85
75
|
}
|
|
86
76
|
|
|
87
|
-
|
|
77
|
+
function urlQueryParamsEquals(url, expectedQueryParams) {
|
|
88
78
|
try {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
79
|
+
url = new URL(url);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
logger.error('Error during parsing page url', err);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const currentQueryParams = url.searchParams.get('eyes-query-params');
|
|
86
|
+
if (!currentQueryParams && !expectedQueryParams) return true;
|
|
87
|
+
if (!currentQueryParams || !expectedQueryParams) return false;
|
|
88
|
+
|
|
89
|
+
const expectedQueryParamNames = Object.keys(expectedQueryParams);
|
|
90
|
+
const currentQueryParamNames = currentQueryParams.split(',');
|
|
91
|
+
|
|
92
|
+
if (currentQueryParamNames.length !== expectedQueryParamNames.length) return false;
|
|
93
|
+
|
|
94
|
+
for (const queryParamName of currentQueryParamNames) {
|
|
95
|
+
if (url.searchParams.get(queryParamName) !== expectedQueryParams[queryParamName]) {
|
|
96
|
+
return false;
|
|
96
97
|
}
|
|
97
|
-
} catch (ex) {
|
|
98
|
-
logger.log('failed to get url from page (in need of eyes-variation param)');
|
|
99
98
|
}
|
|
99
|
+
return true;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// TODO (amit): handle this error in the caller (probably renderStories)
|
package/src/getStoryTitle.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
function getStoryTitle({name, kind, parameters}) {
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
queryParam && queryParam.name === 'eyes-variation' ? queryParam.value : undefined;
|
|
4
|
+
const queryParams = (parameters && parameters.eyes && parameters.eyes.queryParams) || {};
|
|
5
|
+
const eyesVariation = queryParams['eyes-variation'];
|
|
7
6
|
|
|
8
|
-
return `${kind}: ${name}${
|
|
7
|
+
return `${kind}: ${name}${eyesVariation ? ` [${eyesVariation}]` : ''}`;
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
module.exports = getStoryTitle;
|
package/src/getStoryUrl.js
CHANGED
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
const getIframeUrl = require('./getIframeUrl');
|
|
3
3
|
|
|
4
4
|
function getStoryUrl({name, kind, parameters}, baseUrl) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
let storyUrl = getIframeUrl(baseUrl);
|
|
6
|
+
storyUrl += `&selectedKind=${encodeURIComponent(kind)}`;
|
|
7
|
+
storyUrl += `&selectedStory=${encodeURIComponent(name)}`;
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const queryParams = parameters && parameters.eyes && parameters.eyes.queryParams;
|
|
10
|
+
if (queryParams) {
|
|
11
|
+
storyUrl += `&eyes-query-params=${Object.keys(queryParams).join(',')}`;
|
|
12
|
+
for (const [name, value] of Object.entries(queryParams)) {
|
|
13
|
+
storyUrl += `&${name}=${encodeURIComponent(value)}`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return storyUrl;
|
|
11
18
|
}
|
|
12
19
|
|
|
13
20
|
module.exports = getStoryUrl;
|
package/src/renderStory.js
CHANGED
|
@@ -8,7 +8,12 @@ function makeRenderStory({logger, testWindow, performance, timeItAsync}) {
|
|
|
8
8
|
const {name, kind, parameters} = story;
|
|
9
9
|
const baselineName = getStoryBaselineName({name, kind, parameters});
|
|
10
10
|
const title = getStoryTitle({name, kind, parameters});
|
|
11
|
-
const
|
|
11
|
+
const eyesParameters = (parameters && parameters.eyes) || {};
|
|
12
|
+
const eyesOptions = {
|
|
13
|
+
...config,
|
|
14
|
+
...eyesParameters,
|
|
15
|
+
properties: [...(config.properties || []), ...(eyesParameters.properties || [])],
|
|
16
|
+
};
|
|
12
17
|
const {
|
|
13
18
|
ignoreDisplacements,
|
|
14
19
|
ignoreRegions,
|
|
@@ -31,7 +36,6 @@ function makeRenderStory({logger, testWindow, performance, timeItAsync}) {
|
|
|
31
36
|
visualGridOptions,
|
|
32
37
|
useDom,
|
|
33
38
|
enablePatterns,
|
|
34
|
-
queryParam,
|
|
35
39
|
} = eyesOptions;
|
|
36
40
|
|
|
37
41
|
if (sizeMode) {
|
|
@@ -52,10 +56,6 @@ function makeRenderStory({logger, testWindow, performance, timeItAsync}) {
|
|
|
52
56
|
...(properties || []),
|
|
53
57
|
];
|
|
54
58
|
|
|
55
|
-
if (queryParam) {
|
|
56
|
-
storyProperties.push(queryParam);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
59
|
const openParams = {
|
|
60
60
|
testName: baselineName,
|
|
61
61
|
displayName: title,
|
|
@@ -72,7 +72,7 @@ class StorybookConnector extends EventEmitter {
|
|
|
72
72
|
|
|
73
73
|
const successMessageListener = str => {
|
|
74
74
|
const isReady = stripAnsi(str).match(
|
|
75
|
-
/Storybook \d{1,2}\.\d{1,2}\.\d{1,2}(
|
|
75
|
+
/Storybook \d{1,2}\.\d{1,2}\.\d{1,2}(.+)? started|Storybook started on =>/,
|
|
76
76
|
);
|
|
77
77
|
|
|
78
78
|
if (isReady) {
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
function addParametrizedStories({stories, config}) {
|
|
4
|
-
const parametrizedStories = {};
|
|
5
|
-
for (const story of stories) {
|
|
6
|
-
const queryParams = getStoryQueryParams(story, config);
|
|
7
|
-
if (!queryParams) continue;
|
|
8
|
-
for (const queryParam of queryParams) {
|
|
9
|
-
const queryParamString = `${queryParam.name}=${queryParam.value}`;
|
|
10
|
-
const parametrizeStoriesArray =
|
|
11
|
-
parametrizedStories[queryParamString] || (parametrizedStories[queryParamString] = []);
|
|
12
|
-
parametrizeStoriesArray.push({
|
|
13
|
-
...story,
|
|
14
|
-
parameters: addQueryParam(story.parameters, queryParam),
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return Object.values(parametrizedStories).reduce((allStories, parametrizedStories) => {
|
|
20
|
-
return allStories.concat(parametrizedStories);
|
|
21
|
-
}, stories);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function getStoryQueryParams(story, config) {
|
|
25
|
-
let queryParams;
|
|
26
|
-
if (story.parameters && story.parameters.eyes && story.parameters.eyes.queryParams) {
|
|
27
|
-
if (!Array.isArray(story.parameters.eyes.queryParams)) {
|
|
28
|
-
throw new Error('queryParams should be an array');
|
|
29
|
-
}
|
|
30
|
-
queryParams = [...story.parameters.eyes.queryParams];
|
|
31
|
-
}
|
|
32
|
-
if (story.parameters && story.parameters.eyes && story.parameters.eyes.variations) {
|
|
33
|
-
if (!Array.isArray(story.parameters.eyes.variations)) {
|
|
34
|
-
throw new Error('variations should be an array');
|
|
35
|
-
}
|
|
36
|
-
queryParams = [
|
|
37
|
-
...(queryParams || []),
|
|
38
|
-
...variationsToQueryParams(story.parameters.eyes.variations),
|
|
39
|
-
];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (queryParams) return queryParams;
|
|
43
|
-
|
|
44
|
-
if (config.queryParams) {
|
|
45
|
-
if (!Array.isArray(config.queryParams)) {
|
|
46
|
-
throw new Error('global queryParams should be an array');
|
|
47
|
-
}
|
|
48
|
-
queryParams = [...config.queryParams];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (config.variations) {
|
|
52
|
-
if (Array.isArray(config.variations)) {
|
|
53
|
-
queryParams = [...(queryParams || []), ...variationsToQueryParams(config.variations)];
|
|
54
|
-
} else if (typeof config.variations === 'function') {
|
|
55
|
-
const variations = config.variations(story);
|
|
56
|
-
if (variations) {
|
|
57
|
-
if (!Array.isArray(variations)) {
|
|
58
|
-
throw new Error('global variations should be a function that returns array');
|
|
59
|
-
}
|
|
60
|
-
queryParams = [...(queryParams || []), ...variationsToQueryParams(variations)];
|
|
61
|
-
}
|
|
62
|
-
} else {
|
|
63
|
-
throw new Error('global variations should be an array or a function that returns array');
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (queryParams) return queryParams;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function variationsToQueryParams(variations) {
|
|
71
|
-
return variations.map(variation => ({name: 'eyes-variation', value: variation}));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function addQueryParam(parameters, queryParam) {
|
|
75
|
-
parameters = {...parameters};
|
|
76
|
-
parameters.eyes = {...parameters.eyes};
|
|
77
|
-
parameters.eyes.queryParam = queryParam;
|
|
78
|
-
return parameters;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
module.exports = addParametrizedStories;
|