@applitools/eyes-storybook 3.55.9 → 3.56.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,44 @@
1
1
  # Changelog
2
2
 
3
+ ## [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)
4
+
5
+
6
+ ### Features
7
+
8
+ * eyes-setup script | AD-9543 ([#3075](https://github.com/Applitools-Dev/sdk/issues/3075)) ([27f15f9](https://github.com/Applitools-Dev/sdk/commit/27f15f9b54ba93c8189214a5df424b543d564120))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * 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))
14
+
15
+
16
+ ### Dependencies
17
+
18
+ * @applitools/nml-client bumped to 1.10.0
19
+ #### Features
20
+
21
+ * android multi target | AD-9868 ([#2943](https://github.com/Applitools-Dev/sdk/issues/2943)) ([808aa21](https://github.com/Applitools-Dev/sdk/commit/808aa21e489c3562b93006e2e26ff7ffbb743dd6))
22
+
23
+
24
+
25
+ * @applitools/core-base bumped to 1.26.0
26
+ #### Features
27
+
28
+ * batch properties limit | FLD-3174 ([#3080](https://github.com/Applitools-Dev/sdk/issues/3080)) ([feb9e79](https://github.com/Applitools-Dev/sdk/commit/feb9e79d79f5eab3c58eac2b4ef3c15a562f079c))
29
+ * @applitools/ec-client bumped to 1.11.1
30
+
31
+ * @applitools/core bumped to 4.43.0
32
+ #### Features
33
+
34
+ * android multi target | AD-9868 ([#2943](https://github.com/Applitools-Dev/sdk/issues/2943)) ([808aa21](https://github.com/Applitools-Dev/sdk/commit/808aa21e489c3562b93006e2e26ff7ffbb743dd6))
35
+ * batch properties limit | FLD-3174 ([#3080](https://github.com/Applitools-Dev/sdk/issues/3080)) ([feb9e79](https://github.com/Applitools-Dev/sdk/commit/feb9e79d79f5eab3c58eac2b4ef3c15a562f079c))
36
+
37
+
38
+
39
+ * @applitools/eyes bumped to 1.35.3
40
+
41
+
3
42
  ## [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
43
 
5
44
 
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require('../src/eyes-setup');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applitools/eyes-storybook",
3
- "version": "3.55.9",
3
+ "version": "3.56.0",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "applitools",
@@ -14,7 +14,10 @@
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
+ "bin": {
18
+ "eyes-setup": "./bin/eyes-setup.js",
19
+ "eyes-storybook": "./bin/eyes-storybook.js"
20
+ },
18
21
  "files": [
19
22
  "src",
20
23
  "bin",
@@ -53,15 +56,16 @@
53
56
  "up:framework": "cd test/fixtures/storybook-versions/${APPLITOOLS_FRAMEWORK_VERSION} && npm ci"
54
57
  },
55
58
  "dependencies": {
56
- "@applitools/core": "4.42.1",
59
+ "@applitools/core": "4.43.0",
57
60
  "@applitools/driver": "1.22.1",
58
- "@applitools/eyes": "1.35.2",
61
+ "@applitools/eyes": "1.35.3",
59
62
  "@applitools/functional-commons": "1.6.0",
60
63
  "@applitools/logger": "2.1.5",
61
64
  "@applitools/monitoring-commons": "1.0.19",
62
65
  "@applitools/spec-driver-puppeteer": "1.5.1",
63
66
  "@applitools/ufg-client": "1.16.14",
64
67
  "@applitools/utils": "1.9.0",
68
+ "@inquirer/prompts": "7.0.1",
65
69
  "boxen": "4.2.0",
66
70
  "chalk": "3.0.0",
67
71
  "detect-port": "1.3.0",
@@ -0,0 +1,272 @@
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
+ let shouldUpdate = true;
44
+ let fileName;
45
+
46
+ log(chalk.yellow('Setting up Eyes-Storybook for your project:'));
47
+
48
+ let config = await loadExistingConfig();
49
+ if (config) {
50
+ shouldUpdate = false;
51
+ // this show a warning if the apiKey is actually 'process.env.APPLITOOLS_API_KEY' in the config file since we load it
52
+ // if (config.apiKey !== 'process.env.APPLITOOLS_API_KEY') {
53
+ // console.log(config.apiKey)
54
+ // log(
55
+ // chalk.yellow(
56
+ // `Detected 'apiKey' is not obscured. It is recommended to set this via environment variable APPLITOOLS_API_KEY.`,
57
+ // ),
58
+ // );
59
+ // }
60
+ }
61
+
62
+ let apiKey,
63
+ serverUrl,
64
+ shouldUseEnvApiKey = false,
65
+ shouldUseEnvServerUrl = false,
66
+ warnForApiKey = false;
67
+ if (shouldUpdate && utils.general.getEnvValue('API_KEY')) {
68
+ shouldUseEnvApiKey = await confirm({
69
+ message:
70
+ 'Detected APPLITOOLS_API_KEY environment variable. Would you like it to be used? (default Yes):',
71
+ default: true,
72
+ });
73
+ apiKey = shouldUseEnvApiKey ? 'process.env.APPLITOOLS_API_KEY' : undefined;
74
+ warnForApiKey = !shouldUseEnvApiKey; // warn if the user doesn't want to use the env variable
75
+ }
76
+ for (const url of ['EYES_SERVER_URL', 'SERVER_URL']) {
77
+ if (shouldUpdate && utils.general.getEnvValue(url)) {
78
+ shouldUseEnvServerUrl = await confirm({
79
+ message: `Detected APPLITOOLS_${url} environment variable. Would you like it to be used? (default Yes):`,
80
+ default: true,
81
+ });
82
+ serverUrl = shouldUseEnvServerUrl ? `APPLITOOLS_${url}` : undefined;
83
+ }
84
+ }
85
+
86
+ if (shouldUpdate) {
87
+ if (!shouldUseEnvApiKey) {
88
+ apiKey =
89
+ args.apiKey ??
90
+ (await input({
91
+ message:
92
+ 'Enter your API key (https://applitools.com/docs/topics/overview/obtain-api-key.html):',
93
+ }));
94
+ warnForApiKey = apiKey !== ''; // warn if the user did provide an API key
95
+ }
96
+ if (!shouldUseEnvServerUrl) {
97
+ serverUrl =
98
+ args.serverUrl ??
99
+ (await input({
100
+ message: 'Enter your Eyes server URL (default is https://eyes.applitools.com):',
101
+ }));
102
+ }
103
+
104
+ config = {
105
+ ...config,
106
+ appName: 'My Storybook App',
107
+ batchName: 'My Storybook',
108
+ showLogs: false,
109
+ };
110
+
111
+ const fileContent = generateConfigContent();
112
+ await fs.writeFile(fileName, fileContent, 'utf8');
113
+ }
114
+
115
+ printPostlude();
116
+
117
+ async function loadExistingConfig() {
118
+ const configFiles = [
119
+ 'applitools.config.js',
120
+ 'applitools.config.mjs',
121
+ 'applitools.config.cjs',
122
+ 'eyes.config.js',
123
+ ];
124
+
125
+ for (const file of configFiles) {
126
+ fileName = file;
127
+ try {
128
+ await fs.access(file);
129
+ const fileUrl = pathToFileURL(file).href;
130
+ const module = await import(fileUrl);
131
+ return module.default || module;
132
+ } catch (err) {
133
+ // try the next file if it doesn't exist
134
+ if (err.code === 'ENOENT') continue;
135
+ throw err; // we are only interested in the file not found error
136
+ }
137
+ }
138
+ // no config file found
139
+ fileName = 'applitools.config.js';
140
+ return null;
141
+ }
142
+
143
+ function generateConfigContent() {
144
+ const envConfigMap = {
145
+ batchName: 'APPLITOOLS_BATCH_NAME',
146
+ batchId: 'APPLITOOLS_BATCH_ID',
147
+ showLogs: 'APPLITOOLS_SHOW_LOGS',
148
+ batchSequenceName: 'APPLITOOLS_BATCH_SEQUENCE_NAME',
149
+ proxy: 'APPLITOOLS_PROXY',
150
+ notifyOnCompletion: 'APPLITOOLS_NOTIFY_ON_COMPLETION',
151
+ concurrentTabs: 'APPLITOOLS_CONCURRENT_TABS',
152
+ appName: 'APPLITOOLS_APP_NAME',
153
+ };
154
+
155
+ const configLines = [];
156
+
157
+ // Handle apiKey
158
+ if (apiKey === 'process.env.APPLITOOLS_API_KEY') {
159
+ configLines.push('apiKey: process.env.APPLITOOLS_API_KEY,');
160
+ } else if (apiKey) {
161
+ configLines.push(
162
+ `apiKey: '${apiKey}', // Warning: 'apiKey' is not obscured, consider setting it via environment variable APPLITOOLS_API_KEY`,
163
+ );
164
+ } else {
165
+ configLines.push(`// apiKey: '',`);
166
+ }
167
+
168
+ // Handle serverUrl
169
+ if (serverUrl && serverUrl.startsWith('process.env.')) {
170
+ configLines.push(`serverUrl: ${serverUrl},`);
171
+ } else if (serverUrl) {
172
+ configLines.push(`serverUrl: '${serverUrl}',`);
173
+ } else {
174
+ configLines.push(`// serverUrl: '',`);
175
+ }
176
+
177
+ // Handle other config lines
178
+ const otherKeys = [
179
+ 'appName',
180
+ 'batchName',
181
+ 'batchId',
182
+ 'showLogs',
183
+ 'batchSequenceName',
184
+ 'proxy',
185
+ 'notifyOnCompletion',
186
+ 'concurrentTabs',
187
+ ];
188
+
189
+ for (const key of otherKeys) {
190
+ const line = getConfigLine(key);
191
+ if (line) {
192
+ configLines.push(line);
193
+ }
194
+ }
195
+
196
+ return `/**
197
+ * @type {import('@applitools/eyes-storybook').ApplitoolsConfig}
198
+ **/
199
+ module.exports = {
200
+ ${configLines.join('\n ')}
201
+ // browsersInfo: [
202
+ // {width: 1024, height: 768, name: 'firefox'},
203
+ // {width: 1024, height: 768, name: 'chrome'},
204
+ // {iosDeviceInfo: {deviceName: 'iPhone 16'}},
205
+ // {chromeEmulationInfo: {deviceName: 'Galaxy S20'}},
206
+ // ]
207
+ };
208
+ `;
209
+
210
+ function getConfigLine(key, override = undefined) {
211
+ const envVar = envConfigMap[key];
212
+ if (override) {
213
+ return config[key];
214
+ }
215
+ if (utils.types.isArray(envVar)) {
216
+ for (const v of envVar) {
217
+ if (utils.general.getEnvValue(v.replace('APPLITOOLS_', ''))) {
218
+ return `${key}: process.env.${v},`;
219
+ }
220
+ }
221
+ } else if (envVar && utils.general.getEnvValue(envVar.replace('APPLITOOLS_', ''))) {
222
+ return `${key}: process.env.${envVar},`;
223
+ }
224
+
225
+ // Check if the key exists in the config object before using its value.
226
+ // Using Object.prototype.hasOwnProperty.call is a safe way to check.
227
+ if (Object.prototype.hasOwnProperty.call(config, key)) {
228
+ const value = config[key];
229
+ // Format the value based on its type.
230
+ return typeof value === 'string' ? `${key}: '${value}',` : `${key}: ${value},`;
231
+ } else {
232
+ return '';
233
+ }
234
+ }
235
+ }
236
+
237
+ function printPostlude() {
238
+ log('');
239
+ if (shouldUpdate) {
240
+ log(
241
+ chalk.green('✔ Success!'),
242
+ chalk.bold('\n'),
243
+ chalk.bold('Eyes Storybook is now set up!'),
244
+ chalk.bold('Please visit our Storybook documentation to learn more.'),
245
+ chalk.bold('https://applitools.com/tutorials/sdks/storybook/quickstart'),
246
+ );
247
+ } else {
248
+ log(
249
+ chalk.green('✔ Good news! Eyes Storybook is already properly configured!'),
250
+ chalk.bold('\n'),
251
+ chalk.bold('Please visit our Storybook documentation to learn more.'),
252
+ chalk.bold('https://applitools.com/tutorials/sdks/storybook/quickstart'),
253
+ );
254
+ }
255
+
256
+ if (warnForApiKey) {
257
+ log(
258
+ chalk.yellow(
259
+ 'Please consider setting your API key via environment variable APPLITOOLS_API_KEY to avoid exposing it in the config file.',
260
+ ),
261
+ );
262
+ }
263
+ }
264
+ }
265
+
266
+ function log(...args) {
267
+ console.log(...args);
268
+ }
269
+
270
+ function error(...args) {
271
+ console.error(...args);
272
+ }
@@ -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,12 @@ 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 = utils.config.getConfig({
14
+ paths: configPaths,
15
+ params: configParams,
16
+ strict: true,
17
+ traverse: false,
18
+ });
14
19
  const argvConfig = lodash.pick(argv, configParams);
15
20
  const result = Object.assign({}, defaultConfig, config, argvConfig);
16
21
 
@@ -87,12 +92,23 @@ function transformConfig(result) {
87
92
  }
88
93
 
89
94
  function transformBrowser(result) {
95
+ if (result.browsersInfo) {
96
+ transformBrowsersInfo(result.browsersInfo);
97
+ delete result.browsersInfo;
98
+ }
90
99
  if (result.browser) {
100
+ transformBrowsersInfo(result.browser);
101
+ delete result.browser;
102
+ }
103
+
104
+ return result;
105
+
106
+ function transformBrowsersInfo(info) {
91
107
  result.environments = [];
92
- if (!Array.isArray(result.browser)) {
93
- result.browser = [result.browser];
108
+ if (!utils.types.isArray(info)) {
109
+ info = [info];
94
110
  }
95
- result.environments = result.browser.map(browser => {
111
+ result.environments = info.map(browser => {
96
112
  if (browser.deviceName) {
97
113
  return {chromeEmulationInfo: browser};
98
114
  } else if (utils.types.has(browser, 'iosDeviceInfo')) {
@@ -104,8 +120,6 @@ function transformBrowser(result) {
104
120
  return browser;
105
121
  });
106
122
  }
107
- delete result.browser;
108
- return result;
109
123
  }
110
124
 
111
125
  function transformLayoutBreakpoints(result) {
@@ -0,0 +1,4 @@
1
+ import {Configuration} from '@applitools/eyes'
2
+
3
+ // should we omit more properties?
4
+ export type ApplitoolsConfig = Omit<Configuration, 'waitBeforeScreenshots'>