@applitools/eyes-storybook 3.25.0 → 3.26.0

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 CHANGED
@@ -3,6 +3,36 @@
3
3
  ## Unreleased
4
4
 
5
5
 
6
+ ## 3.26.0 - 2021/11/26
7
+
8
+ - add support for `queryParams` config property
9
+ - updated to @applitools/driver@1.3.5 (from 1.2.7)
10
+ - updated to @applitools/eyes-puppeteer@1.9.0 (from 1.8.5)
11
+ - updated to @applitools/eyes-sdk-core@12.24.5 (from 12.23.24)
12
+ - updated to @applitools/logger@1.0.6 (from 1.0.5)
13
+ - updated to @applitools/test-server@1.0.7 (from 1.0.6)
14
+ - updated to @applitools/visual-grid-client@15.8.49 (from 15.8.43)
15
+
16
+ ## 3.25.3 - 2021/11/1
17
+
18
+ - add a retry mechanism for cases we get 0 stories
19
+
20
+ ## 3.25.2 - 2021/10/30
21
+
22
+ - replace legacy logger construction with new
23
+ - deprecate `waitBeforeScreenshot` and introduce `waitBeforeCapture` instead
24
+ - updated to @applitools/driver@1.2.7 (from 1.2.6)
25
+ - updated to @applitools/eyes-puppeteer@1.8.5 (from 1.8.4)
26
+ - updated to @applitools/eyes-sdk-core@12.23.24 (from 12.23.18)
27
+ - updated to @applitools/test-server@1.0.6 (from 1.0.5)
28
+ - updated to @applitools/visual-grid-client@15.8.43 (from 15.8.37)
29
+
30
+ ## 3.25.1 - 2021/10/20
31
+
32
+ - updated to @applitools/eyes-puppeteer@1.8.4 (from 1.8.3)
33
+ - updated to @applitools/eyes-sdk-core@12.23.18 (from 12.23.17)
34
+ - updated to @applitools/visual-grid-client@15.8.37 (from 15.8.36)
35
+
6
36
  ## 3.25.0 - 2021/10/18
7
37
 
8
38
  - updated to @applitools/driver@1.2.6 (from 1.1.3)
package/README.md CHANGED
@@ -4,43 +4,52 @@ Applitools Eyes SDK for [Storybook](http://storybook.js.org).
4
4
 
5
5
  ### Table of contents
6
6
 
7
- - [Installation](#installation)
8
- * [Install npm package](#install-npm-package)
9
- * [Applitools API key](#applitools-api-key)
10
- - [Usage](#usage)
11
- * [Configuring local storybook server](#configuring-local-storybook-server)
12
- * [Standalone server](#standalone-server)
13
- * [Command line arguments](#command-line-arguments)
14
- - [Concurrency](#concurrency)
15
- - [Advanced configuration](#advanced-configuration)
16
- * [Method 1: Environment variables](#method-1--environment-variables)
17
- * [Method 2: The `applitools.config.js` file](#method-2--the--applitoolsconfigjs--file)
18
- - [Configuring the browser](#configuring-the-browser)
19
- * [Previous browser versions](#previous-browser-versions)
20
- * [Getting a screenshot of multiple browsers in parallel](#getting-a-screenshot-of-multiple-browsers-in-parallel)
21
- * [Device emulation](#device-emulation)
22
- * [Faking IE Browser](#faking-ie-browser)
23
- - [Per component configuration](#per-component-configuration)
24
- * [`include`](#include)
25
- * [`variations`](#variations)
26
- * [`waitBeforeScreenshot`](#waitbeforescreenshot)
27
- * [`properties`](#properties)
28
- * [`ignoreRegions`](#ignoreregions)
29
- * [`floatingRegions`](#floatingregions)
30
- * [`layoutRegions`](#layoutregions)
31
- * [`contentRegions`](#contentregions)
32
- * [`strictRegions`](#strictregions)
33
- * [`accessibilityRegions`](#accessibilityregions)
34
- * [`accessibilityValidation`](#accessibilityvalidation)
35
- * [Parameters that cannot be set as an Advanced configuration](#parameters-that-cannot-be-set-as-an-advanced-configuration)
36
- * [`runBefore`](#runbefore)
37
- * [`runAfter`](#runafter)
38
- * [`scriptHooks`](#scripthooks)
39
- + [beforeCaptureScreenshot](#beforecapturescreenshot)
40
- * [`layoutBreakpoints`](#layoutBreakpoints)
41
- - [Running Eyes-Storybook in Docker](#running-eyes-storybook-in-docker)
42
- - [Dealing with dynamic data](#dealing-with-dynamic-data)
43
- - [Troubleshooting](#troubleshooting)
7
+ - [Eyes-Storybook](#eyes-storybook)
8
+ - [Table of contents](#table-of-contents)
9
+ - [Installation](#installation)
10
+ - [Install npm package](#install-npm-package)
11
+ - [Applitools API key](#applitools-api-key)
12
+ - [Usage](#usage)
13
+ - [Configuring local storybook server](#configuring-local-storybook-server)
14
+ - [Standalone server](#standalone-server)
15
+ - [Command line arguments](#command-line-arguments)
16
+ - [Concurrency](#concurrency)
17
+ - [Advanced configuration](#advanced-configuration)
18
+ - [Method 1: Environment variables](#method-1-environment-variables)
19
+ - [Method 2: The `applitools.config.js` file](#method-2-the-applitoolsconfigjs-file)
20
+ - [Configuring the browser](#configuring-the-browser)
21
+ - [Previous browser versions](#previous-browser-versions)
22
+ - [Getting a screenshot of multiple browsers in parallel](#getting-a-screenshot-of-multiple-browsers-in-parallel)
23
+ - [Device emulation](#device-emulation)
24
+ - [iOS device](#ios-device)
25
+ - [Faking IE Browser](#faking-ie-browser)
26
+ - [Per component configuration](#per-component-configuration)
27
+ - [`include`](#include)
28
+ - [global](#global)
29
+ - [component](#component)
30
+ - [`variations`](#variations)
31
+ - [`queryParams`](#queryparams)
32
+ - [`waitBeforeCapture`](#waitbeforecapture)
33
+ - [`properties`](#properties)
34
+ - [`ignoreRegions`](#ignoreregions)
35
+ - [`floatingRegions`](#floatingregions)
36
+ - [`layoutRegions`](#layoutregions)
37
+ - [`contentRegions`](#contentregions)
38
+ - [`strictRegions`](#strictregions)
39
+ - [`accessibilityRegions`](#accessibilityregions)
40
+ - [`accessibilityValidation`](#accessibilityvalidation)
41
+ - [`ignoreDisplacements`](#ignoredisplacements)
42
+ - [`sendDom`](#senddom)
43
+ - [`visualGridOptions`](#visualgridoptions)
44
+ - [`scriptHooks`](#scripthooks)
45
+ - [beforeCaptureScreenshot](#beforecapturescreenshot)
46
+ - [Parameters that cannot be set as an advanced configuration](#parameters-that-cannot-be-set-as-an-advanced-configuration)
47
+ - [`runBefore` and `runAfter` functions](#runbefore-and-runafter-functions)
48
+ - [`runBefore`](#runbefore)
49
+ - [`runAfter`](#runafter)
50
+ - [`layoutBreakpoints`](#layoutbreakpoints)
51
+ - [Running Eyes-Storybook in Docker](#running-eyes-storybook-in-docker)
52
+ - [Dealing with dynamic data](#dealing-with-dynamic-data)
44
53
 
45
54
  ## Installation
46
55
 
@@ -169,9 +178,10 @@ In addition to command-line arguments, it's possible to define the following con
169
178
  | `jsonFilePath` | undefined | Directory path of a results file. If set, then a [JSON](https://www.json.org/json-en.html) file is created in this directory, the file is created with the name eyes.json and contains the Eyes test results. |
170
179
  | `tapFilePath` | undefined | Directory path of a results file. If set, then a [TAP](https://en.wikipedia.org/wiki/Test_Anything_Protocol#Specification) file is created in this directory, the file is created with the name eyes.tap and contains the Eyes test results. |
171
180
  | `xmlFilePath` | undefined | Directory path of a results file. If set, then a [XUnit XML](https://google.github.io/rich-test-results/xunitxml) file is created in this directory, the file is created with the name eyes.xml and contains the Eyes test results. |
172
- | `waitBeforeScreenshot` | 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 [waitBeforeScreenshot.](#waitBeforeScreenshot)<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. |
181
+ | `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. |
173
182
  | `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). |
174
183
  | `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).|
175
185
  | `notifyOnCompletion` | false | If `true` batch completion notifications are sent. |
176
186
  | `dontCloseBatches` | false | If true, batches are not closed for notifyOnCompletion.|
177
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. |
@@ -439,20 +449,27 @@ storiesOf('Components that support RTL', module)
439
449
  )
440
450
  ```
441
451
 
442
- ### `waitBeforeScreenshot`
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
+ ### `waitBeforeCapture`
443
460
 
444
461
  Selector or timeout, see [advanced configuration](#advanced-configuration) for more details.
445
462
 
446
463
  ```js
447
- storiesOf('Components with a waitBeforeScreenshot', module)
464
+ storiesOf('Components with a waitBeforeCapture', module)
448
465
  .add(
449
466
  'Some story',
450
467
  () => <span id="container" class="loading"></span>,
451
- {eyes: { waitBeforeScreenshot: '#container.ready' }}
468
+ {eyes: { waitBeforeCapture: '#container.ready' }}
452
469
  );
453
470
  ```
454
471
 
455
- _Note that the predicate option for `waitBeforeScreenshot` is currently not available in the per component configuration._
472
+ _Note that the predicate option for `waitBeforeCapture` is currently not available in the per component configuration._
456
473
 
457
474
  ### `properties`
458
475
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applitools/eyes-storybook",
3
- "version": "3.25.0",
3
+ "version": "3.26.0",
4
4
  "description": "",
5
5
  "engines": {
6
6
  "node": ">=8.6.0"
@@ -25,10 +25,12 @@
25
25
  "storybook": "start-storybook -c test/fixtures/appWithStorybook -p 9001 -s test/fixtures",
26
26
  "storybook:jslayout": "start-storybook -c test/fixtures/jsLayoutStorybookLocal -p 9001 -s test/fixtures",
27
27
  "storybook:heavy": "start-storybook -c test/fixtures/heavyStorybook -p 9002 -s test/fixtures",
28
+ "storybook:zeroStories": "start-storybook -c test/fixtures/zeroStoriesRetry -p 9003 -s test/fixtures",
28
29
  "eyes-storybook": "node bin/eyes-storybook.js -f test/fixtures/applitools.config.js",
29
30
  "eyes-storybook:jslayout": "node bin/eyes-storybook.js -f test/e2e/happy-config/layout-breakpoints-local.config.js",
30
31
  "eyes-storybook:heavy": "node bin/eyes-storybook.js -f test/fixtures/heavyStorybook/applitools.config.js",
31
32
  "eyes-storybook:configured": "node bin/eyes-storybook.js -f scripts/preconfigured.config.js",
33
+ "eyes-storybook:zeroStories": "node bin/eyes-storybook.js -f test/fixtures/zeroStoriesRetry/applitools.config.js",
32
34
  "changelog": "git changelog -x -p -f v$npm_package_version > History.md && git add ./History.md && git commit -am 'changelog'",
33
35
  "changelog:init": "git config changelog.format \"* %s [[%h]($(echo $npm_package_repository_url|cut -d+ -f2|cut -d. -f1-2)/commit/%H)]\"",
34
36
  "changelog:install": "sudo apt-get install git-extras",
@@ -52,16 +54,18 @@
52
54
  "license": "SEE LICENSE IN LICENSE",
53
55
  "repository": {
54
56
  "type": "git",
55
- "url": "git://github.com/applitools/eyes.sdk.javascript1.git"
57
+ "url": "git://github.com/applitools/eyes.sdk.javascript1.git",
58
+ "directory": "packages/eyes-storybook"
56
59
  },
57
60
  "dependencies": {
58
- "@applitools/driver": "1.2.6",
59
- "@applitools/eyes-puppeteer": "1.8.3",
60
- "@applitools/eyes-sdk-core": "12.23.17",
61
+ "@applitools/driver": "1.3.5",
62
+ "@applitools/eyes-sdk-core": "12.24.5",
61
63
  "@applitools/functional-commons": "1.6.0",
64
+ "@applitools/logger": "1.0.6",
62
65
  "@applitools/monitoring-commons": "1.0.19",
63
- "@applitools/test-server": "1.0.5",
64
- "@applitools/visual-grid-client": "15.8.36",
66
+ "@applitools/spec-driver-puppeteer": "1.1.0",
67
+ "@applitools/test-server": "1.0.7",
68
+ "@applitools/visual-grid-client": "15.8.49",
65
69
  "boxen": "4.2.0",
66
70
  "chalk": "3.0.0",
67
71
  "detect-port": "1.3.0",
@@ -74,10 +78,10 @@
74
78
  },
75
79
  "devDependencies": {
76
80
  "@applitools/scripts": "^1.0.1",
77
- "@applitools/sdk-release-kit": "0.13.3",
78
- "@applitools/sdk-shared": "0.9.6",
81
+ "@applitools/sdk-release-kit": "0.13.4",
82
+ "@applitools/sdk-shared": "0.9.9",
79
83
  "@applitools/snaptdout": "^1.0.1",
80
- "@applitools/utils": "1.2.3",
84
+ "@applitools/utils": "1.2.4",
81
85
  "@storybook/addon-options": "^5.2.8",
82
86
  "@storybook/react": "^5.2.8",
83
87
  "@testing-library/dom": "^5.6.1",
@@ -0,0 +1,81 @@
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;
package/src/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  const yargs = require('yargs');
3
- const {Logger} = require('@applitools/eyes-sdk-core');
3
+ const {makeLogger} = require('@applitools/logger');
4
4
  const {configParams: externalConfigParams} = require('@applitools/visual-grid-client');
5
5
  const VERSION = require('../package.json').version;
6
6
  const eyesStorybook = require('./eyesStorybook');
@@ -32,8 +32,7 @@ const {performance, timeItAsync} = makeTiming();
32
32
  console.log(`Using @applitools/eyes-storybook version ${VERSION}.\n`);
33
33
 
34
34
  const config = generateConfig({argv, defaultConfig, externalConfigParams});
35
- const logger = new Logger(config.showLogs, 'eyes');
36
- logger.setIncludeTime(true);
35
+ const logger = makeLogger({level: config.showLogs ? 'info' : 'silent', label: 'eyes'});
37
36
  await validateAndPopulateConfig({config, logger, packagePath: process.cwd()});
38
37
  logger.log(`Running with the following config:\n${configDigest(config)}`);
39
38
  const [err, results] = await presult(
@@ -7,8 +7,9 @@ module.exports = {
7
7
  storybookUrl: undefined,
8
8
  storybookStaticDir: undefined,
9
9
  showStorybookOutput: false,
10
- waitBeforeScreenshot: 50,
10
+ waitBeforeScreenshot: 50, // backward compatibility
11
11
  waitBeforeScreenshots: 50, // backward compatibility
12
+ waitBeforeCapture: 50,
12
13
  viewportSize: {width: 1024, height: 768},
13
14
  tapFilePath: undefined,
14
15
  xmlFilePath: undefined,
@@ -2,7 +2,7 @@
2
2
  const puppeteer = require('puppeteer');
3
3
  const getStories = require('../dist/getStories');
4
4
  const {makeVisualGridClient} = require('@applitools/visual-grid-client');
5
- const {presult} = require('@applitools/functional-commons');
5
+ const {presult, delay} = require('@applitools/functional-commons');
6
6
  const chalk = require('./chalkify');
7
7
  const makeInitPage = require('./initPage');
8
8
  const makeRenderStory = require('./renderStory');
@@ -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 addVariationStories = require('./addVariationStories');
13
+ const addParametrizedStories = require('./addParametrizedStories');
14
14
  const browserLog = require('./browserLog');
15
15
  const memoryLog = require('./memoryLog');
16
16
  const getIframeUrl = require('./getIframeUrl');
@@ -18,12 +18,14 @@ const createPagePool = require('./pagePool');
18
18
  const getClientAPI = require('../dist/getClientAPI');
19
19
  const {takeDomSnapshots} = require('@applitools/eyes-sdk-core');
20
20
  const {Driver} = require('@applitools/driver');
21
- const spec = require('@applitools/eyes-puppeteer/dist/spec-driver');
21
+ const spec = require('@applitools/spec-driver-puppeteer');
22
22
  const {refineErrorMessage} = require('./errMessages');
23
23
  const {splitConfigsByBrowser} = require('./shouldRenderIE');
24
24
  const executeRenders = require('./executeRenders');
25
25
 
26
26
  const CONCURRENT_PAGES = 3;
27
+ const MAX_RETRIES = 10;
28
+ const RETRY_INTERVAL = 1000;
27
29
 
28
30
  async function eyesStorybook({
29
31
  config,
@@ -37,7 +39,7 @@ async function eyesStorybook({
37
39
  let transitioning = false;
38
40
  takeMemLoop();
39
41
  logger.log('eyesStorybook started');
40
- const {storybookUrl, waitBeforeScreenshot, readStoriesTimeout, reloadPagePerStory} = config;
42
+ const {storybookUrl, waitBeforeCapture, readStoriesTimeout, reloadPagePerStory} = config;
41
43
 
42
44
  let iframeUrl;
43
45
  try {
@@ -76,20 +78,21 @@ async function eyesStorybook({
76
78
  });
77
79
  const pagePool = createPagePool({initPage, logger});
78
80
 
79
- const doTakeDomSnapshots = async ({page, layoutBreakpoints}) => {
80
- const driver = new Driver({spec, driver: page, logger: logger._getNewLogger()});
81
+ const doTakeDomSnapshots = async ({page, browser, layoutBreakpoints, waitBeforeCapture}) => {
82
+ const driver = await new Driver({spec, driver: page, logger}).init();
81
83
  const skipResources = getResourceUrlsInCache();
82
84
  const result = await takeDomSnapshots({
83
85
  logger,
84
86
  driver,
85
87
  breakpoints: layoutBreakpoints !== undefined ? layoutBreakpoints : config.layoutBreakpoints,
86
- browsers: config.browser || [true], // this is a hack, since takeDomSnapshots expects an array. And VGC has a default in case browser is not specified. So we just need an array with length of 1 here.
88
+ browsers: browser || [true], // this is a hack, since takeDomSnapshots expects an array. And VGC has a default in case browser is not specified. So we just need an array with length of 1 here.
87
89
  skipResources,
88
90
  showLogs: !!config.showLogs,
89
91
  disableBrowserFetching: !!config.disableBrowserFetching,
90
92
  getViewportSize: () => config.viewportSize,
91
93
  getIosDevicesSizes,
92
94
  getEmulatedDevicesSizes,
95
+ waitBeforeCapture,
93
96
  });
94
97
  return result;
95
98
  };
@@ -113,14 +116,17 @@ async function eyesStorybook({
113
116
  );
114
117
 
115
118
  const filteredStories = filterStories({stories, config});
116
- const storiesIncludingVariations = addVariationStories({stories: filteredStories, config});
119
+ const storiesIncludingParametrizedVariations = addParametrizedStories({
120
+ stories: filteredStories,
121
+ config,
122
+ });
117
123
 
118
- logger.log(`starting to run ${storiesIncludingVariations.length} stories`);
124
+ logger.log(`starting to run ${storiesIncludingParametrizedVariations.length} stories`);
119
125
 
120
126
  const getStoryData = makeGetStoryData({
121
127
  logger,
122
128
  takeDomSnapshots: doTakeDomSnapshots,
123
- waitBeforeScreenshot,
129
+ waitBeforeCapture,
124
130
  });
125
131
 
126
132
  const renderStory = makeRenderStory({
@@ -152,7 +158,7 @@ async function eyesStorybook({
152
158
  setRenderIE,
153
159
  setTransitioningIntoIE,
154
160
  configs,
155
- stories: storiesIncludingVariations,
161
+ stories: storiesIncludingParametrizedVariations,
156
162
  pagePool,
157
163
  logger,
158
164
  timeItAsync,
@@ -202,9 +208,9 @@ async function eyesStorybook({
202
208
  spinner.fail(failMsg);
203
209
  throw new Error();
204
210
  }
205
- const [getStoriesErr, stories] = await presult(
206
- page.evaluate(getStories, {timeout: readStoriesTimeout}),
207
- );
211
+
212
+ const [getStoriesErr, stories] = await readStoriesWithRetry(MAX_RETRIES);
213
+
208
214
  if (getStoriesErr) {
209
215
  logger.log('Error in getStories:', getStoriesErr);
210
216
  const failMsg = refineErrorMessage({
@@ -256,6 +262,19 @@ async function eyesStorybook({
256
262
  function getTransitiongIntoIE() {
257
263
  return transitioning;
258
264
  }
265
+
266
+ async function readStoriesWithRetry(remainingRetries) {
267
+ const [getStoriesErr, stories] = await presult(
268
+ page.evaluate(getStories, {timeout: readStoriesTimeout}),
269
+ );
270
+ if (getStoriesErr || stories.length > 0 || remainingRetries == 0) {
271
+ return [getStoriesErr, stories];
272
+ } else {
273
+ logger.log(`Got 0 stories, retrying to read stories... ${remainingRetries - 1} are left`);
274
+ await delay(RETRY_INTERVAL);
275
+ return await readStoriesWithRetry(remainingRetries - 1);
276
+ }
277
+ }
259
278
  }
260
279
 
261
280
  module.exports = eyesStorybook;
@@ -14,24 +14,29 @@ function generateConfig({argv = {}, defaultConfig = {}, externalConfigParams = [
14
14
  const result = Object.assign({}, defaultConfig, config, argvConfig);
15
15
 
16
16
  // backward compatibility
17
- if (
18
- result.waitBeforeScreenshots !== defaultConfig.waitBeforeScreenshots &&
19
- result.waitBeforeScreenshot === defaultConfig.waitBeforeScreenshot
20
- ) {
21
- console.log(
22
- deprecationWarning({
23
- deprecatedThing: "'waitBeforeScreenshots'",
24
- newThing: "'waitBeforeScreenshot' (no 's')",
25
- }),
26
- );
27
- result.waitBeforeScreenshot = result.waitBeforeScreenshots;
17
+ if (result.waitBeforeCapture === defaultConfig.waitBeforeCapture) {
18
+ if (result.waitBeforeScreenshots !== defaultConfig.waitBeforeScreenshots) {
19
+ console.log(
20
+ deprecationWarning({
21
+ deprecatedThing: "'waitBeforeScreenshots'",
22
+ newThing: "'waitBeforeCapture'",
23
+ }),
24
+ );
25
+ result.waitBeforeCapture = result.waitBeforeScreenshots;
26
+ }
27
+ if (result.waitBeforeScreenshot !== defaultConfig.waitBeforeScreenshot) {
28
+ console.log(
29
+ deprecationWarning({
30
+ deprecatedThing: "'waitBeforeScreenshot'",
31
+ newThing: "'waitBeforeCapture'",
32
+ }),
33
+ );
34
+ result.waitBeforeCapture = result.waitBeforeScreenshot;
35
+ }
28
36
  }
29
37
 
30
- if (
31
- typeof result.waitBeforeScreenshot === 'string' &&
32
- !isNaN(parseInt(result.waitBeforeScreenshot))
33
- ) {
34
- result.waitBeforeScreenshot = Number(result.waitBeforeScreenshot);
38
+ if (typeof result.waitBeforeCapture === 'string' && !isNaN(parseInt(result.waitBeforeCapture))) {
39
+ result.waitBeforeCapture = Number(result.waitBeforeCapture);
35
40
  }
36
41
 
37
42
  if (result.showLogs === '1') {
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+ const getStoryTitle = require('./getStoryTitle');
3
+
4
+ function getStoryBaselineName({name, kind, parameters}) {
5
+ const storyTitle = getStoryTitle({name, kind, parameters});
6
+ const queryParam = parameters && parameters.eyes && parameters.eyes.queryParam;
7
+
8
+ if (!queryParam || queryParam.name === 'eyes-variation') return storyTitle;
9
+
10
+ return `${storyTitle} [${queryParam.name}=${queryParam.value}]`;
11
+ }
12
+
13
+ module.exports = getStoryBaselineName;
@@ -3,25 +3,30 @@ const {presult} = require('@applitools/functional-commons');
3
3
  const {ArgumentGuard} = require('@applitools/eyes-sdk-core');
4
4
  const renderStoryWithClientAPI = require('../dist/renderStoryWithClientAPI');
5
5
  const runRunBeforeScript = require('../dist/runRunBeforeScript');
6
- const getStoryTitle = require('./getStoryTitle');
6
+ const getStoryBaselineName = require('./getStoryBaselineName');
7
7
  const {URL} = require('url');
8
8
  const runRunAfterScript = require('../dist/runRunAfterScript');
9
9
  const waitFor = require('./waitFor');
10
10
 
11
- function makeGetStoryData({logger, takeDomSnapshots, waitBeforeScreenshot, reloadPagePerStory}) {
12
- return async function getStoryData({story, storyUrl, page, waitBeforeStory}) {
13
- const title = getStoryTitle(story);
11
+ function makeGetStoryData({logger, takeDomSnapshots, waitBeforeCapture, reloadPagePerStory}) {
12
+ return async function getStoryData({story, storyUrl, page, browser, waitBeforeStory}) {
13
+ const title = getStoryBaselineName(story);
14
14
  logger.log(`getting data from story`, title);
15
15
 
16
16
  const eyesParameters = story.parameters && story.parameters.eyes;
17
17
  if (story.isApi && !reloadPagePerStory) {
18
- const actualVariationParam = await getEyesVariationParam(page);
19
- const expectedVariationUrlParam = eyesParameters
20
- ? eyesParameters.variationUrlParam
21
- : undefined;
18
+ const expectedQueryParam = eyesParameters ? eyesParameters.queryParam : undefined;
19
+ const actualQueryParam = await getQueryParam(
20
+ eyesParameters && eyesParameters.queryParams
21
+ ? eyesParameters.queryParams.map(queryParam => queryParam.name)
22
+ : undefined,
23
+ );
22
24
  if (
23
- (!actualVariationParam && !expectedVariationUrlParam) ||
24
- actualVariationParam === expectedVariationUrlParam
25
+ (!actualQueryParam && !expectedQueryParam) ||
26
+ (actualQueryParam &&
27
+ expectedQueryParam &&
28
+ actualQueryParam.value === expectedQueryParam.name &&
29
+ actualQueryParam.value === expectedQueryParam.value)
25
30
  ) {
26
31
  const err = await page.evaluate(renderStoryWithClientAPI, story.index);
27
32
  err && handleRenderStoryError(err);
@@ -32,9 +37,9 @@ function makeGetStoryData({logger, takeDomSnapshots, waitBeforeScreenshot, reloa
32
37
  await renderStoryLegacy();
33
38
  }
34
39
 
35
- const wait = waitBeforeStory || waitBeforeScreenshot;
40
+ const wait = waitBeforeStory || waitBeforeCapture;
36
41
  if (typeof wait === 'number') {
37
- ArgumentGuard.greaterThanOrEqualToZero(wait, 'waitBeforeScreenshot', true);
42
+ ArgumentGuard.greaterThanOrEqualToZero(wait, 'waitBeforeCapture', true);
38
43
  }
39
44
  if (wait) {
40
45
  logger.log(`waiting before screenshot of ${title} ${wait}`);
@@ -49,9 +54,16 @@ function makeGetStoryData({logger, takeDomSnapshots, waitBeforeScreenshot, reloa
49
54
 
50
55
  logger.log(`running takeDomSnapshot(s) for story ${title}`);
51
56
 
52
- const snapshots = await takeDomSnapshots({
57
+ const result = await takeDomSnapshots({
53
58
  page,
59
+ browser,
54
60
  layoutBreakpoints: eyesParameters ? eyesParameters.layoutBreakpoints : undefined,
61
+ waitBeforeCapture: wait
62
+ ? async () => {
63
+ logger.log(`waiting before screenshot of ${title} ${wait}`);
64
+ await waitFor(page, wait);
65
+ }
66
+ : undefined,
55
67
  });
56
68
 
57
69
  if (eyesParameters && eyesParameters.runAfter) {
@@ -61,7 +73,7 @@ function makeGetStoryData({logger, takeDomSnapshots, waitBeforeScreenshot, reloa
61
73
  }
62
74
 
63
75
  logger.log(`done getting data from story`, title);
64
- return snapshots;
76
+ return result;
65
77
 
66
78
  async function renderStoryLegacy() {
67
79
  logger.log(`getting data from story ${storyUrl}`);
@@ -72,9 +84,16 @@ function makeGetStoryData({logger, takeDomSnapshots, waitBeforeScreenshot, reloa
72
84
  }
73
85
  }
74
86
 
75
- async function getEyesVariationParam() {
87
+ async function getQueryParam(queryParamNames = []) {
76
88
  try {
77
- return new URL(await page.url()).searchParams.get('eyes-variation');
89
+ const url = new URL(await page.url());
90
+ queryParamNames = [...queryParamNames, 'eyes-variation'];
91
+ const queryParamName = queryParamNames.find(queryParamName =>
92
+ url.searchParams.has(queryParamName),
93
+ );
94
+ if (queryParamName) {
95
+ return {name: queryParamName, value: url.searchParams.get(queryParamName)};
96
+ }
78
97
  } catch (ex) {
79
98
  logger.log('failed to get url from page (in need of eyes-variation param)');
80
99
  }
@@ -1,10 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  function getStoryTitle({name, kind, parameters}) {
4
- const variationUrlParam = parameters && parameters.eyes && parameters.eyes.variationUrlParam;
5
- const urlSuffix = variationUrlParam ? ` [${variationUrlParam}]` : '';
4
+ const queryParam = parameters && parameters.eyes && parameters.eyes.queryParam;
5
+ const variation =
6
+ queryParam && queryParam.name === 'eyes-variation' ? queryParam.value : undefined;
6
7
 
7
- return `${kind}: ${name}${urlSuffix}`;
8
+ return `${kind}: ${name}${variation ? ` [${variation}]` : ''}`;
8
9
  }
9
10
 
10
11
  module.exports = getStoryTitle;
@@ -2,12 +2,12 @@
2
2
  const getIframeUrl = require('./getIframeUrl');
3
3
 
4
4
  function getStoryUrl({name, kind, parameters}, baseUrl) {
5
- const variationUrlParam = parameters && parameters.eyes && parameters.eyes.variationUrlParam;
6
- const variation = variationUrlParam ? `&eyes-variation=${variationUrlParam}` : '';
5
+ const queryParam = parameters && parameters.eyes && parameters.eyes.queryParam;
6
+ const queryParamString = queryParam ? `&${queryParam.name}=${queryParam.value}` : '';
7
7
 
8
8
  return `${getIframeUrl(baseUrl)}&selectedKind=${encodeURIComponent(
9
9
  kind,
10
- )}&selectedStory=${encodeURIComponent(name)}${variation}`;
10
+ )}&selectedStory=${encodeURIComponent(name)}${queryParamString}`;
11
11
  }
12
12
 
13
13
  module.exports = getStoryUrl;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  const getStoryUrl = require('./getStoryUrl');
3
- const getStoryTitle = require('./getStoryTitle');
3
+ const getStoryBaselineName = require('./getStoryBaselineName');
4
4
  const ora = require('ora');
5
5
  const {presult} = require('@applitools/functional-commons');
6
6
  const {shouldRenderIE} = require('./shouldRenderIE');
@@ -61,16 +61,17 @@ function makeRenderStories({
61
61
  async function processStory() {
62
62
  const story = stories[currIndex++];
63
63
  const storyUrl = getStoryUrl(story, storybookUrl);
64
- const title = getStoryTitle(story);
65
- const {waitBeforeScreenshot} = (story.parameters && story.parameters.eyes) || {};
64
+ const title = getStoryBaselineName(story);
65
+ const {waitBeforeCapture} = (story.parameters && story.parameters.eyes) || {};
66
66
 
67
67
  try {
68
68
  let [error, storyData] = await presult(
69
69
  getStoryData({
70
70
  story,
71
71
  storyUrl,
72
+ browser: config.browser,
72
73
  page,
73
- waitBeforeStory: waitBeforeScreenshot,
74
+ waitBeforeStory: waitBeforeCapture,
74
75
  }),
75
76
  );
76
77
 
@@ -89,8 +90,9 @@ function makeRenderStories({
89
90
  getStoryData({
90
91
  story,
91
92
  storyUrl,
93
+ browser: config.browser,
92
94
  page: newPageObj.page,
93
- waitBeforeStory: waitBeforeScreenshot,
95
+ waitBeforeStory: waitBeforeCapture,
94
96
  }),
95
97
  );
96
98
  error = newError;
@@ -107,7 +109,8 @@ function makeRenderStories({
107
109
  }
108
110
 
109
111
  const testResults = await renderStory({
110
- snapshot: storyData,
112
+ snapshot: storyData.snapshots,
113
+ cookies: storyData.cookies,
111
114
  url: storyUrl,
112
115
  story,
113
116
  config,
@@ -139,7 +142,7 @@ function makeRenderStories({
139
142
 
140
143
  function onDoneStory(resultsOrErr, story) {
141
144
  spinner.text = updateSpinnerText(++doneStories, stories.length);
142
- const title = getStoryTitle(story);
145
+ const title = getStoryBaselineName(story);
143
146
  allTestResults.push({title, resultsOrErr});
144
147
  return {title, resultsOrErr};
145
148
  }
@@ -1,10 +1,12 @@
1
1
  'use strict';
2
2
  const getStoryTitle = require('./getStoryTitle');
3
+ const getStoryBaselineName = require('./getStoryBaselineName');
3
4
  const {deprecationWarning} = require('@applitools/eyes-sdk-core').GeneralUtils;
4
5
 
5
6
  function makeRenderStory({logger, testWindow, performance, timeItAsync}) {
6
7
  return function renderStory({config, story, snapshot, url}) {
7
8
  const {name, kind, parameters} = story;
9
+ const baselineName = getStoryBaselineName({name, kind, parameters});
8
10
  const title = getStoryTitle({name, kind, parameters});
9
11
  const eyesOptions = Object.assign({}, config, (parameters && parameters.eyes) || {});
10
12
  const {
@@ -41,10 +43,11 @@ function makeRenderStory({logger, testWindow, performance, timeItAsync}) {
41
43
  ignoreRegionsBackCompat = ignore;
42
44
  }
43
45
 
44
- logger.log('running story', title);
46
+ logger.log(`running story ${title} with baseline ${baselineName}`);
45
47
 
46
48
  const openParams = {
47
- testName: title,
49
+ testName: baselineName,
50
+ displayName: title,
48
51
  browser: config.browser,
49
52
  properties: [
50
53
  {name: 'Component name', value: kind},
@@ -77,12 +80,12 @@ function makeRenderStory({logger, testWindow, performance, timeItAsync}) {
77
80
  ignoreDisplacements,
78
81
  };
79
82
 
80
- return timeItAsync(title, async () => {
83
+ return timeItAsync(baselineName, async () => {
81
84
  return testWindow({openParams, checkParams, throwEx: false});
82
85
  }).then(onDoneStory);
83
86
 
84
87
  function onDoneStory(results) {
85
- logger.log('finished story', title, 'in', performance[title]);
88
+ logger.log('finished story', baselineName, 'in', performance[baselineName]);
86
89
  return results;
87
90
  }
88
91
  };
@@ -1,54 +0,0 @@
1
- 'use strict';
2
-
3
- function addVariationStories({stories, config}) {
4
- const globalVariationFunc =
5
- typeof config.variations === 'function' ? config.variations : undefined;
6
- const globalVariationArray = Array.isArray(config.variations) ? config.variations : undefined;
7
- const storiesToAddVariations = stories.filter(getStoryVariations);
8
-
9
- const storiesByVariation = {};
10
-
11
- for (const story of storiesToAddVariations) {
12
- const variations = getStoryVariations(story);
13
- if (!Array.isArray(variations)) {
14
- throw new Error('variations should be an array');
15
- }
16
-
17
- for (const variation of variations) {
18
- const variationArr = storiesByVariation[variation] || (storiesByVariation[variation] = []);
19
- variationArr.push({
20
- ...story,
21
- parameters: addFlagToParameters(story.parameters, variation),
22
- });
23
- }
24
- }
25
-
26
- return Object.keys(storiesByVariation).reduce((storiesWithVariations, variation) => {
27
- return storiesWithVariations.concat(storiesByVariation[variation]);
28
- }, stories);
29
-
30
- function getStoryVariations(story) {
31
- if (story.parameters && story.parameters.eyes && story.parameters.eyes.variations) {
32
- return story.parameters.eyes.variations;
33
- }
34
-
35
- if (globalVariationFunc) {
36
- return globalVariationFunc(story);
37
- }
38
-
39
- if (globalVariationArray) return globalVariationArray;
40
- }
41
- }
42
-
43
- function addFlagToParameters(parameters, variation) {
44
- return parameters
45
- ? {
46
- ...parameters,
47
- eyes: parameters.eyes
48
- ? {...parameters.eyes, variationUrlParam: variation}
49
- : {variationUrlParam: variation},
50
- }
51
- : {eyes: {variationUrlParam: variation}};
52
- }
53
-
54
- module.exports = addVariationStories;