@applitools/core 4.38.1 → 4.39.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,89 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.39.0](https://github.com/Applitools-Dev/sdk/compare/js/core@4.38.2...js/core@4.39.0) (2025-05-14)
4
+
5
+
6
+ ### Features
7
+
8
+ * add logging for offline execution startup with system details ([#2971](https://github.com/Applitools-Dev/sdk/issues/2971)) ([82eb5cd](https://github.com/Applitools-Dev/sdk/commit/82eb5cdffe5cf1f88f20e62d50f43600b0a06361))
9
+ * mac arm binary | FLD-3010 ([#2950](https://github.com/Applitools-Dev/sdk/issues/2950)) ([0cb7f59](https://github.com/Applitools-Dev/sdk/commit/0cb7f59b851c746e12b3a8a210a505adf3230903))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * missing resources problem ([#2976](https://github.com/Applitools-Dev/sdk/issues/2976)) ([ab0d217](https://github.com/Applitools-Dev/sdk/commit/ab0d217d23a1df4ad24618a9bf00dc84c57cd8bc))
15
+
16
+
17
+ ### Dependencies
18
+
19
+ * @applitools/core-base bumped to 1.25.3
20
+ #### Performance Improvements
21
+
22
+ * batchify log event ([#2970](https://github.com/Applitools-Dev/sdk/issues/2970)) ([9b1556f](https://github.com/Applitools-Dev/sdk/commit/9b1556f0c9ddc64a52b8b3f928c18e35f6fbbc17))
23
+ * @applitools/ec-client bumped to 1.10.14
24
+
25
+
26
+ ## [4.38.2](https://github.com/Applitools-Dev/sdk/compare/js/core@4.38.1...js/core@4.38.2) (2025-05-11)
27
+
28
+
29
+ ### Bug Fixes
30
+
31
+ * don't kill offline exectuion when one render fails | AD-9892 ([#2962](https://github.com/Applitools-Dev/sdk/issues/2962)) ([add5c43](https://github.com/Applitools-Dev/sdk/commit/add5c43892206dfaa5bf434c45e22a467fa20266))
32
+ * remove redundant file readings and added rendering concurrency limit ([#2956](https://github.com/Applitools-Dev/sdk/issues/2956)) ([b970180](https://github.com/Applitools-Dev/sdk/commit/b97018010fdf12cb0d202192b22f643c16c569d5))
33
+
34
+
35
+ ### Performance Improvements
36
+
37
+ * avoid retry when x-applitools-dont-retry header is presented | AD-9834 ([#2959](https://github.com/Applitools-Dev/sdk/issues/2959)) ([492dff6](https://github.com/Applitools-Dev/sdk/commit/492dff6ca431e242eb85a2c87d429f8676490e1a))
38
+
39
+
40
+ ### Dependencies
41
+
42
+ * @applitools/utils bumped to 1.8.2
43
+ #### Bug Fixes
44
+
45
+ * remove redundant file readings and added rendering concurrency limit ([#2956](https://github.com/Applitools-Dev/sdk/issues/2956)) ([b970180](https://github.com/Applitools-Dev/sdk/commit/b97018010fdf12cb0d202192b22f643c16c569d5))
46
+ * @applitools/ufg-client bumped to 1.16.11
47
+ #### Bug Fixes
48
+
49
+ * remove redundant file readings and added rendering concurrency limit ([#2956](https://github.com/Applitools-Dev/sdk/issues/2956)) ([b970180](https://github.com/Applitools-Dev/sdk/commit/b97018010fdf12cb0d202192b22f643c16c569d5))
50
+
51
+
52
+
53
+ * @applitools/logger bumped to 2.1.4
54
+
55
+ * @applitools/dom-snapshot bumped to 4.11.21
56
+
57
+ * @applitools/socket bumped to 1.2.5
58
+
59
+ * @applitools/req bumped to 1.7.12
60
+
61
+ * @applitools/image bumped to 1.1.20
62
+
63
+ * @applitools/driver bumped to 1.21.4
64
+
65
+ * @applitools/spec-driver-webdriver bumped to 1.2.5
66
+
67
+ * @applitools/spec-driver-selenium bumped to 1.5.101
68
+
69
+ * @applitools/spec-driver-puppeteer bumped to 1.4.30
70
+
71
+ * @applitools/screenshoter bumped to 3.11.4
72
+
73
+ * @applitools/nml-client bumped to 1.9.4
74
+
75
+ * @applitools/tunnel-client bumped to 1.6.7
76
+
77
+ * @applitools/core-base bumped to 1.25.2
78
+ #### Performance Improvements
79
+
80
+ * avoid retry when x-applitools-dont-retry header is presented | AD-9834 ([#2959](https://github.com/Applitools-Dev/sdk/issues/2959)) ([492dff6](https://github.com/Applitools-Dev/sdk/commit/492dff6ca431e242eb85a2c87d429f8676490e1a))
81
+
82
+
83
+
84
+ * @applitools/ec-client bumped to 1.10.13
85
+
86
+
3
87
  ## [4.38.1](https://github.com/Applitools-Dev/sdk/compare/js/core@4.38.0...js/core@4.38.1) (2025-05-07)
4
88
 
5
89
 
@@ -38,21 +38,31 @@ const utils = __importStar(require("@applitools/utils"));
38
38
  const merge_configs_1 = require("./merge-configs");
39
39
  const format_results_1 = require("../utils/format-results");
40
40
  const throat_1 = __importDefault(require("throat"));
41
+ const os_1 = __importDefault(require("os"));
41
42
  const throttledReadFile = (0, throat_1.default)(Number(process.env.APPLITOOLS_FILE_READING_CONCURRENCY) || 1000, fs_1.default.promises.readFile);
42
43
  function getConsoleLogLimit() {
43
44
  var _a;
44
45
  return (_a = utils.general.getEnvValue('OFFLINE_LOG_LIMIT', 'number')) !== null && _a !== void 0 ? _a : 20;
45
46
  }
47
+ function version() {
48
+ try {
49
+ return JSON.parse(fs_1.default.readFileSync(require.resolve('../../package.json'), 'utf-8')).version;
50
+ }
51
+ catch (e) {
52
+ return 'unknown';
53
+ }
54
+ }
46
55
  async function runOfflineSnapshots(options) {
47
56
  var _a;
48
57
  const limitConsoleLogs = getConsoleLogLimit();
58
+ const throttledRender = (0, throat_1.default)(Number(process.env.APPLITOOLS_RENDER_CONCURRENCY) || 1000, render);
49
59
  if (!options.offlineLocationPath)
50
60
  throw new Error('offlineLocationPath is required');
51
61
  const offlineLocationPath = path_1.default.resolve(options.offlineLocationPath);
52
62
  const startTime = Date.now();
53
63
  const logger = (_a = options.logger) !== null && _a !== void 0 ? _a : (0, logger_1.makeLogger)({ format: { label: 'offline-exec' } });
54
64
  const eyesServerSettings = { ...options.config.open, ...options };
55
- const core = (0, core_1.makeCore)();
65
+ const core = (0, core_1.makeCore)({ agentId: `js/core-offline/${version()}`, logger });
56
66
  const account = await core.getAccountInfo({ settings: eyesServerSettings });
57
67
  const ufgClient = await (0, get_ufg_client_1.makeGetUFGClient)({ logger })({
58
68
  settings: { ...account.eyesServer, ...account.ufgServer },
@@ -60,6 +70,7 @@ async function runOfflineSnapshots(options) {
60
70
  const testFolders = (await fs_1.default.promises.readdir(offlineLocationPath))
61
71
  .filter(filename => filename.startsWith('test-'))
62
72
  .sort();
73
+ void logStartupInfo();
63
74
  if (testFolders.length === 1) {
64
75
  logger.console.log(`Running single test from folder ${offlineLocationPath}`);
65
76
  }
@@ -135,36 +146,53 @@ async function runOfflineSnapshots(options) {
135
146
  const uploadResourcePromise = uploadResources(targets, logger).then(() => logger.log('uploaded resources for test', testPath));
136
147
  if (targets.length === 1) {
137
148
  logger.log('only one check found for test', testPath);
138
- await uploadResourcePromise;
139
- const target = targets[0];
140
- resultsGetter = await runOpenCheckAndClose({
141
- ...target,
142
- settings: {
143
- ...(0, merge_configs_1.mergeConfigs)((0, merge_configs_1.mergeConfigs)(closeSettings, openSettings), target.settings),
144
- ...account.eyesServer,
145
- environment: {
146
- ...openSettings.environment,
147
- ...target.settings.environment,
149
+ try {
150
+ await uploadResourcePromise;
151
+ const target = targets[0];
152
+ resultsGetter = await runOpenCheckAndClose({
153
+ ...target,
154
+ settings: {
155
+ ...(0, merge_configs_1.mergeConfigs)((0, merge_configs_1.mergeConfigs)(closeSettings, openSettings), target.settings),
156
+ ...account.eyesServer,
157
+ environment: {
158
+ ...openSettings.environment,
159
+ ...target.settings.environment,
160
+ },
148
161
  },
149
- },
150
- }, logger, environment);
151
- logger.log('finished running checks for test', testPath);
162
+ }, logger, environment);
163
+ logger.log('finished running checks for test', testPath);
164
+ }
165
+ catch (err) {
166
+ logger.log('error running checks for test', testPath, err);
167
+ resultsGetter = await makeAbortedTest(openSettings, environment, logger, err);
168
+ }
152
169
  }
153
170
  else {
154
171
  const eyes = await openEyes(openSettings, environment, logger);
155
172
  logger.log('opened eyes for test', testPath);
156
- await uploadResourcePromise;
157
173
  const lastTarget = targets.pop();
158
- await Promise.all(targets.map((target, index) => runCheck(eyes, target, index, logger)));
159
- if (lastTarget) {
160
- lastTarget.settings = (0, merge_configs_1.mergeConfigs)(lastTarget.settings, closeSettings);
161
- await runCheckAndClose(eyes, lastTarget, targets.length - 1, logger);
162
- logger.log('finished running checks for test', testPath);
174
+ try {
175
+ await uploadResourcePromise;
176
+ await Promise.all(targets.map((target, index) => runCheck(eyes, target, index, logger)));
177
+ if (lastTarget) {
178
+ lastTarget.settings = (0, merge_configs_1.mergeConfigs)(lastTarget.settings, closeSettings);
179
+ await runCheckAndClose(eyes, lastTarget, targets.length - 1, logger);
180
+ logger.log('finished running checks for test', testPath);
181
+ }
182
+ else {
183
+ logger.log('no checks found for test', testPath);
184
+ // on empty test, `checkAndClose` will not be called so we need to call `close` instead
185
+ await eyes.close({ settings: closeSettings });
186
+ }
163
187
  }
164
- else {
165
- logger.log('no checks found for test', testPath);
166
- // on empty test, `checkAndClose` will not be called so we need to call `close` instead
167
- await eyes.close({ settings: closeSettings });
188
+ catch (err) {
189
+ logger.log('error running checks for test', testPath, err);
190
+ await eyes.abort({
191
+ logger,
192
+ settings: {
193
+ reason: err,
194
+ },
195
+ });
168
196
  }
169
197
  resultsGetter = eyes;
170
198
  }
@@ -197,11 +225,11 @@ async function runOfflineSnapshots(options) {
197
225
  }));
198
226
  return targets;
199
227
  }
200
- async function makeAbortedTest(openSettings, environment, logger) {
228
+ async function makeAbortedTest(openSettings, environment, logger, reason = 'internal') {
201
229
  const eyes = await openEyes(openSettings, environment, logger);
202
230
  await eyes.abort({
203
231
  settings: {
204
- reason: 'internal',
232
+ reason,
205
233
  },
206
234
  });
207
235
  return eyes;
@@ -223,7 +251,7 @@ async function runOfflineSnapshots(options) {
223
251
  async function uploadResources(targets, logger) {
224
252
  const uploadLogger = logger.extend({ tags: ['upload-resources'] });
225
253
  const promises = targets.map(async ({ target }) => {
226
- let resourcePromises = Object.values(target.resources)
254
+ const resourcePromises = Object.values(target.resources)
227
255
  .filter(isHashedResource)
228
256
  .map(async (resource) => {
229
257
  return ufgClient.uploadResource({
@@ -242,13 +270,18 @@ async function runOfflineSnapshots(options) {
242
270
  logger: uploadLogger,
243
271
  });
244
272
  });
245
- resourcePromises = resourcePromises.concat(ufgClient.uploadResource({
273
+ resourcePromises.push(ufgClient.uploadResource({
246
274
  resource: {
247
- id: '',
248
- url: '',
249
- value: await throttledReadFile(path_1.default.join(offlineLocationPath, 'resources', target.snapshot.hash)),
250
- contentType: target.snapshot.contentType,
251
275
  hash: target.snapshot,
276
+ async getter() {
277
+ return {
278
+ id: '',
279
+ url: '',
280
+ value: await throttledReadFile(path_1.default.join(offlineLocationPath, 'resources', target.snapshot.hash)),
281
+ contentType: target.snapshot.contentType,
282
+ hash: target.snapshot,
283
+ };
284
+ },
252
285
  },
253
286
  logger: uploadLogger,
254
287
  }));
@@ -262,7 +295,7 @@ async function runOfflineSnapshots(options) {
262
295
  async function runCheck(eyes, target, index, logger) {
263
296
  var _a;
264
297
  const checkLogger = logger.extend({ tags: [`check-${index}`] });
265
- const { mergedCheckSettings, baseTarget } = await render(target, checkLogger);
298
+ const { mergedCheckSettings, baseTarget } = await throttledRender(target, checkLogger);
266
299
  (_a = mergedCheckSettings.stepIndex) !== null && _a !== void 0 ? _a : (mergedCheckSettings.stepIndex = index);
267
300
  await eyes.check({
268
301
  target: { ...baseTarget, isTransformed: true },
@@ -275,7 +308,7 @@ async function runOfflineSnapshots(options) {
275
308
  async function runCheckAndClose(eyes, target, index, logger) {
276
309
  var _a;
277
310
  const checkLogger = logger.extend({ tags: [`check-${index}`] });
278
- const { mergedCheckSettings, baseTarget } = await render(target, checkLogger);
311
+ const { mergedCheckSettings, baseTarget } = await throttledRender(target, checkLogger);
279
312
  (_a = mergedCheckSettings.stepIndex) !== null && _a !== void 0 ? _a : (mergedCheckSettings.stepIndex = index);
280
313
  await eyes.checkAndClose({
281
314
  target: { ...baseTarget, isTransformed: true },
@@ -287,7 +320,7 @@ async function runOfflineSnapshots(options) {
287
320
  }
288
321
  async function runOpenCheckAndClose(target, logger, actualEnvironment) {
289
322
  const checkLogger = logger.extend({ tags: ['open-check-and-close'] });
290
- const { mergedCheckSettings, baseTarget } = await render(target, checkLogger);
323
+ const { mergedCheckSettings, baseTarget } = await throttledRender(target, checkLogger);
291
324
  const settings = (0, merge_configs_1.mergeConfigs)(target.settings, mergedCheckSettings);
292
325
  return core.base.openCheckAndCloseEyes({
293
326
  target: { ...baseTarget, isTransformed: true },
@@ -341,6 +374,48 @@ async function runOfflineSnapshots(options) {
341
374
  const mergedCheckSettings = (0, merge_configs_1.mergeConfigs)(baseSettings, options.config.check);
342
375
  return { mergedCheckSettings, baseTarget };
343
376
  }
377
+ async function logStartupInfo() {
378
+ await core.logEvent({
379
+ logger,
380
+ settings: {
381
+ ...account.eyesServer,
382
+ ...account.ufgServer,
383
+ level: 'Notice',
384
+ event: {
385
+ type: 'offlineExecutionStart',
386
+ timestamp: new Date().toISOString(),
387
+ offlineLocationPath,
388
+ testCount: testFolders.length,
389
+ ufgServerUrl: account.ufgServer.ufgServerUrl,
390
+ CI: process.env.CI,
391
+ config: options.config,
392
+ driver: {
393
+ platform: os_1.default.platform(),
394
+ version: os_1.default.release(),
395
+ arch: os_1.default.arch(),
396
+ cpu: {
397
+ count: os_1.default.cpus().length,
398
+ avgSpeed: os_1.default.cpus().reduce((acc, cpu) => acc + cpu.speed, 0) / os_1.default.cpus().length,
399
+ model: os_1.default.cpus()[0].model,
400
+ },
401
+ memory: {
402
+ total: os_1.default.totalmem(),
403
+ free: os_1.default.freemem(),
404
+ },
405
+ nodeVersion: process.version,
406
+ versions: await Promise.all(['eyes', 'core', 'core-base', 'ufg-client']
407
+ .map(async (name) => ({
408
+ name,
409
+ version: JSON.parse(await throttledReadFile(require.resolve(`@applitools/${name}/package.json`), {
410
+ encoding: 'utf-8',
411
+ })).version,
412
+ }))
413
+ .map(promise => promise.catch(e => `failed to get version: ${e}`))),
414
+ },
415
+ },
416
+ },
417
+ });
418
+ }
344
419
  }
345
420
  exports.runOfflineSnapshots = runOfflineSnapshots;
346
421
  function uniq(arr) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applitools/core",
3
- "version": "4.38.1",
3
+ "version": "4.39.0",
4
4
  "homepage": "https://applitools.com",
5
5
  "bugs": {
6
6
  "url": "https://github.com/applitools/eyes.sdk.javascript1/issues"
@@ -71,24 +71,25 @@
71
71
  "node20-linux-x64-musl": "./bin/core-alpine",
72
72
  "node20-linux-arm64": "./bin/core-linux-arm64",
73
73
  "node20-darwin-x64": "./bin/core-macos",
74
- "node20-win-x64": "./bin/core-win.exe"
74
+ "node20-win-x64": "./bin/core-win.exe",
75
+ "node20-darwin-arm64": "./bin/core-macos-arm64"
75
76
  }
76
77
  },
77
78
  "dependencies": {
78
- "@applitools/core-base": "1.25.1",
79
+ "@applitools/core-base": "1.25.3",
79
80
  "@applitools/dom-capture": "11.5.5",
80
- "@applitools/dom-snapshot": "4.11.20",
81
- "@applitools/driver": "1.21.3",
82
- "@applitools/ec-client": "1.10.12",
83
- "@applitools/logger": "2.1.3",
84
- "@applitools/nml-client": "1.9.3",
85
- "@applitools/req": "1.7.11",
86
- "@applitools/screenshoter": "3.11.3",
81
+ "@applitools/dom-snapshot": "4.11.21",
82
+ "@applitools/driver": "1.21.4",
83
+ "@applitools/ec-client": "1.10.14",
84
+ "@applitools/logger": "2.1.4",
85
+ "@applitools/nml-client": "1.9.4",
86
+ "@applitools/req": "1.7.12",
87
+ "@applitools/screenshoter": "3.11.4",
87
88
  "@applitools/snippets": "2.6.5",
88
- "@applitools/socket": "1.2.4",
89
- "@applitools/spec-driver-webdriver": "1.2.4",
90
- "@applitools/ufg-client": "1.16.10",
91
- "@applitools/utils": "1.8.1",
89
+ "@applitools/socket": "1.2.5",
90
+ "@applitools/spec-driver-webdriver": "1.2.5",
91
+ "@applitools/ufg-client": "1.16.11",
92
+ "@applitools/utils": "1.8.2",
92
93
  "@types/ws": "8.5.5",
93
94
  "abort-controller": "3.0.0",
94
95
  "chalk": "4.1.2",
@@ -102,17 +103,18 @@
102
103
  "devDependencies": {
103
104
  "@applitools/bongo": "^5.10.0",
104
105
  "@applitools/sea": "^1.0.0",
105
- "@applitools/spec-driver-puppeteer": "^1.4.29",
106
- "@applitools/spec-driver-selenium": "^1.5.100",
106
+ "@applitools/spec-driver-puppeteer": "^1.4.30",
107
+ "@applitools/spec-driver-selenium": "^1.5.101",
107
108
  "@applitools/test-server": "^1.2.2",
108
109
  "@applitools/test-utils": "^1.5.17",
109
- "@applitools/tunnel-client": "^1.6.6",
110
+ "@applitools/tunnel-client": "^1.6.7",
110
111
  "@types/mocha": "^10.0.7",
111
112
  "@types/node": "^12.20.55",
112
113
  "@types/selenium-webdriver": "^4.1.2",
113
114
  "@types/semver": "^7.5.8",
114
115
  "@types/yargs": "^17.0.22",
115
116
  "chromedriver": "^131.0.5",
117
+ "crypto": "^1.0.1",
116
118
  "nock": "^13.3.2",
117
119
  "png-async": "^0.9.4",
118
120
  "puppeteer": "^19.11.1",