@testim/testim-cli 3.266.0 → 3.268.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.
@@ -4,6 +4,7 @@ const Promise = require('bluebird');
4
4
  const pRetry = require('p-retry');
5
5
  const io = require('socket.io-client');
6
6
  const config = require('../config');
7
+ const utils = require('../../utils');
7
8
 
8
9
  const MAX_SOCKET_RECONNECT_ATTEMPT = 50;
9
10
  const MAX_RECONNECT_ATTEMPT_BEFORE_SWITCH = 10;
@@ -15,7 +16,9 @@ const logger = require('../logger').getLogger('base socket service');
15
16
  class BaseSocketServiceSocketIO {
16
17
  constructor() {
17
18
  this.attempts = 0;
19
+ /** @type {{ [testResultId: string]: testId }} */
18
20
  this.rooms = {};
21
+ /** @type {undefined | Promise<void>} */
19
22
  this.emitPromiseQueue = undefined;
20
23
  }
21
24
 
@@ -28,10 +31,18 @@ class BaseSocketServiceSocketIO {
28
31
  });
29
32
  }
30
33
 
34
+ /**
35
+ * @param {string} resultId
36
+ * @param {string} testId
37
+ */
31
38
  joinRoom(resultId, testId) {
32
39
  this.rooms[resultId] = testId;
33
40
  }
34
41
 
42
+ /**
43
+ * @param {string} resultId
44
+ * @param {string} testId
45
+ */
35
46
  leaveRoom(resultId) {
36
47
  delete this.rooms[resultId];
37
48
  }
@@ -97,6 +108,10 @@ class BaseSocketServiceSocketIO {
97
108
  });
98
109
  }
99
110
 
111
+ /**
112
+ * @param {string} projectId
113
+ * @param {string} ns
114
+ */
100
115
  initNewSocket(projectId, ns) {
101
116
  const opts = {
102
117
  query: { projectId },
@@ -124,6 +139,10 @@ class BaseSocketServiceSocketIO {
124
139
  });
125
140
  }
126
141
 
142
+ /**
143
+ * @param {string} projectId
144
+ * @param {string} ns
145
+ */
127
146
  init(projectId, ns) {
128
147
  const opts = {
129
148
  query: { projectId },
@@ -146,6 +165,10 @@ class BaseSocketServiceSocketIO {
146
165
  this.addSocketHandlers();
147
166
  }
148
167
 
168
+ /**
169
+ * @param {string} eventName
170
+ * @param {*} eventData
171
+ */
149
172
  emitPromise(eventName, eventData) {
150
173
  const errorneousEvents = {};
151
174
 
@@ -158,10 +181,10 @@ class BaseSocketServiceSocketIO {
158
181
 
159
182
  return reject(new Error('bad ack'));
160
183
  });
161
- }).timeout(EMIT_PROMISE_TIMEOUT);
184
+ });
162
185
 
163
186
  this.emitPromiseQueue = (this.emitPromiseQueue || Promise.resolve())
164
- .then(() => pRetry(emitAndWait, { retries: 200, minTimeout: 3000 }))
187
+ .then(() => pRetry(() => utils.promiseTimeout(emitAndWait(), EMIT_PROMISE_TIMEOUT), { retries: 200, minTimeout: 3000, factor: 1 }))
165
188
  .finally(() => {
166
189
  if (Object.keys(errorneousEvents).length > 0) {
167
190
  logger.error('Bad acknowledge from socket emit', { errorneousEvents });
@@ -5,7 +5,6 @@ const { EventEmitter } = require('events');
5
5
  const featureFlags = require('../featureFlags');
6
6
 
7
7
  const { socketEventTypes } = require('../constants');
8
- const Promise = require('bluebird');
9
8
 
10
9
  class TestResultService extends EventEmitter {
11
10
  init(projectId) {
@@ -561,11 +561,11 @@ function buildSeleniumOptions(browserOptions, testName, testRunConfig, gridInfo,
561
561
  }
562
562
 
563
563
  //testRunConfig not in used for now
564
- function buildAppiumOptions({ projectType, gridInfo, testRunConfig, nativeApp, options }) {
564
+ function buildAppiumOptions({ projectType, gridInfo, testRunConfig, nativeApp, options, appPath }) {
565
565
  const { deviceModel, osVersion, deviceUdid } = options;
566
566
  const headspinSelector = {};
567
567
 
568
- if (!nativeApp) {
568
+ if (!nativeApp && !appPath) {
569
569
  throw Error('missing mobile app!');
570
570
  }
571
571
  if (gridInfo.type !== gridTypes.TESTIM_HEADSPIN) {
@@ -578,21 +578,29 @@ function buildAppiumOptions({ projectType, gridInfo, testRunConfig, nativeApp, o
578
578
  path: `/v0/${gridInfo.accessToken}/wd/hub`,
579
579
  };
580
580
 
581
- let appCaps = {};
581
+ let appCaps = {
582
+ 'headspin:capture': true,
583
+ };
582
584
  switch (projectType) {
583
585
  case 'ios':
584
586
  appCaps = {
585
- 'appium:bundleId': nativeApp.id,
587
+ ...appCaps,
586
588
  platformName: 'iOS',
587
589
  'appium:automationName': 'XCUITest',
590
+ ...(nativeApp && { 'appium:bundleId': nativeApp.id }),
591
+ ...(appPath && { 'appium:app': appPath }),
588
592
  };
589
593
  break;
590
594
  case 'android':
591
595
  appCaps = {
596
+ ...appCaps,
592
597
  platformName: 'Android',
593
598
  'appium:automationName': 'UiAutomator2',
594
- 'appium:appPackage': nativeApp.packageName,
595
- 'appium:appActivity': nativeApp.activity,
599
+ ...(nativeApp && {
600
+ 'appium:appPackage': nativeApp.packageName,
601
+ 'appium:appActivity': nativeApp.activity,
602
+ }),
603
+ ...(appPath && { 'appium:app': appPath }),
596
604
  };
597
605
  break;
598
606
  default:
@@ -609,7 +617,6 @@ function buildAppiumOptions({ projectType, gridInfo, testRunConfig, nativeApp, o
609
617
  delete headspinSelector.model;
610
618
  delete headspinSelector.os_version;
611
619
  }
612
-
613
620
  if (!_.isEmpty(headspinSelector)) {
614
621
  appCaps['headspin:selector'] = headspinSelector;
615
622
  }
@@ -237,6 +237,15 @@ function getSuiteTestList({
237
237
  }), { retries: DEFAULT_REQUEST_RETRY });
238
238
  }
239
239
 
240
+ async function getAppDetails({ appId, projectId }) {
241
+ try {
242
+ return await pRetry(() => getWithAuth(`/mobile-app/app/${appId}?projectId=${projectId}`), { retries: DEFAULT_REQUEST_RETRY, factor: 1 });
243
+ } catch (error) {
244
+ logger.error('failed getting application details', { appId, error });
245
+ return undefined;
246
+ }
247
+ }
248
+
240
249
  function getUsageForCurrentBillingPeriod(projectId) {
241
250
  return pRetry(() => getWithAuth(`/plan/project/${projectId}/usage-current-billing-period`), { retries: DEFAULT_REQUEST_RETRY })
242
251
  .catch((error) => {
@@ -395,10 +404,16 @@ function saveRemoteStep(projectId, resultId, stepId, remoteStep) {
395
404
  }), { retries: DEFAULT_REQUEST_RETRY });
396
405
  }
397
406
 
407
+ /** @param {string} uri */
398
408
  function relativize(uri) {
399
409
  return uri.startsWith('/') ? uri : `/${uri}`;
400
410
  }
401
411
 
412
+ /**
413
+ * @param {string} filePath
414
+ * @param {string} bucket
415
+ * @param {string} projectId
416
+ */
402
417
  function getStorageRelativePath(filePath, bucket, projectId) {
403
418
  let fullPath = relativize(filePath);
404
419
  if (projectId) {
@@ -411,7 +426,15 @@ function getStorageRelativePath(filePath, bucket, projectId) {
411
426
  return fullPath;
412
427
  }
413
428
 
414
- function uploadArtifact(projectId, testId, testResultId, content, subType, mimeType = 'application/octet-stream') {
429
+ /**
430
+ * @param {string} projectId
431
+ * @param {string} testId
432
+ * @param {string} testResultId
433
+ * @param {*} content
434
+ * @param {string} subType
435
+ * @param {string} mimeType
436
+ */
437
+ async function uploadArtifact(projectId, testId, testResultId, content, subType, mimeType = 'application/octet-stream') {
415
438
  let fileSuffix = null;
416
439
  if (mimeType === 'application/json') {
417
440
  fileSuffix = '.json';
@@ -431,9 +454,11 @@ function uploadArtifact(projectId, testId, testResultId, content, subType, mimeT
431
454
  },
432
455
  };
433
456
 
434
- return pRetry(() => postAuthFormData(`/storage${storagePath}`, {}, files, {
435
- 'X-Asset-Encoding': 'gzip',
436
- }), { retries: DEFAULT_REQUEST_RETRY }).then(() => storagePath);
457
+ await pRetry(
458
+ () => postAuthFormData(`/storage${storagePath}`, {}, files, { 'X-Asset-Encoding': 'gzip' }),
459
+ { retries: DEFAULT_REQUEST_RETRY, factor: 1 }
460
+ );
461
+ return storagePath;
437
462
  }
438
463
 
439
464
  const uploadRunDataArtifact = _.memoize(async (projectId, testId, testResultId, runData) => {
@@ -530,6 +555,7 @@ function updateRemoteRunFailure(body) {
530
555
  }
531
556
 
532
557
  module.exports = {
558
+ getAppDetails,
533
559
  getS3Artifact,
534
560
  getTestPlan,
535
561
  saveTestPlanResult,
@@ -553,7 +579,7 @@ module.exports = {
553
579
  saveRemoteStep,
554
580
  getEditorUrl,
555
581
  getLabFeaturesByProjectId,
556
- uploadRunDataArtifact: Promise.method(uploadRunDataArtifact),
582
+ uploadRunDataArtifact,
557
583
  updateTestDataArtifact: Promise.method(updateTestDataArtifact),
558
584
  initializeUserWithAuth,
559
585
  addTestRetry,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@testim/testim-cli",
3
- "version": "3.266.0",
3
+ "version": "3.268.0",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@testim/testim-cli",
9
- "version": "3.266.0",
9
+ "version": "3.268.0",
10
10
  "license": "Proprietary",
11
11
  "dependencies": {
12
12
  "@applitools/eyes-sdk-core": "13.11.21",
@@ -1460,9 +1460,9 @@
1460
1460
  }
1461
1461
  },
1462
1462
  "node_modules/@wdio/types/node_modules/@types/node": {
1463
- "version": "18.11.11",
1464
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz",
1465
- "integrity": "sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g=="
1463
+ "version": "18.11.13",
1464
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.13.tgz",
1465
+ "integrity": "sha512-IASpMGVcWpUsx5xBOrxMj7Bl8lqfuTY7FKAnPmu5cHkfQVWF8GulWS1jbRqA934qZL35xh5xN/+Xe/i26Bod4w=="
1466
1466
  },
1467
1467
  "node_modules/@wdio/utils": {
1468
1468
  "version": "7.24.0",
@@ -4416,9 +4416,9 @@
4416
4416
  "integrity": "sha512-+iipnm2hvmlWs4MVNx7HwSTxhDxsXnQyK5F1OalZVXeUhdPgP/23T42NCyg0TK3wL/Yg92SVrSuGKqdg12o54w=="
4417
4417
  },
4418
4418
  "node_modules/devtools/node_modules/@types/node": {
4419
- "version": "18.11.11",
4420
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz",
4421
- "integrity": "sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g=="
4419
+ "version": "18.11.13",
4420
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.13.tgz",
4421
+ "integrity": "sha512-IASpMGVcWpUsx5xBOrxMj7Bl8lqfuTY7FKAnPmu5cHkfQVWF8GulWS1jbRqA934qZL35xh5xN/+Xe/i26Bod4w=="
4422
4422
  },
4423
4423
  "node_modules/devtools/node_modules/ua-parser-js": {
4424
4424
  "version": "1.0.32",
@@ -6216,9 +6216,9 @@
6216
6216
  }
6217
6217
  },
6218
6218
  "node_modules/got": {
6219
- "version": "11.8.5",
6220
- "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz",
6221
- "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==",
6219
+ "version": "11.8.6",
6220
+ "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
6221
+ "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
6222
6222
  "dependencies": {
6223
6223
  "@sindresorhus/is": "^4.0.0",
6224
6224
  "@szmarczak/http-timer": "^4.0.5",
@@ -15393,9 +15393,9 @@
15393
15393
  }
15394
15394
  },
15395
15395
  "node_modules/vm2": {
15396
- "version": "3.9.12",
15397
- "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.12.tgz",
15398
- "integrity": "sha512-OMmRsKh1gmdosFzuqmj6O43hqIStqXA24YbwjtUTO0TkOBP8yLNHLplbr4odnAzEcMnm9lt2r3R8kTivn8urMg==",
15396
+ "version": "3.9.13",
15397
+ "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.13.tgz",
15398
+ "integrity": "sha512-0rvxpB8P8Shm4wX2EKOiMp7H2zq+HUE/UwodY0pCZXs9IffIKZq6vUti5OgkVCTakKo9e/fgO4X1fkwfjWxE3Q==",
15399
15399
  "dependencies": {
15400
15400
  "acorn": "^8.7.0",
15401
15401
  "acorn-walk": "^8.2.0"
@@ -15468,9 +15468,9 @@
15468
15468
  }
15469
15469
  },
15470
15470
  "node_modules/webdriver/node_modules/@types/node": {
15471
- "version": "18.11.11",
15472
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz",
15473
- "integrity": "sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g=="
15471
+ "version": "18.11.13",
15472
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.13.tgz",
15473
+ "integrity": "sha512-IASpMGVcWpUsx5xBOrxMj7Bl8lqfuTY7FKAnPmu5cHkfQVWF8GulWS1jbRqA934qZL35xh5xN/+Xe/i26Bod4w=="
15474
15474
  },
15475
15475
  "node_modules/webdriverio": {
15476
15476
  "version": "7.24.0",
@@ -15510,9 +15510,9 @@
15510
15510
  }
15511
15511
  },
15512
15512
  "node_modules/webdriverio/node_modules/@types/node": {
15513
- "version": "18.11.11",
15514
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz",
15515
- "integrity": "sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g=="
15513
+ "version": "18.11.13",
15514
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.13.tgz",
15515
+ "integrity": "sha512-IASpMGVcWpUsx5xBOrxMj7Bl8lqfuTY7FKAnPmu5cHkfQVWF8GulWS1jbRqA934qZL35xh5xN/+Xe/i26Bod4w=="
15516
15516
  },
15517
15517
  "node_modules/webdriverio/node_modules/brace-expansion": {
15518
15518
  "version": "2.0.1",
@@ -17129,9 +17129,9 @@
17129
17129
  },
17130
17130
  "dependencies": {
17131
17131
  "@types/node": {
17132
- "version": "18.11.11",
17133
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz",
17134
- "integrity": "sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g=="
17132
+ "version": "18.11.13",
17133
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.13.tgz",
17134
+ "integrity": "sha512-IASpMGVcWpUsx5xBOrxMj7Bl8lqfuTY7FKAnPmu5cHkfQVWF8GulWS1jbRqA934qZL35xh5xN/+Xe/i26Bod4w=="
17135
17135
  }
17136
17136
  }
17137
17137
  },
@@ -19496,9 +19496,9 @@
19496
19496
  },
19497
19497
  "dependencies": {
19498
19498
  "@types/node": {
19499
- "version": "18.11.11",
19500
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz",
19501
- "integrity": "sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g=="
19499
+ "version": "18.11.13",
19500
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.13.tgz",
19501
+ "integrity": "sha512-IASpMGVcWpUsx5xBOrxMj7Bl8lqfuTY7FKAnPmu5cHkfQVWF8GulWS1jbRqA934qZL35xh5xN/+Xe/i26Bod4w=="
19502
19502
  },
19503
19503
  "ua-parser-js": {
19504
19504
  "version": "1.0.32",
@@ -20937,9 +20937,9 @@
20937
20937
  }
20938
20938
  },
20939
20939
  "got": {
20940
- "version": "11.8.5",
20941
- "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz",
20942
- "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==",
20940
+ "version": "11.8.6",
20941
+ "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
20942
+ "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
20943
20943
  "requires": {
20944
20944
  "@sindresorhus/is": "^4.0.0",
20945
20945
  "@szmarczak/http-timer": "^4.0.5",
@@ -27933,9 +27933,9 @@
27933
27933
  }
27934
27934
  },
27935
27935
  "vm2": {
27936
- "version": "3.9.12",
27937
- "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.12.tgz",
27938
- "integrity": "sha512-OMmRsKh1gmdosFzuqmj6O43hqIStqXA24YbwjtUTO0TkOBP8yLNHLplbr4odnAzEcMnm9lt2r3R8kTivn8urMg==",
27936
+ "version": "3.9.13",
27937
+ "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.13.tgz",
27938
+ "integrity": "sha512-0rvxpB8P8Shm4wX2EKOiMp7H2zq+HUE/UwodY0pCZXs9IffIKZq6vUti5OgkVCTakKo9e/fgO4X1fkwfjWxE3Q==",
27939
27939
  "requires": {
27940
27940
  "acorn": "^8.7.0",
27941
27941
  "acorn-walk": "^8.2.0"
@@ -27994,9 +27994,9 @@
27994
27994
  },
27995
27995
  "dependencies": {
27996
27996
  "@types/node": {
27997
- "version": "18.11.11",
27998
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz",
27999
- "integrity": "sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g=="
27997
+ "version": "18.11.13",
27998
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.13.tgz",
27999
+ "integrity": "sha512-IASpMGVcWpUsx5xBOrxMj7Bl8lqfuTY7FKAnPmu5cHkfQVWF8GulWS1jbRqA934qZL35xh5xN/+Xe/i26Bod4w=="
28000
28000
  }
28001
28001
  }
28002
28002
  },
@@ -28035,9 +28035,9 @@
28035
28035
  },
28036
28036
  "dependencies": {
28037
28037
  "@types/node": {
28038
- "version": "18.11.11",
28039
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz",
28040
- "integrity": "sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g=="
28038
+ "version": "18.11.13",
28039
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.13.tgz",
28040
+ "integrity": "sha512-IASpMGVcWpUsx5xBOrxMj7Bl8lqfuTY7FKAnPmu5cHkfQVWF8GulWS1jbRqA934qZL35xh5xN/+Xe/i26Bod4w=="
28041
28041
  },
28042
28042
  "brace-expansion": {
28043
28043
  "version": "2.0.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testim/testim-cli",
3
- "version": "3.266.0",
3
+ "version": "3.268.0",
4
4
  "description": "Command line interface for running Testing on your CI",
5
5
  "author": "Oren Rubin",
6
6
  "contributors": [{
@@ -8,6 +8,8 @@ const featureAvailabilityService = require('../commons/featureAvailabilityServic
8
8
  const { getAbortedTests, getFailedTests, getPassedTests, getFailureEvaluatingCount, getSkippedCount } = require('./reporterUtils');
9
9
 
10
10
  const colorize = { success: chalk.green, warn: chalk.yellow, error: chalk.red };
11
+ const { CLI_MODE } = constants;
12
+ const DEVICE = 'device';
11
13
 
12
14
  class ConsoleReporter {
13
15
  constructor(options, branchToUse) {
@@ -143,13 +145,15 @@ class ConsoleReporter {
143
145
 
144
146
  onGetSlot(workerId, browser) {
145
147
  const gridNameOrId = this.options.grid || this.options.gridId;
148
+ const instanceType = this.options.mode === CLI_MODE.APPIUM ? DEVICE : browser;
146
149
  if (gridNameOrId) {
147
- console.log(this.toWorkerIdPrefix(workerId), `Get ${chalk.underline(browser)} slot from ${chalk.underline(gridNameOrId)}`);
150
+ console.log(this.toWorkerIdPrefix(workerId), `Get ${chalk.underline(instanceType)} slot from ${chalk.underline(gridNameOrId)}`);
148
151
  }
149
152
  }
150
153
 
151
154
  onGetSession(workerId, testName, mode) {
152
- console.log(this.toWorkerIdPrefix(workerId), `Get browser to run ${chalk.underline(testName)}`);
155
+ const instanceType = mode === CLI_MODE.APPIUM ? DEVICE : 'browser';
156
+ console.log(this.toWorkerIdPrefix(workerId), `Get ${instanceType} to run ${chalk.underline(testName)}`);
153
157
  }
154
158
 
155
159
  onWaitToTestStart(workerId) {
package/runOptions.d.ts CHANGED
@@ -109,9 +109,10 @@ interface RunnerOptions extends Partial<Omit<TunnelOptions, 'tunnelOnlyMode' | '
109
109
  retries?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20;
110
110
 
111
111
  //# region mobile flags
112
- deviceModel: string;
113
- deviceUdid: string;
114
- osVersion: string;
112
+ deviceModel?: string;
113
+ deviceUdid?: string;
114
+ osVersion?: string;
115
+ appId?: string;
115
116
  // #endregion
116
117
 
117
118
 
package/runOptions.js CHANGED
@@ -220,6 +220,7 @@ program
220
220
  .option('--device-model [device-model]', 'The device model to use, iPhone 12, Nexus 5X, SM-G900P etc....')
221
221
  .option('--device-udid [device-udid]', 'the device unique id')
222
222
  .option('--os-version [os-version]', 'The operating system version number')
223
+ .option('--app-id [app-id]', 'The application id from app library on Testim Editor App')
223
224
 
224
225
  .option('-str --suppress-tms-reporting [suppress-tms-reporting]', 'disable test management reporting', false)
225
226
  .option('-tsr --tms-suppress-reporting [tms-suppress-reporting]', 'disable test management reporting', false)
@@ -1037,7 +1038,6 @@ module.exports = {
1037
1038
  if (program.deviceUdid && (program.deviceModel || program.osVersion)) {
1038
1039
  throw new ArgError('It is not possible to use --osVersion or --deviceModel with --device-udid. device-udid is unique and cannot be combined with another flag');
1039
1040
  }
1040
-
1041
1041
  if (program.lightweightMode) {
1042
1042
  try {
1043
1043
  const DEFAULTS = {
@@ -1154,10 +1154,11 @@ module.exports = {
1154
1154
  retentionDays: program.setRetention,
1155
1155
  passZeroTests: Boolean(program.passZeroTests),
1156
1156
  runQuarantinedTests: Boolean(program.runQuarantinedTests),
1157
-
1157
+ //Mobile-Flags
1158
1158
  deviceModel: program.deviceModel,
1159
1159
  deviceUdid: program.deviceUdid,
1160
1160
  osVersion: program.osVersion,
1161
+ appId: program.appId,
1161
1162
 
1162
1163
  // Extension
1163
1164
  ext: program.ext,
package/runner.js CHANGED
@@ -254,7 +254,7 @@ async function runRunner(options, customExtensionLocalLocation) {
254
254
  options.source = (useLocalChromeDriver || useChromeLauncher) ? 'cli-local' : 'cli';
255
255
  }
256
256
 
257
- npmDriver.checkNpmVersion();
257
+ await npmDriver.checkNpmVersion();
258
258
  perf.log('in runner.js after checkNpmVersion');
259
259
 
260
260
  await validateCliAccount(options);
@@ -14,11 +14,18 @@ const { StopRunOnError } = require('../errors');
14
14
 
15
15
  require('../player/webdriver'); // preload
16
16
 
17
+ /**
18
+ * @template {unknown[]} T
19
+ * @typedef {T extends [infer H, ...infer R] ? R : T} RemoveFirst
20
+ */
21
+
17
22
  class ParallelWorkerManager {
23
+ /** @param {string=} customExtensionLocalLocation */
18
24
  constructor(customExtensionLocalLocation) {
19
25
  this.customExtensionLocalLocation = customExtensionLocalLocation;
20
26
  }
21
27
 
28
+ /** @param {typeof CLI_MODE[keyof typeof CLI_MODE]} mode */
22
29
  getWorkerType(mode) {
23
30
  switch (mode) {
24
31
  case CLI_MODE.SELENIUM:
@@ -33,6 +40,12 @@ class ParallelWorkerManager {
33
40
  }
34
41
  }
35
42
 
43
+ /**
44
+ * @param {number} count
45
+ * @param {ExecutionQueue} queue
46
+ * @param {typeof CLI_MODE[keyof typeof CLI_MODE]} mode
47
+ * @param {...RemoveFirst<ConstructorParameters<typeof import('../workers/BaseWorker')>>} args
48
+ */
36
49
  createWorkers(count, queue, mode, ...args) {
37
50
  const Worker = this.getWorkerType(mode);
38
51
  const createWorker = () => {
@@ -47,6 +60,17 @@ class ParallelWorkerManager {
47
60
  return Array.from(new Array(count), createWorker);
48
61
  }
49
62
 
63
+ /**
64
+ * @param {import('./TestPlanRunner').ExecutionList} testList
65
+ * @param {import('../testRunStatus')} testStatus
66
+ * @param {string} executionId
67
+ * @param {string} executionName
68
+ * @param {import('../runOptions').RunnerOptions} options
69
+ * @param {string} branchToUse
70
+ * @param {ReturnType<typeof testimCustomToken['getTokenV3UserData']>} authData
71
+ * @param {number} workerCount
72
+ * @param {boolean} stopOnError
73
+ */
50
74
  async runTests(testList, testStatus, executionId, executionName, options, branchToUse, authData, workerCount, stopOnError) {
51
75
  if (testList && testList.length === 0) {
52
76
  return undefined;
@@ -72,6 +96,13 @@ class ParallelWorkerManager {
72
96
  const lightweightMode = options.lightweightMode;
73
97
  const sessionType = utils.getSessionType(options);
74
98
 
99
+ /**
100
+ * @param {number} wid
101
+ * @param {string} testId
102
+ * @param {string} resultId
103
+ * @param {boolean | undefined} isRerun
104
+ * @param {`${number}:${number}`} testRetryKey
105
+ */
75
106
  const onTestStarted = (wid, testId, resultId, isRerun, testRetryKey) => {
76
107
  runningTests++;
77
108
  analyticsService.analyticsTestStart({
@@ -93,6 +124,13 @@ class ParallelWorkerManager {
93
124
  return testStatus.testStartAndReport(wid, executionId, resultId, isRerun, testRetryKey);
94
125
  };
95
126
 
127
+ /**
128
+ * @param {number} wid
129
+ * @param {string} testId
130
+ * @param {*} testResult
131
+ * @param {string} sessionId
132
+ * @param {boolean | undefined} isRerun
133
+ */
96
134
  const onTestCompleted = async (wid, testId, testResult, sessionId, isRerun) => {
97
135
  runningTests--;
98
136
  const update = {};
@@ -135,7 +173,7 @@ class ParallelWorkerManager {
135
173
  .catch(err => logger.error('testEndAndReport threw an error', { err }));
136
174
 
137
175
  if (isRerun) {
138
- return undefined;
176
+ return;
139
177
  }
140
178
  combinedTestResults[testResult.resultId] = testResult;
141
179
  analyticsService.analyticsTestEnd({
@@ -163,11 +201,13 @@ class ParallelWorkerManager {
163
201
  const completedTests = Object.keys(combinedTestResults).length;
164
202
  if (completedTests === testCount || (stoppedOnError && runningTests === 0)) {
165
203
  resolve(combinedTestResults);
166
- return undefined;
167
204
  }
168
- return undefined;
169
205
  };
170
206
 
207
+ /**
208
+ * @param {number} wid
209
+ * @param {{ name: string; testId: string; resultId: string; runnerStatus: 'SKIPPED'; testStatus: 'quarantine' }} testResult
210
+ */
171
211
  const onTestIgnored = (wid, testResult) => {
172
212
  combinedTestResults[testResult.resultId] = testResult;
173
213
  testStatus.onTestIgnored(wid, testResult.resultId);
@@ -178,7 +218,7 @@ class ParallelWorkerManager {
178
218
  }
179
219
  };
180
220
 
181
- const onGridSlot = (_executionId, resultId, gridInfo) => testStatus.onGridSlot(_executionId, resultId, gridInfo);
221
+ const onGridSlot = (resultId, gridInfo) => testStatus.onGridSlot(resultId, gridInfo);
182
222
 
183
223
  options.userData = {
184
224
  loginData: Object.assign({}, testimCustomToken.getTokenV3UserData(), {
@@ -191,14 +231,24 @@ class ParallelWorkerManager {
191
231
  servicesUrl: config.SERVICES_HOST,
192
232
  };
193
233
  perf.log('in localStrategy before createWorker');
194
- this.createWorkers(workerCount, executionQueue, options.mode, options, this.customExtensionLocalLocation, executionId, onTestStarted, onTestCompleted, onGridSlot, onTestIgnored)
195
- .forEach((worker, index) => {
196
- perf.log('before schedule worker.run after createWorkers');
197
- schedule(() => {
198
- perf.log('right before worker.run');
199
- worker.run();
200
- }, index);
201
- });
234
+ this.createWorkers(
235
+ workerCount,
236
+ executionQueue,
237
+ options.mode,
238
+ options,
239
+ this.customExtensionLocalLocation,
240
+ executionId,
241
+ onTestStarted,
242
+ onTestCompleted,
243
+ onGridSlot,
244
+ onTestIgnored,
245
+ ).forEach((worker, index) => {
246
+ perf.log('before schedule worker.run after createWorkers');
247
+ schedule(() => {
248
+ perf.log('right before worker.run');
249
+ worker.run();
250
+ }, index);
251
+ });
202
252
  });
203
253
 
204
254
  try {
@@ -215,6 +265,10 @@ class ParallelWorkerManager {
215
265
  }
216
266
  }
217
267
 
268
+ /**
269
+ * @param {Function} fn
270
+ * @param {number} index
271
+ */
218
272
  function schedule(fn, index) {
219
273
  if (index === 0) {
220
274
  fn();
@@ -26,9 +26,7 @@ const TDK_CHILD_RESULTS_TIMEOUT = 1000 * 60 * 5;
26
26
  /** @typedef {Awaited<ReturnType<typeof getSuite>>['tests'][number]} ExecutionList */
27
27
 
28
28
  class TestPlanRunner {
29
- /**
30
- * @param {string=} customExtensionLocalLocation
31
- */
29
+ /** @param {string=} customExtensionLocalLocation */
32
30
  constructor(customExtensionLocalLocation) {
33
31
  this.workerManager = new ParallelWorkerManager(customExtensionLocalLocation);
34
32
  this.startTime = Date.now();
@@ -115,7 +113,7 @@ class TestPlanRunner {
115
113
  }
116
114
  const { runKey: apiKey, url: serverUrl } = applitoolsIntegrationData;
117
115
  const tmpSDK = require('@applitools/eyes-sdk-core').makeSDK({ name: 'Testim.io', version: '4.0.0', spec: {} });
118
- await tmpSDK.closeBatch({ settings: { batchId: executionId, serverUrl, apiKey } });
116
+ await tmpSDK.closeBatches({ settings: { batchIds: [executionId], serverUrl, apiKey } });
119
117
  } catch (err) {
120
118
  // If a batch with this name did not exist, do not log an error.
121
119
  if (err.message && (err.message.startsWith('Request failed with status code 404') || err.message.startsWith('no batchIds were set'))) {