@testim/testim-cli 3.244.0 → 3.247.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.
Files changed (36) hide show
  1. package/chromiumInstaller.js +92 -0
  2. package/cli.js +89 -87
  3. package/cliAgentMode.js +39 -114
  4. package/commons/chromedriverWrapper.js +4 -3
  5. package/commons/featureFlags.js +1 -0
  6. package/commons/getSessionPlayerRequire.js +2 -1
  7. package/commons/httpRequestCounters.test.js +9 -9
  8. package/commons/npmWrapper.js +3 -3
  9. package/commons/npmWrapper.test.js +1 -1
  10. package/commons/prepareRunner.js +2 -0
  11. package/commons/socket/baseSocketServiceSocketIO.js +2 -2
  12. package/commons/testimDesiredCapabilitiesBuilder.js +1 -3
  13. package/commons/testimServicesApi.js +45 -39
  14. package/errors.js +2 -1
  15. package/npm-shrinkwrap.json +467 -366
  16. package/package.json +2 -2
  17. package/player/WebDriverHttpRequest.js +45 -37
  18. package/player/chromeLauncherTestPlayer.js +2 -2
  19. package/player/services/playbackTimeoutCalculator.js +5 -1
  20. package/player/stepActions/RefreshStepAction.js +2 -4
  21. package/player/stepActions/inputFileStepAction.js +6 -2
  22. package/player/stepActions/sfdcStepAction.js +27 -0
  23. package/player/stepActions/stepActionRegistrar.js +12 -0
  24. package/player/utils/screenshotUtils.js +2 -2
  25. package/player/utils/windowUtils.js +3 -3
  26. package/player/webdriver.js +51 -52
  27. package/runOptions.js +3 -1
  28. package/runner.js +44 -41
  29. package/services/lambdatestService.js +2 -2
  30. package/services/lambdatestService.test.js +2 -1
  31. package/testRunHandler.js +7 -3
  32. package/testRunStatus.js +2 -1
  33. package/utils.js +54 -15
  34. package/utils.test.js +26 -0
  35. package/workers/BaseWorker.js +9 -7
  36. package/workers/BaseWorker.test.js +11 -12
package/runOptions.js CHANGED
@@ -474,7 +474,7 @@ module.exports = {
474
474
  const originalRequire = Module.prototype.require;
475
475
  Module.prototype.require = function requireThatOverridesSessionPlayer(id) {
476
476
  if (id.endsWith('getSessionPlayerRequire')) {
477
- const sessionPlayerPath = path.resolve(fullPlayerPath, 'src/background/sessionPlayerInit.js');
477
+ const sessionPlayerPath = path.resolve(fullPlayerPath, 'src/background/sessionPlayerInit.ts');
478
478
  return originalRequire.call(this, sessionPlayerPath);
479
479
  }
480
480
  if (id === 'rox-alias') {
@@ -1230,6 +1230,8 @@ module.exports = {
1230
1230
  suiteNames: program.intersectWithSuite.length ? [program.intersectWithSuite].flat() : undefined,
1231
1231
  suiteIds: program.intersectWithSuiteId.length ? [program.intersectWithSuiteId].flat() : undefined,
1232
1232
  },
1233
+
1234
+ downloadBrowser: program.downloadBrowser,
1233
1235
  });
1234
1236
  },
1235
1237
  };
package/runner.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  /* eslint-disable no-console */
4
4
  const { CLI_MODE } = require('./commons/constants');
5
- const Promise = require('bluebird');
6
5
  const _ = require('lodash');
7
6
  const { EDITOR_URL } = require('./commons/config');
8
7
  const tunnel = require('./commons/testimTunnel');
@@ -29,7 +28,7 @@ const featureAvailabilityService = require('./commons/featureAvailabilityService
29
28
  const logger = require('./commons/logger').getLogger('runner');
30
29
 
31
30
  function validateCLIRunsAreAllowed(options) {
32
- const hasCliAccess = _(options).get('company.activePlan.premiumFeatures.allowCLI');
31
+ const hasCliAccess = _.get(options, 'company.activePlan.premiumFeatures.allowCLI');
33
32
 
34
33
  if (!hasCliAccess) {
35
34
  const projectId = options.project;
@@ -38,20 +37,18 @@ function validateCLIRunsAreAllowed(options) {
38
37
  }
39
38
  }
40
39
 
41
- function validateProjectQuotaNotDepleted(options) {
40
+ async function validateProjectQuotaNotDepleted(options) {
42
41
  const projectId = options.project;
43
42
 
44
- return servicesApi.getUsageForCurrentBillingPeriod(projectId)
45
- .then(usage => {
46
- const isExecutionBlocked = usage && usage.isExecutionBlocked;
47
- if (!isExecutionBlocked) {
48
- return;
49
- }
50
-
51
- console.error('You have reached the limit of runs for the billing month, please upgrade your plan at https://www.testim.io/upgrade-contact-us?source=cli');
52
- analytics.track(options.authData.uid, 'execution-quota-surpassed', { projectId });
53
- throw new QuotaDepletedError();
54
- });
43
+ const usage = await servicesApi.getUsageForCurrentBillingPeriod(projectId);
44
+ const isExecutionBlocked = usage && usage.isExecutionBlocked;
45
+ if (!isExecutionBlocked) {
46
+ return;
47
+ }
48
+
49
+ console.error('You have reached the limit of runs for the billing month, please upgrade your plan at https://www.testim.io/upgrade-contact-us?source=cli');
50
+ analytics.track(options.authData.uid, 'execution-quota-surpassed', { projectId });
51
+ throw new QuotaDepletedError();
55
52
  }
56
53
 
57
54
  function validateOptionsForCompany(options, company) {
@@ -60,26 +57,27 @@ function validateOptionsForCompany(options, company) {
60
57
  return;
61
58
  }
62
59
 
63
- const companyRetention = _(company).get('activePlan.premiumFeatures.resultRetention');
60
+ const companyRetention = _.get(company, 'activePlan.premiumFeatures.resultRetention');
64
61
  if (optionsRetention > companyRetention) {
65
62
  throw new ArgError(`Retention days (${optionsRetention}) cannot be greater than the company's retention days (${companyRetention}). Run aborted`);
66
63
  }
67
64
  }
68
65
 
69
- function validateCliAccount(options) {
66
+ async function validateCliAccount(options) {
70
67
  if (options.lightweightMode && options.lightweightMode.disableQuotaBlocking) {
71
- return Promise.resolve();
68
+ return;
72
69
  }
73
- return Promise.all([
74
- validateProjectQuotaNotDepleted(options),
75
- validateCLIRunsAreAllowed(options),
76
- ]).catch(err => {
70
+ try {
71
+ await Promise.all([
72
+ validateProjectQuotaNotDepleted(options),
73
+ validateCLIRunsAreAllowed(options),
74
+ ]);
75
+ } catch (err) {
77
76
  if (err instanceof ArgError || err instanceof QuotaDepletedError) {
78
- return Promise.reject(err);
77
+ throw err;
79
78
  }
80
79
  logger.error('could not validate cli account', { err });
81
- return undefined;
82
- });
80
+ }
83
81
  }
84
82
 
85
83
  function analyticsIdentify(projectId) {
@@ -125,10 +123,10 @@ function setCompany(options, company) {
125
123
  if (onprem) {
126
124
  const { mode, extensionPath, ext, playerPath } = options;
127
125
  if ([CLI_MODE.SELENIUM].includes(mode) && !playerPath) {
128
- return Promise.reject(new ArgError('in selenium on prem mode --player-path must be provided'));
126
+ throw new ArgError('in selenium on prem mode --player-path must be provided');
129
127
  }
130
128
  if (mode === 'extension' && !extensionPath && !ext) {
131
- return Promise.reject(new ArgError('In extension on prem mode --ext or --extension-path must be provided'));
129
+ throw new ArgError('In extension on prem mode --ext or --extension-path must be provided');
132
130
  }
133
131
  }
134
132
  const isPOC = Boolean(activePlan.isPoc);
@@ -153,16 +151,14 @@ function setCompany(options, company) {
153
151
  isStartUp,
154
152
  activePlan,
155
153
  };
156
- return undefined;
157
154
  }
158
155
 
159
156
  function setSystemInfo(options, editorConfig) {
160
157
  if (EDITOR_URL) {
161
158
  options.editorUrl = EDITOR_URL;
162
- return Promise.resolve();
159
+ return;
163
160
  }
164
161
  options.editorUrl = editorConfig.editorUrl;
165
- return undefined;
166
162
  }
167
163
 
168
164
  function setAllGrids(options, allGrids) {
@@ -198,7 +194,7 @@ async function setMockNetworkRules(options) {
198
194
  }
199
195
  }
200
196
 
201
- function runRunner(options, customExtensionLocalLocation) {
197
+ async function runRunner(options, customExtensionLocalLocation) {
202
198
  perf.log('in runner.js runRunner');
203
199
 
204
200
  const { project, remoteRunId, useLocalChromeDriver, useChromeLauncher } = options;
@@ -210,15 +206,21 @@ function runRunner(options, customExtensionLocalLocation) {
210
206
  npmDriver.checkNpmVersion();
211
207
  perf.log('in runner.js after checkNpmVersion');
212
208
 
213
- return validateCliAccount(options)
214
- .log('in runRunner before tunnel.connect')
215
- .then(() => tunnel.connect(options))
216
- .log('in runRunner after tunnel.connect')
217
- .then(() => new TestPlanRunner(customExtensionLocalLocation).run(options))
218
- .log('before tunnel.disconnect')
219
- .tap(() => tunnel.disconnect(options))
220
- .tap(() => gridService.keepAlive.end(project))
221
- .log('after tunnel.disconnect and gridService.keepAlive.end');
209
+ await validateCliAccount(options);
210
+
211
+ perf.log('in runRunner before tunnel.connect');
212
+ await tunnel.connect(options);
213
+ perf.log('in runRunner after tunnel.connect');
214
+
215
+ const testPlanRunner = new TestPlanRunner(customExtensionLocalLocation);
216
+ const results = await testPlanRunner.run(options);
217
+
218
+ perf.log('before tunnel.disconnect');
219
+ await tunnel.disconnect(options);
220
+ await gridService.keepAlive.end(project);
221
+ perf.log('after tunnel.disconnect and gridService.keepAlive.end');
222
+
223
+ return results;
222
224
  }
223
225
 
224
226
  function showFreeGridRunWarningIfNeeded(options) {
@@ -237,7 +239,7 @@ function showFreeGridRunWarningIfNeeded(options) {
237
239
  * - Reporting the user to analytics
238
240
  * - Authenticating the user and exchanging their token for a jwt
239
241
  * - Sets the grids for the company and validates the user has permission to run the CLI
240
- * @param {Object} options - the run options rpassed to the CLI, namely the project and token
242
+ * @param {Object} options - the run options passed to the CLI, namely the project and token
241
243
  */
242
244
  async function init(options) {
243
245
  perf.log('start runner init');
@@ -273,6 +275,7 @@ async function init(options) {
273
275
  }
274
276
 
275
277
  if (options.lightweightMode && options.lightweightMode.type === 'turboMode') {
278
+ // eslint-disable-next-line max-len
276
279
  console.log('\nTurbo mode will ignore step delays. Test artifacts like screenshots and logs will only be saved for failed runs. For more information see our docs: https://help.testim.io/docs/turbo-mode');
277
280
  }
278
281
 
@@ -288,5 +291,5 @@ async function init(options) {
288
291
 
289
292
  module.exports = {
290
293
  run: runRunner,
291
- init: Promise.method(init),
294
+ init,
292
295
  };
@@ -1,6 +1,6 @@
1
1
  const os = require('os');
2
2
  const childProcess = require('child_process');
3
- const Promise = require('bluebird');
3
+ const pRetry = require('p-retry');
4
4
  const fse = require('fs-extra');
5
5
  const portfinder = require('portfinder');
6
6
  const ms = require('ms');
@@ -154,7 +154,7 @@ class LambdatestService {
154
154
 
155
155
  // verify that LT tunnel strated successfully
156
156
  try {
157
- const ltInfo = await utils.runWithRetries(() => httpRequest.get(`http://127.0.0.1:${infoAPIPort}/api/v1.0/info`, {}, {}, undefined, { skipProxy: true }), 30, 2000);
157
+ const ltInfo = await pRetry(() => httpRequest.get(`http://127.0.0.1:${infoAPIPort}/api/v1.0/info`, {}, {}, undefined, { skipProxy: true }), { retries: 30, minTimeout: 2000 });
158
158
  logger.info('LT tunnel info', ltInfo);
159
159
  } catch (err) {
160
160
  logger.error('Failed to start LT tunnel', { err, stdoutResult, stderrResult });
@@ -4,6 +4,7 @@ const httpRequest = require('../commons/httpRequest');
4
4
  const LambdatestService = require('./lambdatestService');
5
5
  const utils = require('../utils');
6
6
  const fse = require('fs-extra');
7
+ const { AbortError } = require('p-retry');
7
8
  const os = require('os');
8
9
  const childProcess = require('child_process');
9
10
  const EventEmitter = require('events');
@@ -312,7 +313,7 @@ describe('LambdatestService', () => {
312
313
  sinon.assert.calledOnce(httpGetStub);
313
314
  });
314
315
  it('should throw when tunnel did not start', async () => {
315
- sandbox.stub(utils, 'runWithRetries').rejects(new Error('tunnel did not start'));
316
+ httpGetStub.rejects(new AbortError('tunnel did not start'));
316
317
  await expect(LambdatestService.connectTunnel({ ...credentials })).to.be.rejectedWith(Error, 'tunnel did not start');
317
318
  processMock.stdout.emit('data', '');
318
319
  processMock.stderr.emit('data', '');
package/testRunHandler.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const pRetry = require('p-retry');
3
4
  const _ = require('lodash');
4
5
  const testimCustomToken = require('./commons/testimCustomToken');
5
6
  const remoteStepService = require('./commons/socket/remoteStepService');
@@ -312,12 +313,12 @@ class TestRun {
312
313
  const { sessionId: extensionSessionId } = extensionSession || {};
313
314
  perf.log('before Runtime.evaluate');
314
315
 
315
- await utils.runWithRetries(async () => {
316
+ await pRetry(async () => {
316
317
  const { result } = await cdpTestRunner.cdpCommand('Runtime.evaluate', { expression: 'typeof runTestimTest !== \'undefined\'', returnByValue: true }, extensionSessionId);
317
318
  if (!result.value) {
318
319
  throw new Error('runTestimTest not available on global scope');
319
320
  }
320
- }, 100, 30);
321
+ }, { retries: 100, minTimeout: 30 });
321
322
 
322
323
  perf.log('after wait for runTestimTest function');
323
324
  const { result } = await cdpTestRunner.cdpCommand(
@@ -519,7 +520,10 @@ class TestRun {
519
520
  waitForTestEnd();
520
521
  }
521
522
  })
522
- .tap(() => this.onCompletedCleanup())
523
+ .then(async res => {
524
+ await this.onCompletedCleanup();
525
+ return res;
526
+ })
523
527
  .finally(() => onConnected && !this._options.disableSockets && testResultService.off('socket-connected', onConnected));
524
528
  }
525
529
 
package/testRunStatus.js CHANGED
@@ -442,7 +442,8 @@ RunStatus.prototype.executionStart = function (executionId, projectId, startTime
442
442
  logger.error('Failed to start suite', { err });
443
443
  // eslint-disable-next-line no-console
444
444
  console.error('Failed to start test run. Please contact support@testim.io');
445
- }).return({ beforeTests, tests, afterTests });
445
+ })
446
+ .then(() => ({ beforeTests, tests, afterTests }));
446
447
  });
447
448
  };
448
449
 
package/utils.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const moment = require('moment');
4
- const retry = require('bluebird-retry');
4
+ const pRetry = require('p-retry');
5
5
  const _ = require('lodash');
6
6
  const Promise = require('bluebird');
7
7
  const fs = Promise.promisifyAll(require('fs'));
@@ -10,6 +10,10 @@ const { sessionType, testStatus: testStatusConst } = require('./commons/constant
10
10
  const path = require('path');
11
11
  const httpRequest = require('./commons/httpRequest');
12
12
  const decompress = require('decompress');
13
+ const os = require('os');
14
+
15
+ const HOMEDIR = os.homedir();
16
+ const TESTIM_BROWSER_DIR = path.join(HOMEDIR, '.testim-browser-profile');
13
17
 
14
18
  const OSS = [
15
19
  { osName: 'Linux', bs: { os: 'LINUX' }, sl: { platform: 'Linux' } },
@@ -96,17 +100,6 @@ function getDurationSec(ms) {
96
100
  return moment.duration(ms).asSeconds();
97
101
  }
98
102
 
99
- function runWithRetries(task, times, interval, predicate) {
100
- times = times || 3;
101
- interval = interval || 3000;
102
-
103
- const opt = { max_tries: times, interval, throw_original: true };
104
- if (predicate) {
105
- opt.predicate = predicate;
106
- }
107
- return retry(task, opt);
108
- }
109
-
110
103
  function getRunnerVersion() {
111
104
  try {
112
105
  const pack = require(`${__dirname}/package.json`);
@@ -194,7 +187,7 @@ function extractElementId(element) {
194
187
  }
195
188
 
196
189
  function isURL(path) {
197
- const legacyPattern = new RegExp('^(https?:\\/\\/)', 'i');
190
+ const legacyPattern = /^(https?:\/\/)/i;
198
191
 
199
192
  // https://gist.github.com/dperini/729294 (validator.js based on).
200
193
  const pattern = new RegExp(
@@ -243,7 +236,7 @@ function isURL(path) {
243
236
  }
244
237
 
245
238
  const DOWNLOAD_RETRY = 3;
246
- const download = async (url) => runWithRetries(() => httpRequest.download(url), DOWNLOAD_RETRY);
239
+ const download = async (url) => pRetry(() => httpRequest.download(url), { retries: DOWNLOAD_RETRY });
247
240
 
248
241
  const downloadAndSave = async (url, saveToLocation) => {
249
242
  const res = await download(url);
@@ -311,6 +304,16 @@ function getPlanType(plan) {
311
304
  return 'free';
312
305
  }
313
306
 
307
+ /**
308
+ * @param time {number} in ms
309
+ * @returns {Promise}
310
+ */
311
+ function delay(time) {
312
+ return new Promise(((resolve) => {
313
+ setTimeout(resolve, time);
314
+ }));
315
+ }
316
+
314
317
  const calcPercentile = (arr, percentile) => {
315
318
  if (arr.length === 0) return 0;
316
319
  if (typeof percentile !== 'number') throw new TypeError('p must be a number');
@@ -357,12 +360,45 @@ function groupTestsByRetries(testResults = []) { // NOTE: This duplicates a func
357
360
  .value();
358
361
  }
359
362
 
363
+ async function getCdpAddressForHost(browserInstanceHost, timeout) {
364
+ try {
365
+ /**
366
+ Example response:
367
+ {
368
+ "Browser": "Chrome/81.0.4044.138",
369
+ "Protocol-Version": "1.3",
370
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
371
+ "V8-Version": "8.1.307.32",
372
+ "WebKit-Version": "537.36 (@8c6c7ba89cc9453625af54f11fd83179e23450fa)",
373
+ "webSocketDebuggerUrl": "ws://localhost:58938/devtools/browser/d4290379-ec08-4d03-a41a-ab9d9d4c36ac"
374
+ }
375
+ */
376
+ const debuggerAddress = await httpRequest.get(`http://${browserInstanceHost}/json/version`, undefined, undefined, timeout);
377
+ return debuggerAddress.webSocketDebuggerUrl;
378
+ } catch (e) {
379
+ throw new Error('unable to connect to devtools server');
380
+ }
381
+ }
382
+
383
+ function getArgsOnRemoteRunFailure() {
384
+ const { argv: args } = process;
385
+ if (!args.includes('--remoteRunId')) {
386
+ return undefined;
387
+ }
388
+ return {
389
+ remoteRunId: args[args.indexOf('--remoteRunId') + 1],
390
+ projectId: args[args.indexOf('--project') + 1],
391
+ token: args[args.indexOf('--token') + 1],
392
+ };
393
+ }
394
+
395
+
360
396
  module.exports = {
397
+ TESTIM_BROWSER_DIR,
361
398
  removePropertyFromObject,
362
399
  getTestUrl,
363
400
  getDuration,
364
401
  getDurationSec,
365
- runWithRetries,
366
402
  getRunnerVersion,
367
403
  getEnginesVersion,
368
404
  getEnginesVersionAsync,
@@ -391,4 +427,7 @@ module.exports = {
391
427
  isQuarantineAndNotRemoteRun,
392
428
  groupTestsByRetries,
393
429
  getPlanType,
430
+ delay,
431
+ getCdpAddressForHost,
432
+ getArgsOnRemoteRunFailure,
394
433
  };
package/utils.test.js CHANGED
@@ -39,4 +39,30 @@ describe('utils', () => {
39
39
  .to.equal('http://localhost:8080/#/project/project/branch/encoded%2520branch/test/test?result-id=result');
40
40
  });
41
41
  });
42
+
43
+ describe('getArgsOnRemoteRunFailure', () => {
44
+ let originalArgv;
45
+
46
+ beforeEach(() => {
47
+ originalArgv = process.argv;
48
+ });
49
+
50
+ afterEach(() => {
51
+ process.argv = originalArgv;
52
+ });
53
+
54
+ it('should return undefined if no remote run is current', () => {
55
+ process.argv = ['node', 'file.js', '--token', 'token', '--project', 'project-id'];
56
+ expect(utils.getArgsOnRemoteRunFailure()).to.be.undefined;
57
+ });
58
+
59
+ it('should return details if remote run is current', () => {
60
+ process.argv = ['node', 'file.js', '--token', 'token', '--project', 'project-id', '--remoteRunId', 'remote-run-id'];
61
+ expect(utils.getArgsOnRemoteRunFailure()).to.eql({
62
+ remoteRunId: 'remote-run-id',
63
+ projectId: 'project-id',
64
+ token: 'token',
65
+ });
66
+ });
67
+ });
42
68
  });
@@ -2,6 +2,7 @@
2
2
 
3
3
  const Bluebird = require('bluebird');
4
4
  const moment = require('moment');
5
+ const pRetry = require('p-retry');
5
6
  const ms = require('ms');
6
7
 
7
8
  const { timeoutMessages, testRunStatus, stepResult, runnerTestStatus } = require('../commons/constants');
@@ -116,7 +117,8 @@ class BaseWorker {
116
117
  try {
117
118
  perf.log('before getSlotOnce retries');
118
119
  let failedGetSlotAttempts = 0;
119
- let gridInfo = await utils.runWithRetries(async () => {
120
+
121
+ let gridInfo = await pRetry(async () => {
120
122
  const startTime = Date.now();
121
123
  try {
122
124
  return await Bluebird.resolve(this.getSlotOnce(testRunHandler))
@@ -124,10 +126,10 @@ class BaseWorker {
124
126
  } catch (error) {
125
127
  logger.error('error getting grid slot', { error, testId: this.testId, testResultId: this.testResultId, executionId: this.executionId });
126
128
  failedGetSlotAttempts++;
127
- await Bluebird.delay(this.options.getBrowserTimeout - (Date.now() - startTime));
129
+ await utils.delay(this.options.getBrowserTimeout - (Date.now() - startTime));
128
130
  throw error;
129
131
  }
130
- }, this.options.getBrowserRetries);
132
+ }, { retries: this.options.getBrowserRetries - 1, minTimeout: 0 });
131
133
  perf.log('after getSlotOnce retries');
132
134
 
133
135
  perf.log('before getBrowserOnce retries');
@@ -136,7 +138,7 @@ class BaseWorker {
136
138
  throw new Error('No free browser slots in desired grid');
137
139
  }
138
140
  let failedGetBrowserAttempts = 0;
139
- testPlayer = await utils.runWithRetries(async () => {
141
+ testPlayer = await pRetry(async () => {
140
142
  const startTime = Date.now();
141
143
  const player = this.initPlayer(testRunHandler);
142
144
  try {
@@ -162,11 +164,11 @@ class BaseWorker {
162
164
  player.onDone();
163
165
 
164
166
  if (!(error instanceof PageNotAvailableError)) {
165
- await Bluebird.delay(this.options.getBrowserTimeout - (Date.now() - startTime));
167
+ await utils.delay(this.options.getBrowserTimeout - (Date.now() - startTime));
166
168
  }
167
169
  throw error;
168
170
  }
169
- }, getBrowserRetriesNumber, undefined, (err) => !(err instanceof PageNotAvailableError));
171
+ }, { retries: getBrowserRetriesNumber - 1, minTimeout: 0 });
170
172
  perf.log('after getBrowserOnce retries');
171
173
  } catch (err) {
172
174
  await releasePlayer(this.id, this.releaseSlotOnTestFinished, projectId, testPlayer);
@@ -235,7 +237,7 @@ class BaseWorker {
235
237
  testResult.testRetryKey = testRetryKey;
236
238
  await this.onTestCompleted(this.id, this.testId, testResult, sessionId, shouldRerun);
237
239
  if (this.executionQueue.hasMoreTests() && !(this.options.lightweightMode && this.options.lightweightMode.general)) {
238
- await Bluebird.delay(DELAY_BETWEEN_TESTS);
240
+ await utils.delay(DELAY_BETWEEN_TESTS);
239
241
  }
240
242
  await this.runTestCleanup();
241
243
  if (shouldRerun) {
@@ -3,10 +3,9 @@ const proxyquire = require('proxyquire');
3
3
  const { expect, sinon } = require('../../test/utils/testUtils');
4
4
  const gridService = require('../services/gridService');
5
5
  const reporter = require('../reports/reporter');
6
- const Bluebird = require('bluebird');
7
6
  const { PageNotAvailableError, GridError, GetBrowserError } = require('../errors');
8
7
  const servicesApi = require('../commons/testimServicesApi');
9
- const { releasePlayer } = require('./workerUtils');
8
+ const utils = require('../utils');
10
9
 
11
10
 
12
11
  describe('BaseWorker', () => {
@@ -41,7 +40,7 @@ describe('BaseWorker', () => {
41
40
  getGridSlotStub = sinon.stub(gridService, 'getGridSlot').resolves({});
42
41
 
43
42
 
44
- sandbox.stub(Bluebird, 'delay').returns(Promise.resolve());
43
+ sandbox.stub(utils, 'delay').returns(Promise.resolve());
45
44
  sandbox.stub(reporter);
46
45
  });
47
46
 
@@ -57,7 +56,7 @@ describe('BaseWorker', () => {
57
56
  it('should get grid slot from server', async () => {
58
57
  await worker.getTestPlayer(testRunHandlerMock);
59
58
  sinon.assert.calledOnce(getGridSlotStub);
60
- sinon.assert.notCalled(Bluebird.delay);
59
+ sinon.assert.notCalled(utils.delay);
61
60
  });
62
61
  it('should retry getting slot until it succeeds', async () => {
63
62
  getGridSlotStub.onFirstCall().rejects();
@@ -65,7 +64,7 @@ describe('BaseWorker', () => {
65
64
 
66
65
  await worker.getTestPlayer(testRunHandlerMock);
67
66
  sinon.assert.calledTwice(getGridSlotStub);
68
- sinon.assert.calledTwice(Bluebird.delay);
67
+ sinon.assert.calledOnce(utils.delay);
69
68
  });
70
69
  it('should not proceed to getting browser if all retries used for getting a slot', async () => {
71
70
  getGridSlotStub.resolves({});
@@ -74,7 +73,7 @@ describe('BaseWorker', () => {
74
73
  sinon.assert.calledOnce(getGridSlotStub);
75
74
  sinon.assert.notCalled(worker.getBrowserOnce);
76
75
  sinon.assert.notCalled(worker.initPlayer);
77
- sinon.assert.notCalled(Bluebird.delay);
76
+ sinon.assert.notCalled(utils.delay);
78
77
  });
79
78
  });
80
79
 
@@ -84,7 +83,7 @@ describe('BaseWorker', () => {
84
83
  sinon.assert.calledOnce(getGridSlotStub);
85
84
  sinon.assert.calledOnce(worker.getBrowserOnce);
86
85
  sinon.assert.calledOnce(worker.initPlayer);
87
- sinon.assert.notCalled(Bluebird.delay);
86
+ sinon.assert.notCalled(utils.delay);
88
87
  });
89
88
  it('should retry getting browser until it succeeds', async () => {
90
89
  worker.getBrowserOnce.onFirstCall().rejects();
@@ -95,16 +94,16 @@ describe('BaseWorker', () => {
95
94
  sinon.assert.calledTwice(worker.getBrowserOnce);
96
95
  sinon.assert.calledTwice(worker.initPlayer);
97
96
  sinon.assert.calledOnce(testPlayerMock.onDone);
98
- sinon.assert.calledTwice(Bluebird.delay);
97
+ sinon.assert.calledOnce(utils.delay);
99
98
  });
100
99
  it('should not retry if page is not available', async () => {
101
100
  worker.getBrowserOnce.throws(() => new PageNotAvailableError());
102
101
 
103
- await expect(worker.getTestPlayer(testRunHandlerMock)).to.eventually.be.rejectedWith(PageNotAvailableError);
102
+ await expect(worker.getTestPlayer(testRunHandlerMock)).to.eventually.be.rejectedWith(Error);
104
103
  sinon.assert.calledOnce(getGridSlotStub);
105
104
  sinon.assert.calledOnce(worker.getBrowserOnce);
106
105
  sinon.assert.calledOnce(testPlayerMock.onDone);
107
- sinon.assert.notCalled(Bluebird.delay);
106
+ sinon.assert.notCalled(utils.delay);
108
107
  });
109
108
  it('should handle get grid error', async () => {
110
109
  worker.getBrowserOnce.throws(() => new GridError());
@@ -153,8 +152,8 @@ describe('BaseWorker', () => {
153
152
  getHybridGridProviderStub.onFirstCall().resolves({ provider: 'loacker', connectionDetails: { host: 'localhost', external: { user: 'user', key: 'password' } } });
154
153
  getHybridGridProviderStub.onSecondCall().resolves({ provider: 'nitzan', connectionDetails: { host: 'google.com', port: 443 } });
155
154
  getHybridGridProviderStub.onThirdCall().resolves({ provider: 'a', connectionDetails: { host: 'google.com', port: 4444 } });
156
- worker.getBrowserOnce.onFirstCall().rejects();
157
- worker.getBrowserOnce.onSecondCall().rejects();
155
+ worker.getBrowserOnce.onFirstCall().rejects(new Error());
156
+ worker.getBrowserOnce.onSecondCall().rejects(new Error());
158
157
  worker.getBrowserOnce.onThirdCall().resolves({});
159
158
 
160
159
  await worker.getTestPlayer(testRunHandlerMock);