@applitools/eyes-storybook 3.55.9 → 3.57.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
@@ -1,5 +1,114 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.57.0](https://github.com/Applitools-Dev/sdk/compare/js/eyes-storybook@3.56.0...js/eyes-storybook@3.57.0) (2025-08-05)
4
+
5
+
6
+ ### Features
7
+
8
+ * improve configuration handling ([#3130](https://github.com/Applitools-Dev/sdk/issues/3130)) ([def7be1](https://github.com/Applitools-Dev/sdk/commit/def7be1dd07460f49142cddfe55203baa884e6c3))
9
+ * release java ([7bc39e6](https://github.com/Applitools-Dev/sdk/commit/7bc39e679eab27a19322ca4b121177da7437c106))
10
+
11
+
12
+ ### Dependencies
13
+
14
+ * @applitools/utils bumped to 1.11.0
15
+ #### Features
16
+
17
+ * improve configuration handling ([#3130](https://github.com/Applitools-Dev/sdk/issues/3130)) ([def7be1](https://github.com/Applitools-Dev/sdk/commit/def7be1dd07460f49142cddfe55203baa884e6c3))
18
+ * make utils.general.guid crypto secured ([#3137](https://github.com/Applitools-Dev/sdk/issues/3137)) ([775df08](https://github.com/Applitools-Dev/sdk/commit/775df08307e41402a6603812205bc857bd3f936e))
19
+ * @applitools/eyes bumped to 1.36.0
20
+ #### Features
21
+
22
+ * improve configuration handling ([#3130](https://github.com/Applitools-Dev/sdk/issues/3130)) ([def7be1](https://github.com/Applitools-Dev/sdk/commit/def7be1dd07460f49142cddfe55203baa884e6c3))
23
+ * release java ([7bc39e6](https://github.com/Applitools-Dev/sdk/commit/7bc39e679eab27a19322ca4b121177da7437c106))
24
+
25
+
26
+
27
+ * @applitools/snaptdout bumped to 1.1.0
28
+ #### Features
29
+
30
+ * release java ([7bc39e6](https://github.com/Applitools-Dev/sdk/commit/7bc39e679eab27a19322ca4b121177da7437c106))
31
+ * @applitools/test-server bumped to 1.3.0
32
+ #### Features
33
+
34
+ * release java ([7bc39e6](https://github.com/Applitools-Dev/sdk/commit/7bc39e679eab27a19322ca4b121177da7437c106))
35
+
36
+
37
+
38
+ * @applitools/logger bumped to 2.2.1
39
+
40
+ * @applitools/dom-snapshot bumped to 4.13.1
41
+
42
+ * @applitools/socket bumped to 1.3.1
43
+
44
+ * @applitools/req bumped to 1.8.1
45
+
46
+ * @applitools/image bumped to 1.2.1
47
+
48
+ * @applitools/dom-capture bumped to 11.6.1
49
+
50
+ * @applitools/driver bumped to 1.23.1
51
+
52
+ * @applitools/spec-driver-webdriver bumped to 1.4.1
53
+
54
+ * @applitools/spec-driver-selenium bumped to 1.7.1
55
+
56
+ * @applitools/spec-driver-puppeteer bumped to 1.6.1
57
+
58
+ * @applitools/screenshoter bumped to 3.12.1
59
+
60
+ * @applitools/nml-client bumped to 1.11.1
61
+
62
+ * @applitools/tunnel-client bumped to 1.10.2
63
+
64
+ * @applitools/ufg-client bumped to 1.17.1
65
+
66
+ * @applitools/core-base bumped to 1.27.1
67
+
68
+ * @applitools/ec-client bumped to 1.12.2
69
+
70
+ * @applitools/core bumped to 4.44.2
71
+
72
+
73
+ ## [3.56.0](https://github.com/Applitools-Dev/sdk/compare/js/eyes-storybook@3.55.9...js/eyes-storybook@3.56.0) (2025-07-23)
74
+
75
+
76
+ ### Features
77
+
78
+ * eyes-setup script | AD-9543 ([#3075](https://github.com/Applitools-Dev/sdk/issues/3075)) ([27f15f9](https://github.com/Applitools-Dev/sdk/commit/27f15f9b54ba93c8189214a5df424b543d564120))
79
+
80
+
81
+ ### Bug Fixes
82
+
83
+ * use strict mode to get config | AD-10156 ([#3092](https://github.com/Applitools-Dev/sdk/issues/3092)) ([6b2d21b](https://github.com/Applitools-Dev/sdk/commit/6b2d21b23219f03b56d27604d0afe768eaae6fc2))
84
+
85
+
86
+ ### Dependencies
87
+
88
+ * @applitools/nml-client bumped to 1.10.0
89
+ #### Features
90
+
91
+ * android multi target | AD-9868 ([#2943](https://github.com/Applitools-Dev/sdk/issues/2943)) ([808aa21](https://github.com/Applitools-Dev/sdk/commit/808aa21e489c3562b93006e2e26ff7ffbb743dd6))
92
+
93
+
94
+
95
+ * @applitools/core-base bumped to 1.26.0
96
+ #### Features
97
+
98
+ * batch properties limit | FLD-3174 ([#3080](https://github.com/Applitools-Dev/sdk/issues/3080)) ([feb9e79](https://github.com/Applitools-Dev/sdk/commit/feb9e79d79f5eab3c58eac2b4ef3c15a562f079c))
99
+ * @applitools/ec-client bumped to 1.11.1
100
+
101
+ * @applitools/core bumped to 4.43.0
102
+ #### Features
103
+
104
+ * android multi target | AD-9868 ([#2943](https://github.com/Applitools-Dev/sdk/issues/2943)) ([808aa21](https://github.com/Applitools-Dev/sdk/commit/808aa21e489c3562b93006e2e26ff7ffbb743dd6))
105
+ * batch properties limit | FLD-3174 ([#3080](https://github.com/Applitools-Dev/sdk/issues/3080)) ([feb9e79](https://github.com/Applitools-Dev/sdk/commit/feb9e79d79f5eab3c58eac2b4ef3c15a562f079c))
106
+
107
+
108
+
109
+ * @applitools/eyes bumped to 1.35.3
110
+
111
+
3
112
  ## [3.55.9](https://github.com/Applitools-Dev/sdk/compare/js/eyes-storybook@3.55.8...js/eyes-storybook@3.55.9) (2025-07-15)
4
113
 
5
114
 
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require('../src/eyes-setup');
@@ -0,0 +1,39 @@
1
+ import type { Configuration, DesktopBrowserInfo, ChromeEmulationInfo, IOSDeviceInfo, IOSMultiDeviceInfo } from '@applitools/eyes';
2
+ /**
3
+ * https://applitools.com/tutorials/sdks/storybook/config
4
+ */
5
+ export type ApplitoolsConfig = Omit<Configuration, 'waitBeforeScreenshots'> & {
6
+ storybookUrl?: string;
7
+ storybookPort?: number;
8
+ storybookHost?: string;
9
+ storybookConfigDir?: string;
10
+ storybookStaticDir?: string;
11
+ showStorybookOutput?: boolean;
12
+ runInDocker?: boolean;
13
+ include?: ((story: {
14
+ name: string;
15
+ kind: string;
16
+ storyTitle?: string;
17
+ parameters?: any;
18
+ }) => boolean) | string | RegExp;
19
+ variations?: Record<string, any>;
20
+ readStoriesTimeout?: number;
21
+ puppeteerOptions?: Record<string, any>;
22
+ puppeteerExtraHTTPHeaders?: Record<string, string>;
23
+ navigationWaitUntil?: 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
24
+ browserCacheRequests?: boolean;
25
+ browser?: (DesktopBrowserInfo | ChromeEmulationInfo | IOSDeviceInfo | IOSMultiDeviceInfo)[];
26
+ envName?: string;
27
+ testConcurrency?: number;
28
+ showLogs?: boolean;
29
+ exitcode?: boolean;
30
+ ignoreRegions?: any[];
31
+ floatingRegions?: any[];
32
+ layoutRegions?: any[];
33
+ strictRegions?: any[];
34
+ contentRegions?: any[];
35
+ accessibilityRegions?: any[];
36
+ jsonFilePath?: string;
37
+ tapFilePath?: string;
38
+ xmlFilePath?: string;
39
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applitools/eyes-storybook",
3
- "version": "3.55.9",
3
+ "version": "3.57.0",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "applitools",
@@ -14,7 +14,11 @@
14
14
  "homepage": "https://applitools.com/tutorials/sdks/storybook",
15
15
  "license": "SEE LICENSE IN LICENSE",
16
16
  "author": "Applitools Team <team@applitools.com>",
17
- "bin": "./bin/eyes-storybook.js",
17
+ "types": "dist/index.d.ts",
18
+ "bin": {
19
+ "eyes-setup": "./bin/eyes-setup.js",
20
+ "eyes-storybook": "./bin/eyes-storybook.js"
21
+ },
18
22
  "files": [
19
23
  "src",
20
24
  "bin",
@@ -22,7 +26,8 @@
22
26
  ],
23
27
  "scripts": {
24
28
  "lint": "run --top-level eslint '**/*.js'",
25
- "build": "rollup -c rollup.config.js",
29
+ "build:types": "tsc --project tsconfig.types.json",
30
+ "build": "rollup -c rollup.config.js && yarn build:types",
26
31
  "build:heavy": "node scripts/bitmap.js",
27
32
  "test": "yarn build && yarn test:mocha",
28
33
  "test:mocha": "run --top-level mocha 'test/{unit,it,e2e}/*.test.js'",
@@ -53,15 +58,16 @@
53
58
  "up:framework": "cd test/fixtures/storybook-versions/${APPLITOOLS_FRAMEWORK_VERSION} && npm ci"
54
59
  },
55
60
  "dependencies": {
56
- "@applitools/core": "4.42.1",
57
- "@applitools/driver": "1.22.1",
58
- "@applitools/eyes": "1.35.2",
61
+ "@applitools/core": "4.44.2",
62
+ "@applitools/driver": "1.23.1",
63
+ "@applitools/eyes": "1.36.0",
59
64
  "@applitools/functional-commons": "1.6.0",
60
- "@applitools/logger": "2.1.5",
65
+ "@applitools/logger": "2.2.1",
61
66
  "@applitools/monitoring-commons": "1.0.19",
62
- "@applitools/spec-driver-puppeteer": "1.5.1",
63
- "@applitools/ufg-client": "1.16.14",
64
- "@applitools/utils": "1.9.0",
67
+ "@applitools/spec-driver-puppeteer": "1.6.1",
68
+ "@applitools/ufg-client": "1.17.1",
69
+ "@applitools/utils": "1.11.0",
70
+ "@inquirer/prompts": "7.0.1",
65
71
  "boxen": "4.2.0",
66
72
  "chalk": "3.0.0",
67
73
  "detect-port": "1.3.0",
@@ -75,8 +81,8 @@
75
81
  },
76
82
  "devDependencies": {
77
83
  "@applitools/bongo": "^5.10.0",
78
- "@applitools/snaptdout": "^1.0.0",
79
- "@applitools/test-server": "^1.2.3",
84
+ "@applitools/snaptdout": "^1.1.0",
85
+ "@applitools/test-server": "^1.3.0",
80
86
  "@applitools/test-utils": "^1.5.17",
81
87
  "@storybook/addon-interactions": "^6.4.18",
82
88
  "@storybook/react": "^6.5.16",
@@ -92,6 +98,7 @@
92
98
  "react-dom": "^16.12.0",
93
99
  "rollup": "^1.28.0",
94
100
  "rollup-plugin-commonjs": "^10.1.0",
101
+ "typescript": "^5.3.3",
95
102
  "ua-parser-js": "^0.7.21"
96
103
  },
97
104
  "engines": {
@@ -0,0 +1,269 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs').promises;
4
+ const {input, confirm} = require('@inquirer/prompts');
5
+ const yargs = require('yargs/yargs');
6
+ const chalk = require('chalk');
7
+ const {pathToFileURL} = require('url');
8
+ const utils = require('@applitools/utils');
9
+
10
+ const {hideBin} = require('yargs/helpers');
11
+ yargs(hideBin(process.argv))
12
+ .command({
13
+ command: '*',
14
+ builder: yargs => {
15
+ const args = yargs.options({
16
+ apiKey: {
17
+ describe:
18
+ "Your Applitools Team's API key (here's how to obtain it: https://applitools.com/docs/topics/overview/obtain-api-key.html)",
19
+ type: 'string',
20
+ },
21
+ serverUrl: {describe: 'Your Eyes dedicated cloud server URL', type: 'string'},
22
+ });
23
+ return args;
24
+ },
25
+ handler: handleError(init),
26
+ })
27
+ .help()
28
+ .parse();
29
+
30
+ function handleError(asyncFunc) {
31
+ return async (...args) =>
32
+ asyncFunc(...args).catch(err => {
33
+ if (err.name === 'ExitPromptError') {
34
+ log('See you next time!');
35
+ } else {
36
+ error(`Error while setting up Eyes: ${err.message}`);
37
+ }
38
+ process.exit(1);
39
+ });
40
+ }
41
+
42
+ async function init(args) {
43
+ log(chalk.yellow('Setting up Eyes-Storybook for your project:'));
44
+
45
+ let {config, fileName, isCorrupted} = await loadExistingConfig();
46
+
47
+ if (isCorrupted) {
48
+ log(chalk.yellow(`\nWarning: Your configuration file '${fileName}' appears to be corrupted.`));
49
+ const shouldOverwrite = await confirm({
50
+ message:
51
+ 'Would you like to create a new one? This will replace the corrupted file (Default Yes):',
52
+ default: true,
53
+ });
54
+
55
+ if (!shouldOverwrite) {
56
+ log('Aborting setup. Please fix your configuration file manually or remove it.');
57
+ process.exit(0);
58
+ }
59
+ // By letting this block finish, the script proceeds as if no config was found
60
+ }
61
+
62
+ // If no config exists or is corrupted, run the interactive setup
63
+ let apiKey,
64
+ serverUrl,
65
+ warnForApiKey = false;
66
+
67
+ let shouldUpdate = !config;
68
+ if (shouldUpdate) {
69
+ let shouldUseEnvApiKey = false;
70
+ if (utils.general.getEnvValue('API_KEY')) {
71
+ shouldUseEnvApiKey = await confirm({
72
+ message:
73
+ 'Detected APPLITOOLS_API_KEY environment variable. Would you like it to be used? (default Yes):',
74
+ default: true,
75
+ });
76
+ if (shouldUseEnvApiKey) {
77
+ apiKey = 'process.env.APPLITOOLS_API_KEY';
78
+ }
79
+ warnForApiKey = !shouldUseEnvApiKey; // Warn if the user has an env var but doesn't use it
80
+ }
81
+
82
+ let shouldUseEnvServerUrl = false;
83
+ for (const url of ['EYES_SERVER_URL', 'SERVER_URL']) {
84
+ if (utils.general.getEnvValue(url)) {
85
+ shouldUseEnvServerUrl = await confirm({
86
+ message: `Detected APPLITOOLS_${url} environment variable. Would you like it to be used? (default Yes):`,
87
+ default: true,
88
+ });
89
+ if (shouldUseEnvServerUrl) {
90
+ serverUrl = `process.env.APPLITOOLS_${url}`;
91
+ break; // Use the first one found
92
+ }
93
+ }
94
+ }
95
+
96
+ if (!shouldUseEnvApiKey) {
97
+ apiKey =
98
+ args.apiKey ??
99
+ (await input({
100
+ message:
101
+ 'Enter your API key (https://applitools.com/docs/topics/overview/obtain-api-key.html):',
102
+ }));
103
+ warnForApiKey = apiKey !== ''; // Warn if the user provided any API key directly
104
+ }
105
+
106
+ if (!shouldUseEnvServerUrl) {
107
+ serverUrl =
108
+ args.serverUrl ??
109
+ (await input({
110
+ message: 'Enter your Eyes server URL (default is https://eyes.applitools.com):',
111
+ }));
112
+ }
113
+
114
+ config = {
115
+ ...(config || {}),
116
+ appName: 'My Storybook App',
117
+ batchName: 'My Storybook',
118
+ showLogs: false,
119
+ };
120
+
121
+ const fileContent = generateConfigContent();
122
+ await fs.writeFile(fileName, fileContent, 'utf8');
123
+ }
124
+
125
+ printPostlude();
126
+
127
+ // --- Helper Functions ---
128
+
129
+ async function loadExistingConfig() {
130
+ const configFiles = [
131
+ 'applitools.config.js',
132
+ 'applitools.config.mjs',
133
+ 'applitools.config.cjs',
134
+ 'eyes.config.js',
135
+ ];
136
+
137
+ for (const file of configFiles) {
138
+ try {
139
+ const fileUrl = pathToFileURL(file).href;
140
+ const module = await import(`${fileUrl}?t=${Date.now()}`); // Append timestamp to bypass cache
141
+
142
+ return {config: module.default || module, fileName: file, isCorrupted: false};
143
+ } catch (err) {
144
+ if (err.code === 'ERR_MODULE_NOT_FOUND') {
145
+ // This file doesn't exist, so we continue searching for the one that does.
146
+ continue;
147
+ }
148
+
149
+ return {config: null, fileName: file, isCorrupted: true};
150
+ }
151
+ }
152
+
153
+ // No file was found in any of the possible locations.
154
+ // We return a default filename for a new file to be created.
155
+ return {config: null, fileName: 'applitools.config.js', isCorrupted: false};
156
+ }
157
+
158
+ function generateConfigContent() {
159
+ const envConfigMap = {
160
+ batchName: 'APPLITOOLS_BATCH_NAME',
161
+ batchId: 'APPLITOOLS_BATCH_ID',
162
+ showLogs: 'APPLITOOLS_SHOW_LOGS',
163
+ batchSequenceName: 'APPLITOOLS_BATCH_SEQUENCE_NAME',
164
+ proxy: 'APPLITOOLS_PROXY',
165
+ notifyOnCompletion: 'APPLITOOLS_NOTIFY_ON_COMPLETION',
166
+ concurrentTabs: 'APPLITOOLS_CONCURRENT_TABS',
167
+ appName: 'APPLITOOLS_APP_NAME',
168
+ };
169
+
170
+ const configLines = [];
171
+
172
+ // Handle apiKey
173
+ if (apiKey === 'process.env.APPLITOOLS_API_KEY') {
174
+ configLines.push('apiKey: process.env.APPLITOOLS_API_KEY,');
175
+ } else if (apiKey) {
176
+ configLines.push(
177
+ `apiKey: '${apiKey}', // Warning: 'apiKey' is not obscured, consider setting it via environment variable APPLITOOLS_API_KEY`,
178
+ );
179
+ } else {
180
+ configLines.push(`// apiKey: '',`);
181
+ }
182
+
183
+ // Handle serverUrl
184
+ if (serverUrl && serverUrl.startsWith('process.env.')) {
185
+ configLines.push(`serverUrl: ${serverUrl},`);
186
+ } else if (serverUrl) {
187
+ configLines.push(`serverUrl: '${serverUrl}',`);
188
+ }
189
+
190
+ // Handle other config lines by prioritizing env vars over defaults
191
+ const otherKeys = [
192
+ 'appName',
193
+ 'batchName',
194
+ 'batchId',
195
+ 'showLogs',
196
+ 'batchSequenceName',
197
+ 'proxy',
198
+ 'notifyOnCompletion',
199
+ 'concurrentTabs',
200
+ 'navigationWaitUntil',
201
+ 'waitBeforeCapture',
202
+ ];
203
+
204
+ for (const key of otherKeys) {
205
+ const envVar = envConfigMap[key];
206
+ // Check if an environment variable for this key is set
207
+ if (envVar && utils.general.getEnvValue(envVar.replace('APPLITOOLS_', ''))) {
208
+ configLines.push(`${key}: process.env.${envVar},`);
209
+ } else if (Object.prototype.hasOwnProperty.call(config, key)) {
210
+ // Otherwise, use the default value from the config object
211
+ const value = config[key];
212
+ if (value !== undefined) {
213
+ const line = typeof value === 'string' ? `${key}: '${value}',` : `${key}: ${value},`;
214
+ configLines.push(line);
215
+ }
216
+ }
217
+ }
218
+
219
+ return `/**
220
+ * @type {import('@applitools/eyes-storybook').ApplitoolsConfig}
221
+ **/
222
+ module.exports = {
223
+ ${configLines.join('\n ')}
224
+ // browsersInfo: [
225
+ // {width: 1024, height: 768, name: 'firefox'},
226
+ // {width: 1024, height: 768, name: 'chrome'},
227
+ // {iosDeviceInfo: {deviceName: 'iPhone 16'}},
228
+ // {chromeEmulationInfo: {deviceName: 'Galaxy S20'}},
229
+ // ]
230
+ };
231
+ `;
232
+ }
233
+
234
+ function printPostlude() {
235
+ log('');
236
+ if (shouldUpdate) {
237
+ log(
238
+ chalk.green('✔ Success!'),
239
+ chalk.bold('\n'),
240
+ chalk.bold('Eyes Storybook is now set up!'),
241
+ chalk.bold('Please visit our Storybook documentation to learn more.'),
242
+ chalk.bold('https://applitools.com/tutorials/sdks/storybook/quickstart'),
243
+ );
244
+ } else {
245
+ log(
246
+ chalk.green('✔ Good news! Eyes Storybook is already properly configured!'),
247
+ chalk.bold('\n'),
248
+ chalk.bold('Please visit our Storybook documentation to learn more.'),
249
+ chalk.bold('https://applitools.com/tutorials/sdks/storybook/quickstart'),
250
+ );
251
+ }
252
+
253
+ if (warnForApiKey) {
254
+ log(
255
+ chalk.yellow(
256
+ '\nWarning: Consider setting your API key via the APPLITOOLS_API_KEY environment variable.',
257
+ ),
258
+ );
259
+ }
260
+ }
261
+ }
262
+
263
+ function log(...args) {
264
+ console.log(...args);
265
+ }
266
+
267
+ function error(...args) {
268
+ console.error(...args);
269
+ }
@@ -91,7 +91,11 @@ async function eyesStorybook({
91
91
  },
92
92
  })
93
93
  .catch(async error => {
94
- if (error && error.message && error.message.includes('Unauthorized(401)')) {
94
+ if (
95
+ error &&
96
+ error.message &&
97
+ error.message.includes('Please check your API key and try again.')
98
+ ) {
95
99
  const failMsg = 'Incorrect API Key';
96
100
  logger.log(failMsg);
97
101
  await browser.close();
@@ -10,7 +10,7 @@ function generateConfig({argv = {}, defaultConfig = {}, externalConfigParams = [
10
10
  const defaultConfigParams = Object.keys(defaultConfig);
11
11
  const configPaths = argv.conf ? [resolve(process.cwd(), argv.conf)] : undefined;
12
12
  const configParams = uniq(defaultConfigParams.concat(externalConfigParams));
13
- const config = utils.config.getConfig({paths: configPaths, params: configParams});
13
+ const config = getAndParseConfig({configPaths, configParams});
14
14
  const argvConfig = lodash.pick(argv, configParams);
15
15
  const result = Object.assign({}, defaultConfig, config, argvConfig);
16
16
 
@@ -81,18 +81,50 @@ function generateConfig({argv = {}, defaultConfig = {}, externalConfigParams = [
81
81
  return result;
82
82
  }
83
83
 
84
+ function getAndParseConfig({configPaths, configParams}) {
85
+ try {
86
+ return utils.config.getConfig({
87
+ paths: configPaths,
88
+ params: configParams,
89
+ strict: true,
90
+ traverse: false,
91
+ });
92
+ } catch (error) {
93
+ if (error.message.includes('Could not find configuration file')) {
94
+ return utils.config.populateConfigParams({config: {}, params: configParams});
95
+ }
96
+ const documentationUrl = 'https://applitools.com/tutorials/sdks/storybook/config#properties';
97
+ throw new Error(
98
+ `Your configuration file is invalid. Please review our documentation for valid configuration settings: ${documentationUrl}.
99
+ Additionally, you can generate a new configuration by running 'npx eyes-setup'.
100
+ \n\nError details: ${error.message}`.trim(),
101
+ );
102
+ }
103
+ }
104
+
84
105
  function transformConfig(result) {
85
106
  transformLayoutBreakpoints(result);
86
107
  transformBrowser(result);
87
108
  }
88
109
 
89
110
  function transformBrowser(result) {
111
+ if (result.browsersInfo) {
112
+ transformBrowsersInfo(result.browsersInfo);
113
+ delete result.browsersInfo;
114
+ }
90
115
  if (result.browser) {
116
+ transformBrowsersInfo(result.browser);
117
+ delete result.browser;
118
+ }
119
+
120
+ return result;
121
+
122
+ function transformBrowsersInfo(info) {
91
123
  result.environments = [];
92
- if (!Array.isArray(result.browser)) {
93
- result.browser = [result.browser];
124
+ if (!utils.types.isArray(info)) {
125
+ info = [info];
94
126
  }
95
- result.environments = result.browser.map(browser => {
127
+ result.environments = info.map(browser => {
96
128
  if (browser.deviceName) {
97
129
  return {chromeEmulationInfo: browser};
98
130
  } else if (utils.types.has(browser, 'iosDeviceInfo')) {
@@ -104,8 +136,6 @@ function transformBrowser(result) {
104
136
  return browser;
105
137
  });
106
138
  }
107
- delete result.browser;
108
- return result;
109
139
  }
110
140
 
111
141
  function transformLayoutBreakpoints(result) {
package/src/index.ts ADDED
@@ -0,0 +1,35 @@
1
+ import type {Configuration, DesktopBrowserInfo, ChromeEmulationInfo, IOSDeviceInfo, IOSMultiDeviceInfo} from '@applitools/eyes'
2
+
3
+ /**
4
+ * https://applitools.com/tutorials/sdks/storybook/config
5
+ */
6
+ export type ApplitoolsConfig = Omit<Configuration, 'waitBeforeScreenshots'> & {
7
+ storybookUrl?: string;
8
+ storybookPort?: number;
9
+ storybookHost?: string;
10
+ storybookConfigDir?: string;
11
+ storybookStaticDir?: string;
12
+ showStorybookOutput?: boolean;
13
+ runInDocker?: boolean;
14
+ include?: ((story: { name: string; kind: string; storyTitle?: string; parameters?: any }) => boolean) | string | RegExp;
15
+ variations?: Record<string, any>;
16
+ readStoriesTimeout?: number;
17
+ puppeteerOptions?: Record<string, any>;
18
+ puppeteerExtraHTTPHeaders?: Record<string, string>;
19
+ navigationWaitUntil?: 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
20
+ browserCacheRequests?: boolean;
21
+ browser?: (DesktopBrowserInfo | ChromeEmulationInfo | IOSDeviceInfo | IOSMultiDeviceInfo)[];
22
+ envName?: string;
23
+ testConcurrency?: number;
24
+ showLogs?: boolean;
25
+ exitcode?: boolean;
26
+ ignoreRegions?: any[];
27
+ floatingRegions?: any[];
28
+ layoutRegions?: any[];
29
+ strictRegions?: any[];
30
+ contentRegions?: any[];
31
+ accessibilityRegions?: any[];
32
+ jsonFilePath?: string;
33
+ tapFilePath?: string;
34
+ xmlFilePath?: string;
35
+ }
@@ -0,0 +1,116 @@
1
+ 'use strict';
2
+ const chalk = require('chalk');
3
+ const {Configuration} = require('@applitools/eyes');
4
+
5
+ // Get all keys directly from the base Configuration class
6
+ const dynamicBaseKeys = [
7
+ ...Object.getOwnPropertyNames(Object.getPrototypeOf(new Configuration())),
8
+ ].filter(
9
+ prop =>
10
+ !prop.startsWith('get') &&
11
+ !prop.startsWith('set') &&
12
+ !prop.startsWith('to') &&
13
+ !prop.startsWith('_') &&
14
+ prop !== 'constructor',
15
+ );
16
+
17
+ // known aliases that are not on the Configuration prototype
18
+ const knownAliases = [
19
+ 'envName',
20
+ 'testConcurrency',
21
+ 'concurrency',
22
+ 'eyesServerUrl',
23
+ 'batchId',
24
+ 'batchName',
25
+ 'batchSequenceName',
26
+ 'keepBatchOpen',
27
+ 'environments',
28
+ ];
29
+
30
+ const applitoolsBaseKeys = [...dynamicBaseKeys, ...knownAliases];
31
+
32
+ // A list of all keys specific to the eyes-storybook package.
33
+ const storybookSpecificKeys = [
34
+ 'storybookPort',
35
+ 'storybookHost',
36
+ 'storybookConfigDir',
37
+ 'storybookUrl',
38
+ 'storybookStaticDir',
39
+ 'showStorybookOutput',
40
+ 'exitcode',
41
+ 'include',
42
+ 'variations',
43
+ 'runInDocker',
44
+ 'readStoriesTimeout',
45
+ 'reloadPagePerStory',
46
+ 'startStorybookServerTimeout',
47
+
48
+ // Browser & Puppeteer Control
49
+ 'browser',
50
+ 'puppeteerOptions',
51
+ 'puppeteerExtraHTTPHeaders',
52
+ 'navigationWaitUntil',
53
+ 'networkBlockPatterns',
54
+ 'browserRequestsTimeout',
55
+ 'browserHeadersOverride',
56
+ 'browserCacheRequests',
57
+
58
+ // Region Matching
59
+ 'ignoreRegions',
60
+ 'floatingRegions',
61
+ 'layoutRegions',
62
+ 'strictRegions',
63
+ 'contentRegions',
64
+ 'accessibilityRegions',
65
+
66
+ // Reporting
67
+ 'tapFilePath',
68
+ 'xmlFilePath',
69
+ 'jsonFilePath',
70
+
71
+ // Legacy & Internal
72
+ 'waitBeforeScreenshot', // backward compatibility
73
+ 'waitBeforeScreenshots', // backward compatibility
74
+ 'waitBeforeCapture',
75
+ 'fakeIE',
76
+ 'storyConfiguration',
77
+ 'storyDataGap',
78
+ 'packagePath',
79
+ ];
80
+
81
+ // Combine all known keys into a single Set for efficient O(1) lookups.
82
+ const knownKeys = new Set([...storybookSpecificKeys, ...applitoolsBaseKeys]);
83
+
84
+ /**
85
+ * Checks the user's config object for any keys that are not recognized
86
+ * and logs a warning to the console if any are found.
87
+ * @param {object} config - The user's configuration object.
88
+ * @param {object} logger - The logger instance.
89
+ */
90
+ function logUnrecognizedKeys(config, logger) {
91
+ const unrecognizedKeys = Object.keys(config).filter(key => !knownKeys.has(key));
92
+
93
+ if (unrecognizedKeys.length > 0) {
94
+ // uses a warning sign emoji - ⚠️
95
+ console.log(
96
+ chalk.yellow(
97
+ `\n\u26A0 The following configuration keys are unrecognized and will be ignored:`,
98
+ ),
99
+ );
100
+ unrecognizedKeys.forEach(key => {
101
+ console.log(chalk.yellow(` - ${key}`));
102
+ });
103
+ logger.log('\n');
104
+
105
+ const docsUrl = 'https://applitools.com/tutorials/sdks/storybook/config#properties';
106
+ console.log(
107
+ chalk.bold(`Please review our documentation for valid configuration settings: ${docsUrl}`),
108
+ );
109
+ }
110
+ }
111
+
112
+ module.exports = {
113
+ logUnrecognizedKeys,
114
+ // Exporting the set is optional but can be useful for other utilities
115
+ knownKeys,
116
+ };
@@ -13,9 +13,11 @@ const startStorybookServer = require('./startStorybookServer');
13
13
  const {isIE} = require('./shouldRenderIE');
14
14
  const {makeLogger} = require('@applitools/logger');
15
15
  const determineStorybookVersion = require('./utils/determineStorybookVersion');
16
+ const {logUnrecognizedKeys} = require('./utils/config-validator');
17
+ const utils = require('@applitools/utils');
16
18
 
17
19
  async function validateAndPopulateConfig({config, packagePath = '', logger = makeLogger()}) {
18
- if (!config.apiKey) {
20
+ if (!config.apiKey && !utils.general.getEnvValue('API_KEY')) {
19
21
  throw new Error(missingApiKeyFailMsg);
20
22
  }
21
23
 
@@ -77,5 +79,8 @@ async function validateAndPopulateConfig({config, packagePath = '', logger = mak
77
79
  ),
78
80
  );
79
81
  }
82
+
83
+ logUnrecognizedKeys(config, logger);
80
84
  }
85
+
81
86
  module.exports = validateAndPopulateConfig;