@applitools/eyes-storybook 3.60.0 → 3.61.1
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 +74 -0
- package/dist/index.d.ts +22 -0
- package/package.json +4 -3
- package/src/cli.js +9 -16
- package/src/configParams.js +10 -2
- package/src/defaultConfig.js +3 -12
- package/src/errMessages.js +24 -9
- package/src/extractEnvironment.js +4 -1
- package/src/eyesStorybook.js +25 -17
- package/src/eyesStorybookEventEmitter.js +36 -0
- package/src/generateConfig.js +2 -12
- package/src/getConfigAndLogger.js +25 -0
- package/src/index.ts +26 -0
- package/src/main.js +7 -0
- package/src/renderStories.js +33 -5
- package/src/renderStory.js +21 -1
- package/src/transformSettings.js +0 -1
- package/src/utils/config-validator.js +8 -3
- package/src/validateAndPopulateConfig.js +11 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,79 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.61.1](https://github.com/Applitools-Dev/sdk/compare/js/eyes-storybook@3.61.0...js/eyes-storybook@3.61.1) (2025-10-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* various config fixes for storybook-addon ([#3257](https://github.com/Applitools-Dev/sdk/issues/3257)) ([86fd4d1](https://github.com/Applitools-Dev/sdk/commit/86fd4d114122bbaf675b5fa361b0e967595ca296))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Dependencies
|
|
12
|
+
|
|
13
|
+
* @applitools/dom-snapshot bumped to 4.13.9
|
|
14
|
+
#### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* verbose Unknown CSS object model logging | AD-11542 | FLD-3687 ([#3261](https://github.com/Applitools-Dev/sdk/issues/3261)) ([ba85d32](https://github.com/Applitools-Dev/sdk/commit/ba85d3287a81af109db1a7b407e5ead20f395d9f))
|
|
17
|
+
* @applitools/core-base bumped to 1.28.2
|
|
18
|
+
#### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* various config fixes for storybook-addon ([#3257](https://github.com/Applitools-Dev/sdk/issues/3257)) ([86fd4d1](https://github.com/Applitools-Dev/sdk/commit/86fd4d114122bbaf675b5fa361b0e967595ca296))
|
|
21
|
+
* @applitools/eyes bumped to 1.36.10
|
|
22
|
+
#### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* various config fixes for storybook-addon ([#3257](https://github.com/Applitools-Dev/sdk/issues/3257)) ([86fd4d1](https://github.com/Applitools-Dev/sdk/commit/86fd4d114122bbaf675b5fa361b0e967595ca296))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
* @applitools/nml-client bumped to 1.11.8
|
|
29
|
+
|
|
30
|
+
* @applitools/ec-client bumped to 1.12.10
|
|
31
|
+
|
|
32
|
+
* @applitools/core bumped to 4.50.1
|
|
33
|
+
#### Bug Fixes
|
|
34
|
+
|
|
35
|
+
* various config fixes for storybook-addon ([#3257](https://github.com/Applitools-Dev/sdk/issues/3257)) ([86fd4d1](https://github.com/Applitools-Dev/sdk/commit/86fd4d114122bbaf675b5fa361b0e967595ca296))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## [3.61.0](https://github.com/Applitools-Dev/sdk/compare/js/eyes-storybook@3.60.0...js/eyes-storybook@3.61.0) (2025-10-01)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Features
|
|
44
|
+
|
|
45
|
+
* storybook addon ([#3104](https://github.com/Applitools-Dev/sdk/issues/3104)) ([16e09cb](https://github.com/Applitools-Dev/sdk/commit/16e09cba8928c3a24b9e0d9d41e0936fbaec2773))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
### Dependencies
|
|
49
|
+
|
|
50
|
+
* @applitools/screenshoter bumped to 3.12.6
|
|
51
|
+
#### Bug Fixes
|
|
52
|
+
|
|
53
|
+
* wait after scroll | FLD-3594 ([#3252](https://github.com/Applitools-Dev/sdk/issues/3252)) ([e452422](https://github.com/Applitools-Dev/sdk/commit/e4524229b64e40d9b9596a92bfa94daf5824286a))
|
|
54
|
+
* @applitools/core-base bumped to 1.28.1
|
|
55
|
+
#### Bug Fixes
|
|
56
|
+
|
|
57
|
+
* unexpected concurrency values from server | AD-11465 ([#3248](https://github.com/Applitools-Dev/sdk/issues/3248)) ([0dd28c7](https://github.com/Applitools-Dev/sdk/commit/0dd28c7b297d5ad3aabc6b87e427e3e09a993825))
|
|
58
|
+
* @applitools/nml-client bumped to 1.11.7
|
|
59
|
+
|
|
60
|
+
* @applitools/ec-client bumped to 1.12.9
|
|
61
|
+
|
|
62
|
+
* @applitools/core bumped to 4.49.0
|
|
63
|
+
#### Features
|
|
64
|
+
|
|
65
|
+
* storybook addon ([#3104](https://github.com/Applitools-Dev/sdk/issues/3104)) ([16e09cb](https://github.com/Applitools-Dev/sdk/commit/16e09cba8928c3a24b9e0d9d41e0936fbaec2773))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
#### Bug Fixes
|
|
69
|
+
|
|
70
|
+
* duplicate concurrency warnings ([#3255](https://github.com/Applitools-Dev/sdk/issues/3255)) ([ef2f94a](https://github.com/Applitools-Dev/sdk/commit/ef2f94ab4137c78396583f166344285beeb49be7))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
* @applitools/eyes bumped to 1.36.9
|
|
75
|
+
|
|
76
|
+
|
|
3
77
|
## [3.60.0](https://github.com/Applitools-Dev/sdk/compare/js/eyes-storybook@3.59.1...js/eyes-storybook@3.60.0) (2025-09-22)
|
|
4
78
|
|
|
5
79
|
|
package/dist/index.d.ts
CHANGED
|
@@ -98,6 +98,21 @@ export type ApplitoolsConfig = Omit<ConfigurationPlain, irrelevantToStorybook |
|
|
|
98
98
|
* @default false
|
|
99
99
|
*/
|
|
100
100
|
browserCacheRequests?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Headers to override in all browser requests.
|
|
103
|
+
* @default undefined
|
|
104
|
+
*/
|
|
105
|
+
browserHeadersOverride?: Record<string, string>;
|
|
106
|
+
/**
|
|
107
|
+
* Timeout in milliseconds for all browser requests.
|
|
108
|
+
* @default undefined
|
|
109
|
+
*/
|
|
110
|
+
browserRequestsTimeout?: number;
|
|
111
|
+
/**
|
|
112
|
+
* Array of URL patterns to block requests to.
|
|
113
|
+
* @default undefined
|
|
114
|
+
*/
|
|
115
|
+
networkBlockPatterns?: string[];
|
|
101
116
|
/**
|
|
102
117
|
* Array of browser configurations for screenshot generation.
|
|
103
118
|
* Defines size and browser type for generated screenshots.
|
|
@@ -241,6 +256,13 @@ export type ApplitoolsConfig = Omit<ConfigurationPlain, irrelevantToStorybook |
|
|
|
241
256
|
* @default undefined
|
|
242
257
|
*/
|
|
243
258
|
shard?: string;
|
|
259
|
+
/**
|
|
260
|
+
* The size of the Puppeteer browser's window.
|
|
261
|
+
* 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.
|
|
262
|
+
*
|
|
263
|
+
* Note: Stories will **not** be rendered and tested on this viewport size, unless you also include it in the `browser` parameter.
|
|
264
|
+
*/
|
|
265
|
+
viewportSize?: ConfigurationPlain['viewportSize'];
|
|
244
266
|
};
|
|
245
267
|
export type configKeys = keyof ApplitoolsConfig;
|
|
246
268
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applitools/eyes-storybook",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.61.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"applitools",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"eyes-setup": "./bin/eyes-setup.js",
|
|
20
20
|
"eyes-storybook": "./bin/eyes-storybook.js"
|
|
21
21
|
},
|
|
22
|
+
"main": "./src/main.js",
|
|
22
23
|
"files": [
|
|
23
24
|
"src",
|
|
24
25
|
"bin",
|
|
@@ -58,9 +59,9 @@
|
|
|
58
59
|
"up:framework": "cd test/fixtures/storybook-versions/${APPLITOOLS_FRAMEWORK_VERSION} && npm ci"
|
|
59
60
|
},
|
|
60
61
|
"dependencies": {
|
|
61
|
-
"@applitools/core": "4.
|
|
62
|
+
"@applitools/core": "4.50.1",
|
|
62
63
|
"@applitools/driver": "1.23.5",
|
|
63
|
-
"@applitools/eyes": "1.36.
|
|
64
|
+
"@applitools/eyes": "1.36.10",
|
|
64
65
|
"@applitools/functional-commons": "1.6.0",
|
|
65
66
|
"@applitools/logger": "2.2.4",
|
|
66
67
|
"@applitools/monitoring-commons": "1.0.19",
|
package/src/cli.js
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const yargs = require('yargs');
|
|
3
|
-
const {makeLogger} = require('@applitools/logger');
|
|
4
|
-
const {configParams: externalConfigParams} = require('./configParams');
|
|
5
3
|
const VERSION = require('../package.json').version;
|
|
6
|
-
const eyesStorybook = require('./eyesStorybook');
|
|
7
4
|
const processResults = require('./processResults');
|
|
8
|
-
const validateAndPopulateConfig = require('./validateAndPopulateConfig');
|
|
9
5
|
const yargsOptions = require('./yargsOptions');
|
|
10
|
-
const {generateConfig} = require('./generateConfig');
|
|
11
|
-
const defaultConfig = require('./defaultConfig');
|
|
12
|
-
const configDigest = require('./configDigest');
|
|
13
6
|
const {makeTiming} = require('@applitools/monitoring-commons');
|
|
14
7
|
const handleJsonFile = require('./handleJsonFile');
|
|
15
8
|
const handleTapFile = require('./handleTapFile');
|
|
16
9
|
const handleXmlFile = require('./handleXmlFile');
|
|
10
|
+
const {getConfigAndLogger} = require('./getConfigAndLogger');
|
|
17
11
|
const {presult} = require('@applitools/functional-commons');
|
|
18
12
|
const chalk = require('chalk');
|
|
19
13
|
const utils = require('@applitools/utils');
|
|
14
|
+
const {EyesError} = require('@applitools/eyes');
|
|
15
|
+
const eyesStorybook = require('./eyesStorybook');
|
|
20
16
|
const {performance, timeItAsync} = makeTiming();
|
|
21
17
|
|
|
22
18
|
(async function () {
|
|
@@ -31,7 +27,7 @@ const {performance, timeItAsync} = makeTiming();
|
|
|
31
27
|
.options(yargsOptions).argv;
|
|
32
28
|
|
|
33
29
|
console.log(`Using @applitools/eyes-storybook version ${VERSION}.\n`);
|
|
34
|
-
const config =
|
|
30
|
+
const {config, logger} = await getConfigAndLogger(argv);
|
|
35
31
|
|
|
36
32
|
if (config.shard) {
|
|
37
33
|
console.log(`Running with shard: ${config.shard.current}/${config.shard.total}`);
|
|
@@ -41,13 +37,6 @@ const {performance, timeItAsync} = makeTiming();
|
|
|
41
37
|
}
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
const logger = makeLogger({level: config.showLogs ? 'info' : 'silent', label: 'eyes'});
|
|
45
|
-
await validateAndPopulateConfig({
|
|
46
|
-
config,
|
|
47
|
-
logger,
|
|
48
|
-
packagePath: process.cwd(),
|
|
49
|
-
});
|
|
50
|
-
logger.log(`Running with the following config:\n${configDigest(config)}`);
|
|
51
40
|
const [err, results] = await presult(
|
|
52
41
|
timeItAsync('eyesStorybook', () => eyesStorybook({config, logger, performance, timeItAsync})),
|
|
53
42
|
);
|
|
@@ -78,7 +67,11 @@ const {performance, timeItAsync} = makeTiming();
|
|
|
78
67
|
process.exit(exitCode);
|
|
79
68
|
}
|
|
80
69
|
} catch (ex) {
|
|
81
|
-
|
|
70
|
+
if (utils.types.instanceOf(ex, EyesError)) {
|
|
71
|
+
console.log(ex.message);
|
|
72
|
+
} else {
|
|
73
|
+
console.log(ex);
|
|
74
|
+
}
|
|
82
75
|
process.exit(1);
|
|
83
76
|
}
|
|
84
77
|
})();
|
package/src/configParams.js
CHANGED
|
@@ -30,10 +30,8 @@ const configParams = [
|
|
|
30
30
|
'autProxy',
|
|
31
31
|
'saveDiffs',
|
|
32
32
|
'saveFailedTests',
|
|
33
|
-
'saveNewTests',
|
|
34
33
|
'compareWithParentBranch',
|
|
35
34
|
'ignoreBaseline',
|
|
36
|
-
'serverUrl',
|
|
37
35
|
'concurrency',
|
|
38
36
|
'testConcurrency',
|
|
39
37
|
'useDom',
|
|
@@ -44,6 +42,16 @@ const configParams = [
|
|
|
44
42
|
'dontCloseBatches',
|
|
45
43
|
'showBrowserLogs',
|
|
46
44
|
'shard',
|
|
45
|
+
'storybookUrl',
|
|
46
|
+
'browserCacheRequests',
|
|
47
|
+
'browserHeadersOverride',
|
|
48
|
+
'browserRequestsTimeout',
|
|
49
|
+
'networkBlockPatterns',
|
|
50
|
+
'navigationWaitUntil',
|
|
51
|
+
'include',
|
|
52
|
+
'xmlFilePath',
|
|
53
|
+
'tapFilePath',
|
|
54
|
+
'storybookStaticDir',
|
|
47
55
|
];
|
|
48
56
|
|
|
49
57
|
module.exports = {configParams};
|
package/src/defaultConfig.js
CHANGED
|
@@ -5,25 +5,16 @@ module.exports = {
|
|
|
5
5
|
storybookPort: 9000,
|
|
6
6
|
storybookHost: 'localhost',
|
|
7
7
|
storybookConfigDir: '.storybook',
|
|
8
|
-
storybookUrl: undefined,
|
|
9
|
-
storybookStaticDir: undefined,
|
|
10
8
|
showStorybookOutput: false,
|
|
11
9
|
waitBeforeScreenshot: 50, // backward compatibility
|
|
12
10
|
waitBeforeScreenshots: 50, // backward compatibility
|
|
13
11
|
waitBeforeCapture: 50,
|
|
14
12
|
viewportSize: {width: 1024, height: 768},
|
|
15
|
-
tapFilePath: undefined,
|
|
16
|
-
xmlFilePath: undefined,
|
|
17
13
|
exitcode: true,
|
|
18
14
|
readStoriesTimeout: 60000,
|
|
19
15
|
reloadPagePerStory: false,
|
|
20
|
-
include: undefined,
|
|
21
16
|
startStorybookServerTimeout: 300,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
browserHeadersOverride: undefined,
|
|
26
|
-
browserCacheRequests: undefined,
|
|
27
|
-
showBrowserLogs: undefined,
|
|
28
|
-
shard: undefined,
|
|
17
|
+
serverUrl: 'https://eyes.applitools.com',
|
|
18
|
+
saveNewTests: true,
|
|
19
|
+
fully: true,
|
|
29
20
|
};
|
package/src/errMessages.js
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
|
|
2
3
|
const chalk = require('chalk');
|
|
4
|
+
const {MissingApiKeyError} = require('@applitools/core');
|
|
5
|
+
const {EyesError} = require('@applitools/eyes');
|
|
6
|
+
|
|
7
|
+
class AbortedByUserError extends EyesError {
|
|
8
|
+
reason = 'abortedByUser';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class InvalidConfigFileError extends EyesError {
|
|
12
|
+
reason = 'invalidConfigFile';
|
|
3
13
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
constructor(error) {
|
|
15
|
+
super();
|
|
16
|
+
|
|
17
|
+
const documentationUrl = 'https://applitools.com/tutorials/sdks/storybook/config#properties';
|
|
18
|
+
|
|
19
|
+
this.message =
|
|
20
|
+
`Your configuration file is invalid. Please review our documentation for valid configuration settings: ${documentationUrl}.
|
|
21
|
+
Additionally, you can generate a new configuration by running 'npx eyes-setup'.
|
|
22
|
+
\n\nError details: ${error.message}`.trim();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
12
25
|
|
|
13
26
|
const missingAppNameAndPackageJsonFailMsg = `
|
|
14
27
|
${chalk.red(
|
|
@@ -47,9 +60,11 @@ function deprecationWarning({deprecatedThing, newThing, isDead}) {
|
|
|
47
60
|
}
|
|
48
61
|
|
|
49
62
|
module.exports = {
|
|
50
|
-
missingApiKeyFailMsg,
|
|
51
63
|
missingAppNameAndPackageJsonFailMsg,
|
|
52
64
|
missingAppNameInPackageJsonFailMsg,
|
|
53
65
|
refineErrorMessage,
|
|
54
66
|
deprecationWarning,
|
|
67
|
+
MissingApiKeyError,
|
|
68
|
+
AbortedByUserError,
|
|
69
|
+
InvalidConfigFileError,
|
|
55
70
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const {getStorybookFrameworks} = require('./utils/frameworks');
|
|
2
2
|
|
|
3
|
-
function extractEnvironment() {
|
|
3
|
+
function extractEnvironment(addonVersion) {
|
|
4
4
|
const versions = {};
|
|
5
5
|
try {
|
|
6
6
|
const {name, version} = require('storybook/package.json');
|
|
@@ -23,6 +23,9 @@ function extractEnvironment() {
|
|
|
23
23
|
peerDependencies,
|
|
24
24
|
);
|
|
25
25
|
sdk = {lang: 'js', name, currentVersion: version, framework, dependencyFrameworks};
|
|
26
|
+
if (addonVersion) {
|
|
27
|
+
sdk.addonVersion = addonVersion;
|
|
28
|
+
}
|
|
26
29
|
} catch {
|
|
27
30
|
// NOTE: ignore error
|
|
28
31
|
}
|
package/src/eyesStorybook.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const puppeteer = require('puppeteer');
|
|
3
|
+
const configDigest = require('./configDigest');
|
|
3
4
|
const getStories = require('../dist/getStories');
|
|
4
5
|
const {presult} = require('@applitools/functional-commons');
|
|
5
6
|
const {executeWithRetry} = require('./utils/executeWithRetry');
|
|
@@ -31,7 +32,12 @@ async function eyesStorybook({
|
|
|
31
32
|
performance,
|
|
32
33
|
timeItAsync,
|
|
33
34
|
outputStream = process.stderr,
|
|
35
|
+
eventEmitter,
|
|
36
|
+
signal = new AbortController().signal,
|
|
37
|
+
addonVersion,
|
|
34
38
|
}) {
|
|
39
|
+
logger.log(`Running with the following config:\n${configDigest(config)}`);
|
|
40
|
+
|
|
35
41
|
let renderIE = false;
|
|
36
42
|
let transitioning = false;
|
|
37
43
|
logger.log('eyesStorybook started');
|
|
@@ -51,7 +57,6 @@ async function eyesStorybook({
|
|
|
51
57
|
logger.log(ex);
|
|
52
58
|
throw new Error(`Storybook URL is not valid: ${storybookUrl}`);
|
|
53
59
|
}
|
|
54
|
-
const agentId = `eyes-storybook/${require('../package.json').version}`;
|
|
55
60
|
process.env.PUPPETEER_DISABLE_HEADLESS_WARNING = true;
|
|
56
61
|
const browser = await puppeteer.launch(config.puppeteerOptions);
|
|
57
62
|
logger.log('browser launched');
|
|
@@ -73,8 +78,8 @@ async function eyesStorybook({
|
|
|
73
78
|
cache: config.browserCacheRequests,
|
|
74
79
|
});
|
|
75
80
|
|
|
76
|
-
const environment = extractEnvironment();
|
|
77
|
-
const core = await makeCore({spec, agentId, environment, logger});
|
|
81
|
+
const environment = extractEnvironment(addonVersion);
|
|
82
|
+
const core = await makeCore({spec, agentId: config.agentId, environment, logger});
|
|
78
83
|
const manager = await core.makeManager({
|
|
79
84
|
type: 'ufg',
|
|
80
85
|
settings: {concurrency: config.testConcurrency, useServerConcurrency: true},
|
|
@@ -85,24 +90,15 @@ async function eyesStorybook({
|
|
|
85
90
|
settings: {
|
|
86
91
|
eyesServerUrl: config.eyesServerUrl,
|
|
87
92
|
apiKey: config.apiKey,
|
|
88
|
-
agentId,
|
|
93
|
+
agentId: config.agentId,
|
|
89
94
|
proxy: config.proxy,
|
|
90
95
|
useDnsCache: config.useDnsCache,
|
|
91
96
|
},
|
|
92
97
|
})
|
|
93
98
|
.catch(async error => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
error.message.includes('Please check your API key and try again.')
|
|
98
|
-
) {
|
|
99
|
-
const failMsg = 'Incorrect API Key';
|
|
100
|
-
logger.log(failMsg);
|
|
101
|
-
await browser.close();
|
|
102
|
-
throw new Error(failMsg);
|
|
103
|
-
} else {
|
|
104
|
-
throw error;
|
|
105
|
-
}
|
|
99
|
+
logger.error(error?.message);
|
|
100
|
+
await browser.close();
|
|
101
|
+
throw error;
|
|
106
102
|
});
|
|
107
103
|
|
|
108
104
|
const getStoriesWithConfig = makeGetStoriesWithConfig({config});
|
|
@@ -182,6 +178,7 @@ async function eyesStorybook({
|
|
|
182
178
|
concurrency: account.serverConcurrency.componentConcurrency,
|
|
183
179
|
appName: config.appName,
|
|
184
180
|
serverSettings: account.eyesServer,
|
|
181
|
+
signal,
|
|
185
182
|
});
|
|
186
183
|
|
|
187
184
|
const renderStories = makeRenderStories({
|
|
@@ -192,6 +189,8 @@ async function eyesStorybook({
|
|
|
192
189
|
logger,
|
|
193
190
|
stream: outputStream,
|
|
194
191
|
pagePool,
|
|
192
|
+
eventEmitter,
|
|
193
|
+
signal,
|
|
195
194
|
});
|
|
196
195
|
|
|
197
196
|
logger.log('finished creating functions');
|
|
@@ -209,6 +208,15 @@ async function eyesStorybook({
|
|
|
209
208
|
timeItAsync,
|
|
210
209
|
}),
|
|
211
210
|
);
|
|
211
|
+
if (signal.aborted) {
|
|
212
|
+
if (error) {
|
|
213
|
+
const msg = refineErrorMessage({prefix: 'Error in executeRenders:', error});
|
|
214
|
+
logger.log('Error in executeRenders:', error);
|
|
215
|
+
throw new Error(msg);
|
|
216
|
+
} else {
|
|
217
|
+
return {results}; // processResults (which is not used in the addon) doesn't support missing the summary. But it's not a real concern right now.
|
|
218
|
+
}
|
|
219
|
+
}
|
|
212
220
|
const [errorInGetResults, testResultsSummary] = await presult(
|
|
213
221
|
manager.getResults({throwErr: false}),
|
|
214
222
|
);
|
|
@@ -219,7 +227,7 @@ async function eyesStorybook({
|
|
|
219
227
|
|
|
220
228
|
if (error) {
|
|
221
229
|
const msg = refineErrorMessage({prefix: 'Error in executeRenders:', error});
|
|
222
|
-
logger.log(error);
|
|
230
|
+
logger.log('Error in executeRenders:', error);
|
|
223
231
|
throw new Error(msg);
|
|
224
232
|
} else {
|
|
225
233
|
return {summary: testResultsSummary, results};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const eyesStorybookOrig = require('./eyesStorybook');
|
|
4
|
+
const {EventEmitter} = require('node:events');
|
|
5
|
+
|
|
6
|
+
function eyesStorybookEventEmitter({
|
|
7
|
+
eyesStorybook = eyesStorybookOrig,
|
|
8
|
+
config,
|
|
9
|
+
logger,
|
|
10
|
+
performance,
|
|
11
|
+
timeItAsync,
|
|
12
|
+
signal,
|
|
13
|
+
addonVersion,
|
|
14
|
+
}) {
|
|
15
|
+
const eventEmitter = new EventEmitter();
|
|
16
|
+
const startedAt = Date.now();
|
|
17
|
+
|
|
18
|
+
eyesStorybook({config, logger, performance, timeItAsync, eventEmitter, signal, addonVersion})
|
|
19
|
+
.then(({results}) => {
|
|
20
|
+
eventEmitter.emit('result', {
|
|
21
|
+
startedAt,
|
|
22
|
+
duration: performance['renderStories'],
|
|
23
|
+
storyResults: results.map(({story, resultsOrErr}) => ({
|
|
24
|
+
story: {id: story.id, queryParams: story.config.queryParams},
|
|
25
|
+
[Array.isArray(resultsOrErr) ? 'results' : 'error']: resultsOrErr,
|
|
26
|
+
})),
|
|
27
|
+
});
|
|
28
|
+
})
|
|
29
|
+
.catch(err => {
|
|
30
|
+
eventEmitter.emit('error', err);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return eventEmitter;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {eyesStorybookEventEmitter};
|
package/src/generateConfig.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const lodash = require('lodash');
|
|
3
3
|
const utils = require('@applitools/utils');
|
|
4
4
|
const {resolve} = require('path');
|
|
5
|
-
const {deprecationWarning} = require('./errMessages');
|
|
5
|
+
const {deprecationWarning, InvalidConfigFileError} = require('./errMessages');
|
|
6
6
|
const uniq = require('./uniq');
|
|
7
7
|
const {DEFAULT_CONCURRENCY} = require('@applitools/core');
|
|
8
8
|
const MAX_DATA_GAP = DEFAULT_CONCURRENCY * 2;
|
|
@@ -53,10 +53,6 @@ function generateConfig({argv = {}, defaultConfig = {}, externalConfigParams = [
|
|
|
53
53
|
|
|
54
54
|
result.eyesServerUrl = result.serverUrl;
|
|
55
55
|
|
|
56
|
-
result.viewportSize = result.viewportSize ? result.viewportSize : {width: 1024, height: 600};
|
|
57
|
-
|
|
58
|
-
result.saveNewTests = result.saveNewTests === undefined ? true : result.saveNewTests;
|
|
59
|
-
|
|
60
56
|
// Auto-enable dontCloseBatches when sharding is used (unless explicitly set by user)
|
|
61
57
|
if (result.shard && result.dontCloseBatches === undefined) {
|
|
62
58
|
result.dontCloseBatches = true;
|
|
@@ -66,7 +62,6 @@ function generateConfig({argv = {}, defaultConfig = {}, externalConfigParams = [
|
|
|
66
62
|
}
|
|
67
63
|
|
|
68
64
|
result.keepBatchOpen = result.dontCloseBatches;
|
|
69
|
-
result.fully = result.fully === undefined ? true : false;
|
|
70
65
|
|
|
71
66
|
if (result.batchName) {
|
|
72
67
|
result.batch = {name: result.batchName, ...result.batch};
|
|
@@ -103,12 +98,7 @@ function getAndParseConfig({configPaths, configParams}) {
|
|
|
103
98
|
if (error.message.includes('Could not find configuration file')) {
|
|
104
99
|
return utils.config.populateConfigParams({config: {}, params: configParams});
|
|
105
100
|
}
|
|
106
|
-
|
|
107
|
-
throw new Error(
|
|
108
|
-
`Your configuration file is invalid. Please review our documentation for valid configuration settings: ${documentationUrl}.
|
|
109
|
-
Additionally, you can generate a new configuration by running 'npx eyes-setup'.
|
|
110
|
-
\n\nError details: ${error.message}`.trim(),
|
|
111
|
-
);
|
|
101
|
+
throw new InvalidConfigFileError(error);
|
|
112
102
|
}
|
|
113
103
|
}
|
|
114
104
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {generateConfig} = require('./generateConfig');
|
|
4
|
+
const defaultConfig = require('./defaultConfig');
|
|
5
|
+
const validateAndPopulateConfig = require('./validateAndPopulateConfig');
|
|
6
|
+
const {makeLogger} = require('@applitools/logger');
|
|
7
|
+
const {configParams: externalConfigParams} = require('./configParams');
|
|
8
|
+
|
|
9
|
+
async function getConfigAndLogger(argv = {}) {
|
|
10
|
+
const config = generateConfig({argv, defaultConfig, externalConfigParams});
|
|
11
|
+
const logger = makeLogger({
|
|
12
|
+
handler: argv.logHandler,
|
|
13
|
+
level: argv.logHandler ? 'all' : config.showLogs ? 'info' : 'silent', // if logHandler is passed, let's pass it all the logs. Otherwise, respect the config
|
|
14
|
+
label: 'eyes',
|
|
15
|
+
});
|
|
16
|
+
await validateAndPopulateConfig({
|
|
17
|
+
config,
|
|
18
|
+
logger,
|
|
19
|
+
packagePath: process.cwd(),
|
|
20
|
+
addonVersion: argv.addonVersion,
|
|
21
|
+
});
|
|
22
|
+
return {config, logger, defaultConfig};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = {getConfigAndLogger};
|
package/src/index.ts
CHANGED
|
@@ -139,6 +139,24 @@ export type ApplitoolsConfig = Omit<ConfigurationPlain, irrelevantToStorybook |
|
|
|
139
139
|
*/
|
|
140
140
|
browserCacheRequests?: boolean;
|
|
141
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Headers to override in all browser requests.
|
|
144
|
+
* @default undefined
|
|
145
|
+
*/
|
|
146
|
+
browserHeadersOverride?: Record<string, string>;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Timeout in milliseconds for all browser requests.
|
|
150
|
+
* @default undefined
|
|
151
|
+
*/
|
|
152
|
+
browserRequestsTimeout?: number;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Array of URL patterns to block requests to.
|
|
156
|
+
* @default undefined
|
|
157
|
+
*/
|
|
158
|
+
networkBlockPatterns?: string[];
|
|
159
|
+
|
|
142
160
|
/**
|
|
143
161
|
* Array of browser configurations for screenshot generation.
|
|
144
162
|
* Defines size and browser type for generated screenshots.
|
|
@@ -307,6 +325,14 @@ export type ApplitoolsConfig = Omit<ConfigurationPlain, irrelevantToStorybook |
|
|
|
307
325
|
* @default undefined
|
|
308
326
|
*/
|
|
309
327
|
shard?: string;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* The size of the Puppeteer browser's window.
|
|
331
|
+
* 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.
|
|
332
|
+
*
|
|
333
|
+
* Note: Stories will **not** be rendered and tested on this viewport size, unless you also include it in the `browser` parameter.
|
|
334
|
+
*/
|
|
335
|
+
viewportSize?: ConfigurationPlain['viewportSize'];
|
|
310
336
|
}
|
|
311
337
|
|
|
312
338
|
export type configKeys = keyof ApplitoolsConfig;
|
package/src/main.js
ADDED
package/src/renderStories.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
const getStoryUrl = require('./getStoryUrl');
|
|
3
3
|
const getStoryBaselineName = require('./getStoryBaselineName');
|
|
4
4
|
const ora = require('ora');
|
|
5
|
+
const {EventEmitter} = require('node:events');
|
|
5
6
|
const {presult} = require('@applitools/functional-commons');
|
|
7
|
+
const {AbortedByUserError} = require('./errMessages');
|
|
6
8
|
|
|
7
9
|
function makeRenderStories({
|
|
8
10
|
getStoryData,
|
|
@@ -13,19 +15,23 @@ function makeRenderStories({
|
|
|
13
15
|
stream,
|
|
14
16
|
sanityCheckForPage,
|
|
15
17
|
maxPageTTL = 60000,
|
|
18
|
+
eventEmitter = new EventEmitter(),
|
|
19
|
+
signal = new AbortController().signal,
|
|
16
20
|
}) {
|
|
17
21
|
let newPageIdToAdd;
|
|
18
22
|
|
|
19
23
|
return async function renderStories(stories, isIE) {
|
|
20
24
|
let doneStories = 0;
|
|
25
|
+
const totalStories = stories.length;
|
|
21
26
|
const allTestResults = [];
|
|
22
27
|
let allStoriesPromise = Promise.resolve();
|
|
23
28
|
let currIndex = 0;
|
|
24
29
|
|
|
25
30
|
const spinner = ora({
|
|
26
|
-
text: updateSpinnerText(0,
|
|
31
|
+
text: updateSpinnerText(0, totalStories),
|
|
27
32
|
stream,
|
|
28
33
|
});
|
|
34
|
+
eventEmitter.emit('progress', {doneStories, totalStories});
|
|
29
35
|
spinner.start();
|
|
30
36
|
prepareNewPage();
|
|
31
37
|
|
|
@@ -35,7 +41,19 @@ function makeRenderStories({
|
|
|
35
41
|
return allTestResults;
|
|
36
42
|
|
|
37
43
|
async function processStoryLoop() {
|
|
38
|
-
if (currIndex ===
|
|
44
|
+
if (currIndex === totalStories) return;
|
|
45
|
+
|
|
46
|
+
if (signal.aborted) {
|
|
47
|
+
const story = stories[currIndex++];
|
|
48
|
+
const title = getStoryBaselineName(story);
|
|
49
|
+
logger.log('aborting story before processing', title);
|
|
50
|
+
onDoneStory(
|
|
51
|
+
new AbortedByUserError(`${title} aborted before processing ${signal.reason}`),
|
|
52
|
+
story,
|
|
53
|
+
);
|
|
54
|
+
return processStoryLoop();
|
|
55
|
+
}
|
|
56
|
+
|
|
39
57
|
const {page, pageId, markPageAsFree, removePage, getCreatedAt} = await pagePool.getFreePage();
|
|
40
58
|
const livedTime = Date.now() - getCreatedAt();
|
|
41
59
|
logger.log(`[prepareNewPage] got free page: ${pageId}, lived time: ${livedTime}`);
|
|
@@ -103,6 +121,12 @@ function makeRenderStories({
|
|
|
103
121
|
const errMsg = `[page ${pageId}] Failed to get story data for "${title}". ${error}`;
|
|
104
122
|
logger.log(errMsg);
|
|
105
123
|
}
|
|
124
|
+
|
|
125
|
+
if (signal.aborted) {
|
|
126
|
+
logger.log('aborting story before open', title);
|
|
127
|
+
return onDoneStory(new Error(`${title} aborted before open ${signal.reason}`), story);
|
|
128
|
+
}
|
|
129
|
+
|
|
106
130
|
const testResults = await renderStory({
|
|
107
131
|
snapshots: storyData,
|
|
108
132
|
url: storyUrl,
|
|
@@ -132,10 +156,14 @@ function makeRenderStories({
|
|
|
132
156
|
}
|
|
133
157
|
|
|
134
158
|
function onDoneStory(resultsOrErr, story) {
|
|
135
|
-
spinner.text = updateSpinnerText(++doneStories,
|
|
159
|
+
spinner.text = updateSpinnerText(++doneStories, totalStories, story.config);
|
|
136
160
|
const title = getStoryBaselineName(story);
|
|
137
|
-
|
|
138
|
-
|
|
161
|
+
const result = {title, resultsOrErr, story};
|
|
162
|
+
allTestResults.push(result);
|
|
163
|
+
|
|
164
|
+
eventEmitter.emit('progress', {doneStories, totalStories});
|
|
165
|
+
|
|
166
|
+
return result;
|
|
139
167
|
}
|
|
140
168
|
|
|
141
169
|
async function prepareNewPage() {
|
package/src/renderStory.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
|
|
2
3
|
const throat = require('throat');
|
|
4
|
+
const {AbortedByUserError} = require('./errMessages');
|
|
3
5
|
const {storyToCheckSettings, storyToOpenSettings} = require('./transformSettings');
|
|
4
6
|
|
|
5
7
|
function makeRenderStory({
|
|
@@ -11,6 +13,7 @@ function makeRenderStory({
|
|
|
11
13
|
appName,
|
|
12
14
|
serverSettings,
|
|
13
15
|
concurrency,
|
|
16
|
+
signal = new AbortController().signal,
|
|
14
17
|
}) {
|
|
15
18
|
const throttle = throat(storyDataGap);
|
|
16
19
|
return function renderStory({story, snapshots, url}) {
|
|
@@ -24,7 +27,12 @@ function makeRenderStory({
|
|
|
24
27
|
|
|
25
28
|
return timeItAsync(baselineName, async () => {
|
|
26
29
|
const eyes = await openEyes({settings: openParams});
|
|
27
|
-
return new Promise((resolve, reject) => {
|
|
30
|
+
return new Promise(async (resolve, reject) => {
|
|
31
|
+
if (signal.aborted) {
|
|
32
|
+
return await abortStory();
|
|
33
|
+
} else {
|
|
34
|
+
signal.addEventListener('abort', abortStory);
|
|
35
|
+
}
|
|
28
36
|
throttle(async () => {
|
|
29
37
|
try {
|
|
30
38
|
if (snapshots) {
|
|
@@ -44,6 +52,18 @@ function makeRenderStory({
|
|
|
44
52
|
reject(err);
|
|
45
53
|
}
|
|
46
54
|
});
|
|
55
|
+
|
|
56
|
+
async function abortStory() {
|
|
57
|
+
logger.log('received abort signal for story', title);
|
|
58
|
+
|
|
59
|
+
// Inside core-base this will cause internal operations to be aborted
|
|
60
|
+
await eyes.abort({settings: {environments: checkParams.environments}});
|
|
61
|
+
|
|
62
|
+
// This will intentionally cause not to wait for results.
|
|
63
|
+
// Therefore there will be a "hanging" promise.
|
|
64
|
+
// But for the purpose of the addon, which is a long living process, it doesn't matter that we didn't stop the operation inside core.
|
|
65
|
+
reject(new AbortedByUserError(`${title} aborted after open ${signal.reason}`));
|
|
66
|
+
}
|
|
47
67
|
});
|
|
48
68
|
}).then(onDoneStory);
|
|
49
69
|
|
package/src/transformSettings.js
CHANGED
|
@@ -36,9 +36,6 @@ const applitoolsBaseKeys = [...dynamicBaseKeys, ...knownAliases];
|
|
|
36
36
|
* @type {Array<keyof (import('../index').ApplitoolsConfig) |
|
|
37
37
|
* 'reloadPagePerStory' |
|
|
38
38
|
* 'startStorybookServerTimeout' |
|
|
39
|
-
* 'networkBlockPatterns' |
|
|
40
|
-
* 'browserRequestsTimeout' |
|
|
41
|
-
* 'browserHeadersOverride' |
|
|
42
39
|
* 'waitBeforeScreenshot' |
|
|
43
40
|
* 'waitBeforeScreenshots' |
|
|
44
41
|
* 'fakeIE' |
|
|
@@ -51,11 +48,19 @@ const storybookSpecificKeys = [
|
|
|
51
48
|
'variations', // TODO - are we sure it should be supported from the applitools config file? https://applitools.com/tutorials/sdks/storybook/component-config#variations
|
|
52
49
|
'runInDocker',
|
|
53
50
|
'showLogs',
|
|
51
|
+
'storybookUrl',
|
|
52
|
+
'storybookStaticDir',
|
|
53
|
+
'include',
|
|
54
54
|
|
|
55
55
|
// Browser & Puppeteer Control
|
|
56
56
|
'browser',
|
|
57
57
|
'puppeteerOptions',
|
|
58
58
|
'puppeteerExtraHTTPHeaders', // created for a specific user, TODO - consider removing
|
|
59
|
+
'browserCacheRequests',
|
|
60
|
+
'browserHeadersOverride',
|
|
61
|
+
'browserRequestsTimeout',
|
|
62
|
+
'networkBlockPatterns',
|
|
63
|
+
'navigationWaitUntil',
|
|
59
64
|
|
|
60
65
|
// Region Matching
|
|
61
66
|
'ignoreRegions',
|
|
@@ -4,7 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const detect = require('detect-port');
|
|
5
5
|
const {version: packageVersion} = require('../package.json');
|
|
6
6
|
const {
|
|
7
|
-
|
|
7
|
+
MissingApiKeyError,
|
|
8
8
|
missingAppNameAndPackageJsonFailMsg,
|
|
9
9
|
missingAppNameInPackageJsonFailMsg,
|
|
10
10
|
startStorybookFailMsg,
|
|
@@ -16,9 +16,14 @@ const determineStorybookVersion = require('./utils/determineStorybookVersion');
|
|
|
16
16
|
const {logUnrecognizedKeys} = require('./utils/config-validator');
|
|
17
17
|
const utils = require('@applitools/utils');
|
|
18
18
|
|
|
19
|
-
async function validateAndPopulateConfig({
|
|
19
|
+
async function validateAndPopulateConfig({
|
|
20
|
+
config,
|
|
21
|
+
packagePath = '',
|
|
22
|
+
logger = makeLogger(),
|
|
23
|
+
addonVersion,
|
|
24
|
+
}) {
|
|
20
25
|
if (!config.apiKey && !utils.general.getEnvValue('API_KEY')) {
|
|
21
|
-
throw new
|
|
26
|
+
throw new MissingApiKeyError();
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
const packageJsonPath = `${packagePath}/package.json`;
|
|
@@ -62,7 +67,9 @@ async function validateAndPopulateConfig({config, packagePath = '', logger = mak
|
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
config.agentId = `eyes-storybook/${packageVersion}
|
|
70
|
+
config.agentId = `eyes-storybook/${packageVersion} ${
|
|
71
|
+
addonVersion ? `[eyes-storybook-addon/${addonVersion}]` : ''
|
|
72
|
+
}`.trim();
|
|
66
73
|
|
|
67
74
|
if (config.runInDocker) {
|
|
68
75
|
config.puppeteerOptions = config.puppeteerOptions || {};
|