@testim/testim-cli 3.196.0 → 3.197.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 (66) hide show
  1. package/README.md +1 -1
  2. package/cli/onExit.js +12 -1
  3. package/cli.js +5 -1
  4. package/commons/constants.js +0 -25
  5. package/commons/featureFlags.js +2 -0
  6. package/commons/socket/testResultService.js +4 -14
  7. package/commons/testimAnalytics.js +0 -1
  8. package/commons/testimDesiredCapabilitiesBuilder.js +0 -94
  9. package/commons/testimServicesApi.js +9 -79
  10. package/executionQueue.js +7 -4
  11. package/npm-shrinkwrap.json +948 -512
  12. package/package.json +3 -1
  13. package/player/stepActions/baseJsStepAction.js +5 -1
  14. package/player/stepActions/pixelValidationStepAction.js +28 -0
  15. package/player/stepActions/salesforceAutoLoginStepAction.js +5 -3
  16. package/player/stepActions/stepActionRegistrar.js +3 -48
  17. package/player/utils/eyeSdkService.js +230 -0
  18. package/reports/consoleReporter.js +0 -20
  19. package/reports/reporter.js +0 -21
  20. package/runOptions.js +13 -89
  21. package/runner.js +3 -44
  22. package/runners/{strategies/LocalStrategy.js → ParallelWorkerManager.js} +59 -68
  23. package/runners/TestPlanRunner.js +286 -67
  24. package/runners/runnerUtils.js +73 -0
  25. package/services/analyticsService.js +94 -0
  26. package/services/gridService.js +24 -16
  27. package/services/gridService.test.js +21 -21
  28. package/testRunHandler.js +1 -1
  29. package/testRunStatus.js +13 -9
  30. package/utils.js +5 -5
  31. package/workers/BaseWorker.js +38 -39
  32. package/workers/BaseWorker.test.js +1 -1
  33. package/workers/WorkerExtensionSingleBrowser.js +6 -3
  34. package/commons/apkUploader/apkUploader.js +0 -46
  35. package/commons/apkUploader/apkUploaderFactory.js +0 -68
  36. package/commons/apkUploader/deviceFarmApkUploader.js +0 -41
  37. package/commons/apkUploader/saucelabsApkUploader.js +0 -36
  38. package/commons/apkUploader/testObjectApkUploader.js +0 -34
  39. package/player/mobile/mobileTestPlayer.js +0 -80
  40. package/player/mobile/mobileWebDriver.js +0 -155
  41. package/player/mobile/services/frameLocatorMock.js +0 -18
  42. package/player/mobile/services/mobilePortSelector.js +0 -22
  43. package/player/mobile/services/mobileTabService.js +0 -241
  44. package/player/mobile/utils/mobileScreenshotUtils.js +0 -46
  45. package/player/mobile/utils/mobileWindowUtils.js +0 -84
  46. package/player/stepActions/mobile/android/androidLocateStepAction.js +0 -122
  47. package/player/stepActions/mobile/android/androidLongClickStepAction.js +0 -12
  48. package/player/stepActions/mobile/android/androidScrollStepAction.js +0 -134
  49. package/player/stepActions/mobile/android/androidSpecialKeyStepAction.js +0 -22
  50. package/player/stepActions/mobile/android/androidSwipeStepAction.js +0 -32
  51. package/player/stepActions/mobile/androidGlobalActionStepAction.js +0 -12
  52. package/player/stepActions/mobile/androidTapStepAction.js +0 -19
  53. package/player/stepActions/mobile/androidTextChangeStepAction.js +0 -23
  54. package/player/stepActions/mobile/ios/iosLocateStepAction.js +0 -124
  55. package/player/stepActions/mobile/ios/iosScrollStepAction.js +0 -76
  56. package/runners/AnonymousTestPlanRunner.js +0 -106
  57. package/runners/BaseRunner.js +0 -42
  58. package/runners/BaseTestPlanRunner.js +0 -194
  59. package/runners/DeviceFarmRemoteRunner.js +0 -50
  60. package/runners/SchedulerRemoteRunner.js +0 -47
  61. package/runners/strategies/BaseStrategy.js +0 -86
  62. package/runners/strategies/DeviceFarmStrategy.js +0 -195
  63. package/runners/strategies/LocalDeviceFarmStrategy.js +0 -12
  64. package/runners/strategies/LocalTestStrategy.js +0 -14
  65. package/runners/strategies/Strategy.js +0 -17
  66. package/workers/WorkerAppium.js +0 -70
@@ -175,19 +175,19 @@ describe('gridService', () => {
175
175
 
176
176
  describe('getGridSlot', () => {
177
177
  let getGridByIdStub;
178
- let getGridByProjectStub;
178
+ let getGridByNameStub;
179
179
  let onGridSlot;
180
180
  let addItemToGridCacheStub;
181
181
 
182
182
  beforeEach(() => {
183
183
  getGridByIdStub = sinon.stub(servicesApi, 'getGridById').resolves({ grid: { gridId: 'gridId', type: 'gridId' }, status: 'success' });
184
- getGridByProjectStub = sinon.stub(servicesApi, 'getGridByProject').resolves({ grid: { gridId: 'gridId', type: 'gridName' }, status: 'success' });
184
+ getGridByNameStub = sinon.stub(servicesApi, 'getGridByName').resolves({ grid: { gridId: 'gridId', type: 'gridName' }, status: 'success' });
185
185
  addItemToGridCacheStub = sinon.stub(gridService, 'addItemToGridCache').callThrough();
186
186
  onGridSlot = sinon.stub().resolves();
187
187
  });
188
188
  afterEach(() => {
189
189
  getGridByIdStub.restore();
190
- getGridByProjectStub.restore();
190
+ getGridByNameStub.restore();
191
191
  addItemToGridCacheStub.restore();
192
192
  });
193
193
 
@@ -195,7 +195,7 @@ describe('gridService', () => {
195
195
  const slot = await gridService.getGridSlot('browser', 'executionId', 'testResultId', onGridSlot, { useLocalChromeDriver: true }, 'workerId');
196
196
  expect(slot).to.eql({ mode: 'local' });
197
197
  sinon.assert.notCalled(getGridByIdStub);
198
- sinon.assert.notCalled(getGridByProjectStub);
198
+ sinon.assert.notCalled(getGridByNameStub);
199
199
  sinon.assert.notCalled(addItemToGridCacheStub);
200
200
  sinon.assert.calledOnce(onGridSlot);
201
201
  });
@@ -204,7 +204,7 @@ describe('gridService', () => {
204
204
  const slot = await gridService.getGridSlot('browser', 'executionId', 'testResultId', onGridSlot, { useChromeLauncher: true }, 'workerId');
205
205
  expect(slot).to.eql({ mode: 'local' });
206
206
  sinon.assert.notCalled(getGridByIdStub);
207
- sinon.assert.notCalled(getGridByProjectStub);
207
+ sinon.assert.notCalled(getGridByNameStub);
208
208
  sinon.assert.notCalled(addItemToGridCacheStub);
209
209
  sinon.assert.calledOnce(onGridSlot);
210
210
  });
@@ -213,7 +213,7 @@ describe('gridService', () => {
213
213
  const slot = await gridService.getGridSlot('browser', 'executionId', 'testResultId', onGridSlot, { host: 'localhost', port: 4444 }, 'workerId');
214
214
  expect(slot).to.shallowDeepEqual({ type: 'hostAndPort', host: 'localhost', port: 4444 });
215
215
  sinon.assert.notCalled(getGridByIdStub);
216
- sinon.assert.notCalled(getGridByProjectStub);
216
+ sinon.assert.notCalled(getGridByNameStub);
217
217
  sinon.assert.notCalled(addItemToGridCacheStub);
218
218
  sinon.assert.calledOnce(onGridSlot);
219
219
  });
@@ -222,7 +222,7 @@ describe('gridService', () => {
222
222
  const slot = await gridService.getGridSlot('browser', 'executionId', 'testResultId', onGridSlot, { gridId: 'gridId' }, 'workerId');
223
223
  expect(slot).to.shallowDeepEqual({ type: 'gridId', gridId: 'gridId' });
224
224
  sinon.assert.calledOnce(getGridByIdStub);
225
- sinon.assert.notCalled(getGridByProjectStub);
225
+ sinon.assert.notCalled(getGridByNameStub);
226
226
  sinon.assert.calledOnce(addItemToGridCacheStub);
227
227
  sinon.assert.calledOnce(onGridSlot);
228
228
  });
@@ -230,7 +230,7 @@ describe('gridService', () => {
230
230
  it('should get grid from server when passing grid name', async () => {
231
231
  const slot = await gridService.getGridSlot('browser', 'executionId', 'testResultId', onGridSlot, { grid: 'gridName' }, 'workerId');
232
232
  expect(slot).to.shallowDeepEqual({ type: 'gridName', gridId: 'gridId' });
233
- sinon.assert.calledOnce(getGridByProjectStub);
233
+ sinon.assert.calledOnce(getGridByNameStub);
234
234
  sinon.assert.notCalled(getGridByIdStub);
235
235
  sinon.assert.calledOnce(addItemToGridCacheStub);
236
236
  sinon.assert.calledOnce(onGridSlot);
@@ -282,9 +282,9 @@ describe('gridService', () => {
282
282
  });
283
283
 
284
284
  it('should call releaseGridSlot with the correct parameters', async () => {
285
- gridService.addItemToGridCache('workerId', 'gridId', 'slotId', 'chrome');
285
+ gridService.addItemToGridCache('workerId', 'companyId', 'gridId', 'slotId', 'chrome');
286
286
  await gridService.releaseGridSlot('workerId', 'projectId');
287
- sinon.assert.calledWith(releaseGridSlotStub, 'projectId', 'slotId', 'gridId', 'chrome');
287
+ sinon.assert.calledWith(releaseGridSlotStub, 'companyId', 'projectId', 'slotId', 'gridId', 'chrome');
288
288
  });
289
289
 
290
290
  it('should not call releaseGridSlot if the workerId is not in the cache', async () => {
@@ -293,16 +293,16 @@ describe('gridService', () => {
293
293
  });
294
294
 
295
295
  it('should not call releaseGridSlot if no slotId', async () => {
296
- gridService.addItemToGridCache('workerId', 'gridId');
296
+ gridService.addItemToGridCache('workerId', 'companyId', 'gridId');
297
297
  await gridService.releaseGridSlot('workerId', 'projectId');
298
298
  sinon.assert.notCalled(releaseGridSlotStub);
299
299
  });
300
300
 
301
301
  it('should handle releaseGridSlot request error', async () => {
302
302
  releaseGridSlotStub.rejects({ });
303
- gridService.addItemToGridCache('workerId', 'gridId', 'slotId', 'chrome');
303
+ gridService.addItemToGridCache('workerId', 'companyId', 'gridId', 'slotId', 'chrome');
304
304
  await gridService.releaseGridSlot('workerId', 'projectId');
305
- sinon.assert.calledWith(releaseGridSlotStub, 'projectId', 'slotId', 'gridId', 'chrome');
305
+ sinon.assert.calledWith(releaseGridSlotStub, 'companyId', 'projectId', 'slotId', 'gridId', 'chrome');
306
306
  });
307
307
  });
308
308
 
@@ -322,15 +322,15 @@ describe('gridService', () => {
322
322
  });
323
323
 
324
324
  it('should send keepAlive to server on an interval', async () => {
325
- gridService.addItemToGridCache('workerId', 'gridId', 'slotId', 'chrome');
325
+ gridService.addItemToGridCache('workerId', 'companyId', 'gridId', 'slotId', 'chrome');
326
326
  await gridService.keepAlive.start('projectId');
327
327
  clock.tick(10010);
328
- sinon.assert.calledOnceWithExactly(keepAliveStub, 'projectId', [{ gridId: 'gridId', slotId: 'slotId', browser: 'chrome' }]);
328
+ sinon.assert.calledOnceWithExactly(keepAliveStub, 'projectId', [{ gridId: 'gridId', companyId: 'companyId', slotId: 'slotId', browser: 'chrome' }]);
329
329
  await gridService.releaseGridSlot('workerId', 'projectId');
330
330
  });
331
331
 
332
332
  it('should send keepAlive to server every 10 seconds', async () => {
333
- gridService.addItemToGridCache('workerId', 'gridId', 'slotId', 'chrome');
333
+ gridService.addItemToGridCache('workerId', 'companyId', 'gridId', 'slotId', 'chrome');
334
334
  await gridService.keepAlive.start('projectId');
335
335
  clock.tick(30010);
336
336
  sinon.assert.calledThrice(keepAliveStub);
@@ -345,7 +345,7 @@ describe('gridService', () => {
345
345
 
346
346
  it('should handle keepAlive request error', async () => {
347
347
  keepAliveStub.rejects({ });
348
- gridService.addItemToGridCache('workerId', 'gridId', 'slotId', 'chrome');
348
+ gridService.addItemToGridCache('workerId', 'companyId', 'gridId', 'slotId', 'chrome');
349
349
  await gridService.keepAlive.start('projectId');
350
350
  clock.tick(10010);
351
351
  sinon.assert.calledOnce(keepAliveStub);
@@ -353,13 +353,13 @@ describe('gridService', () => {
353
353
  });
354
354
 
355
355
  it('should release all slots when ending', async () => {
356
- gridService.addItemToGridCache('workerId', 'gridId', 'slotId', 'chrome');
357
- gridService.addItemToGridCache('workerId1', 'gridId', 'slotId1', 'firefox');
356
+ gridService.addItemToGridCache('workerId', 'companyId', 'gridId', 'slotId', 'chrome');
357
+ gridService.addItemToGridCache('workerId1', 'companyId', 'gridId', 'slotId1', 'firefox');
358
358
  await gridService.keepAlive.start('projectId');
359
359
  await gridService.keepAlive.end('projectId');
360
360
  sinon.assert.calledTwice(releaseGridSlotStub);
361
- sinon.assert.calledWith(releaseGridSlotStub, 'projectId', 'slotId', 'gridId', 'chrome');
362
- sinon.assert.calledWith(releaseGridSlotStub, 'projectId', 'slotId1', 'gridId', 'firefox');
361
+ sinon.assert.calledWith(releaseGridSlotStub, 'companyId', 'projectId', 'slotId', 'gridId', 'chrome');
362
+ sinon.assert.calledWith(releaseGridSlotStub, 'companyId', 'projectId', 'slotId1', 'gridId', 'firefox');
363
363
  });
364
364
 
365
365
  it('should not release slots if there is no slots in use', async () => {
package/testRunHandler.js CHANGED
@@ -241,7 +241,7 @@ TestRun.prototype.clearTestResult = function () {
241
241
  // make sure the execution is created by now
242
242
  await this._testRunStatus.waitForExecutionStartedFinished();
243
243
  // we probably can save this backend call by initializing the execution
244
- return testResultService.clearTestResult(this._options.project, this._testResultId, this._testId, {
244
+ return testimServicesApi.clearTestResult(this._options.project, this._testResultId, this._testId, {
245
245
  name: this._testName,
246
246
  resultId: this._testResultId,
247
247
  status: 'pending',
package/testRunStatus.js CHANGED
@@ -167,14 +167,14 @@ RunStatus.prototype.updateTestStatusRunning = function (test, executionId, testR
167
167
  delete testConfig.testData;
168
168
  testConfig.testDataUrl = testDataUrl;
169
169
  await this.executionStartedPromise;
170
- return servicesApi.updateTestStatus('RUNNING', executionId, test.testId, test.resultId, test.startTime, null, null, null, testConfig, projectId, remoteRunId, testRetryKey);
170
+ return servicesApi.updateTestStatus(projectId, executionId, test.testId, test.resultId, 'RUNNING', { startTime: test.startTime, config: testConfig, remoteRunId, testRetryKey });
171
171
  });
172
172
  if (this.asyncReporting) {
173
173
  // silence is golden
174
174
  } else {
175
175
  return res;
176
176
  }
177
- return this.executionStartedPromise.then(() => servicesApi.updateTestStatus('RUNNING', executionId, test.testId, test.resultId, test.startTime, null, null, null, test.config, projectId, remoteRunId, testRetryKey));
177
+ return servicesApi.updateTestStatus(projectId, executionId, test.testId, test.resultId, 'RUNNING', { startTime: test.startTime, config: test.config, remoteRunId, testRetryKey });
178
178
  };
179
179
 
180
180
  RunStatus.prototype.testStartReport = function (test, executionId, testRetryKey) {
@@ -201,9 +201,6 @@ RunStatus.prototype.onGridSlot = function (executionId, resultId, gridInfo) {
201
201
  const test = this.getTestResult(resultId);
202
202
  test.config.gridInfo = Object.assign({}, gridInfo, { key: undefined, user: undefined });
203
203
  logger.info('on get grid info', { gridInfo: test.config.gridInfo });
204
- // we don't wait for this call since it's only for analytics/debugging sake.
205
- const p = this.updateTestStatusRunning(test, executionId);
206
- p.catch(() => {}); // suppress unhandled rejection
207
204
  };
208
205
 
209
206
  RunStatus.prototype.reportTestStatus = function (workerId, result, test, isRerun) {
@@ -277,7 +274,7 @@ RunStatus.prototype.testEnd = function (wid, result, executionId, sessionId, isR
277
274
  return test;
278
275
  };
279
276
 
280
- RunStatus.prototype.testEndReport = async function (test, executionId, result) {
277
+ RunStatus.prototype.testEndReport = async function (test, executionId, result, testResultUpdates) {
281
278
  const globalParameters = result.exportsGlobal;
282
279
  try {
283
280
  try {
@@ -291,16 +288,23 @@ RunStatus.prototype.testEndReport = async function (test, executionId, result) {
291
288
  return undefined;
292
289
  }
293
290
 
294
- return await servicesApi.updateTestStatus('FINISHED', executionId, test.testId, test.resultId, test.startTime, result.endTime, test.success, test.failureReason, null, this.options.project, this.options.remoteRunId, undefined, 5);
291
+ return await servicesApi.updateTestStatus(this.options.project, executionId, test.testId, test.resultId, 'FINISHED', {
292
+ startTime: test.startTime,
293
+ endTime: result.endTime,
294
+ success: test.success,
295
+ failureReason: test.failureReason,
296
+ remoteRunId: this.options.remoteRunId,
297
+ ...testResultUpdates,
298
+ }, 5);
295
299
  } catch (err) {
296
300
  logger.error('Failed to update test finished', { err });
297
301
  throw err;
298
302
  }
299
303
  };
300
304
 
301
- RunStatus.prototype.testEndAndReport = function (wid, result, executionId, sessionId, isRerun) {
305
+ RunStatus.prototype.testEndAndReport = function (wid, result, executionId, sessionId, isRerun, testResultUpdates) {
302
306
  const test = this.testEnd(wid, result, executionId, sessionId, isRerun);
303
- return this.testEndReport(test, executionId, result);
307
+ return this.testEndReport(test, executionId, result, testResultUpdates);
304
308
  };
305
309
 
306
310
  RunStatus.prototype.calcTestRunStatus = function () {
package/utils.js CHANGED
@@ -19,7 +19,7 @@ const OSS = [
19
19
  { osName: 'Windows 7', bs: { os: 'WINDOWS', os_version: '7' }, sl: { platform: 'Windows 7' } },
20
20
  { osName: 'Windows XP', bs: { os: 'WINDOWS', os_version: 'XP' }, sl: { platform: 'Windows XP' } },
21
21
  { osName: 'macOS Big Sur', bs: { os: 'OS X', os_version: 'Big Sur', safari_version: '14' }, sl: { platform: 'macOS 11', safari_version: '14' } },
22
- { osName: 'macOS Catalina',bs: { os: 'OS X', os_version: 'Catalina', safari_version: '13' }, sl: { platform: 'macOS 10.15', safari_version: '13' } },
22
+ { osName: 'macOS Catalina', bs: { os: 'OS X', os_version: 'Catalina', safari_version: '13' }, sl: { platform: 'macOS 10.15', safari_version: '13' } },
23
23
  { osName: 'macOS Mojave', bs: { os: 'OS X', os_version: 'Mojave', safari_version: '12' }, sl: { platform: 'macOS 10.14', safari_version: '12' } },
24
24
  { osName: 'macOS High Sierra', bs: { os: 'OS X', os_version: 'High Sierra', safari_version: '11' }, sl: { platform: 'macOS 10.13', safari_version: '11' } },
25
25
  { osName: 'macOS Sierra', bs: { os: 'OS X', os_version: 'Sierra', safari_version: '10' }, sl: { platform: 'macOS 10.12', safari_version: '10.0' } },
@@ -35,11 +35,11 @@ const OSS = [
35
35
  ];
36
36
 
37
37
  const BROWSERS = [
38
- { browserName: 'Chrome', bs: { browser: 'Chrome', browser_version: '92' }, sl: { browserName: 'chrome', version: '92.0' }, browserValue: 'chrome' },
38
+ { browserName: 'Chrome', bs: { browser: 'Chrome', browser_version: '94' }, sl: { browserName: 'chrome', version: '94.0' }, browserValue: 'chrome' },
39
39
  { browserName: 'Firefox', bs: { browser: 'Firefox', browser_version: '89' }, sl: { browserName: 'firefox', version: '89.0' }, browserValue: 'firefox' },
40
40
  { browserName: 'Safari', bs: { browser: 'Safari' }, sl: { browserName: 'safari' }, browserValue: 'safari' },
41
- { browserName: 'Edge', bs: { browser: 'Edge', browser_version: '17' }, sl: { browserName: 'MicrosoftEdge', version: '17.17134' }, browserValue: 'edge' },
42
- { browserName: 'Edge Chromium', bs: { browser: 'Edge', browser_version: '92' }, sl: { browserName: 'MicrosoftEdge', version: '92' }, synonyms: ['edge-chromium'], browserValue: 'edge-chromium', seleniumName: 'MicrosoftEdge' },
41
+ { browserName: 'Edge', bs: { browser: 'Edge', browser_version: '18' }, sl: { browserName: 'MicrosoftEdge', version: '18.17763' }, browserValue: 'edge' },
42
+ { browserName: 'Edge Chromium', bs: { browser: 'Edge', browser_version: '94' }, sl: { browserName: 'MicrosoftEdge', version: '94' }, synonyms: ['edge-chromium'], browserValue: 'edge-chromium', seleniumName: 'MicrosoftEdge' },
43
43
  { browserName: 'Internet Explorer 11', bs: { browser: 'IE', browser_version: '11' }, sl: { browserName: 'internet explorer', version: '11.0' }, synonyms: ['ie11'], browserValue: 'ie11' },
44
44
  { browserName: 'Browser', bs: {}, sl: { browserName: 'Browser' }, browserValue: 'browser' },
45
45
  { browserName: 'Android', bs: { browserName: 'android' }, sl: {}, browserValue: 'android' },
@@ -143,7 +143,7 @@ function getEnvironmentGitBranch() {
143
143
  }
144
144
 
145
145
  function getUniqBrowsers(options, testList) {
146
- if ((options.testConfigNames.length || options.testConfigIds.length || options.testPlan.length || options.testPlanIds.length || options.testObject) && !options.browser) {
146
+ if ((options.testConfigNames.length || options.testConfigIds.length || options.testPlan.length || options.testPlanIds.length) && !options.browser) {
147
147
  return _.uniq(testList.map(t => t.runConfig.browserValue));
148
148
  }
149
149
  return [options.browser.toLowerCase()];
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const Promise = require('bluebird');
3
+ const Bluebird = require('bluebird');
4
4
  const moment = require('moment');
5
5
  const ms = require('ms');
6
6
 
@@ -38,12 +38,26 @@ function buildFailureResult(testId, testName, resultId, reason) {
38
38
  }
39
39
 
40
40
  class BaseWorker {
41
- constructor(executionQueue, customExtensionLocalLocation) {
41
+ constructor(executionQueue, options, customExtensionLocalLocation, executionId, onTestStarted, onTestCompleted, onGridSlot, onTestIgnored, releaseSlotOnTestFinished = true) {
42
42
  this.lambdatestService = new LambdatestService();
43
+
43
44
  this.id = BaseWorker.getWorkerId();
44
45
  this.executionQueue = executionQueue;
45
46
  this.customExtensionLocalLocation = customExtensionLocalLocation;
46
- this.runTest = Promise.method(this.runTest);
47
+
48
+ this.isCodeMode = options.files && options.files.length > 0;
49
+ this.baseUrl = options.baseUrl;
50
+ this.isRegressionBaselineRun = options.isRegressionBaselineRun;
51
+ this.testRunTimeout = options.timeout;
52
+ this.onTestStarted = onTestStarted;
53
+ this.onTestCompleted = onTestCompleted;
54
+ this.onGridSlot = onGridSlot;
55
+ this.onTestIgnored = onTestIgnored;
56
+ this.releaseSlotOnTestFinished = releaseSlotOnTestFinished;
57
+
58
+ this.userData = options.userData;
59
+ this.executionId = executionId;
60
+ this.options = options;
47
61
  }
48
62
 
49
63
  static getWorkerId() {
@@ -69,7 +83,7 @@ class BaseWorker {
69
83
  throw new NotImplementedError(true);
70
84
  }
71
85
 
72
- runTestOnce(testRunHandler, player) {
86
+ async runTestOnce(testRunHandler, player) {
73
87
  testRunHandler.setSessionId(player.getSessionId());
74
88
  logger.info('Test run started', {
75
89
  testId: testRunHandler.getTestId(),
@@ -77,26 +91,9 @@ class BaseWorker {
77
91
  seleniumSession: player.getSessionId(),
78
92
  });
79
93
  if (this.options.lightweightMode && this.options.lightweightMode.disableResults) {
80
- return Promise.resolve();
94
+ return undefined;
81
95
  }
82
- return testRunHandler.clearTestResult();
83
- }
84
-
85
- start(options, onStart, onResult, executionId, onGridSlot, onTestIgnored, releaseSlotOnTestFinished) {
86
- this.isCodeMode = options.files.length > 0;
87
- this.baseUrl = options.baseUrl;
88
- this.isRegressionBaselineRun = options.isRegressionBaselineRun;
89
- this.testRunTimeout = options.timeout;
90
- this.onStart = onStart;
91
- this.onResult = onResult;
92
- this.onGridSlot = onGridSlot;
93
- this.onTestIgnored = onTestIgnored;
94
- this.releaseSlotOnTestFinished = releaseSlotOnTestFinished;
95
-
96
- this.userData = options.userData;
97
- this.executionId = executionId;
98
- this.options = options;
99
- return this.run();
96
+ return await testRunHandler.clearTestResult();
100
97
  }
101
98
 
102
99
  handleQuarantine(testRunHandler) {
@@ -124,13 +121,12 @@ class BaseWorker {
124
121
  let gridInfo = await utils.runWithRetries(async () => {
125
122
  const startTime = Date.now();
126
123
  try {
127
- return await Promise.resolve()
128
- .then(() => this.getSlotOnce(testRunHandler))
124
+ return await Bluebird.resolve(this.getSlotOnce(testRunHandler))
129
125
  .timeout(this.options.getBrowserTimeout, timeoutMessages.GET_BROWSER_TIMEOUT_MSG);
130
126
  } catch (error) {
131
127
  logger.error('error getting grid slot', { error, testId: this.testId, testResultId: this.testResultId, executionId: this.executionId });
132
128
  failedGetSlotAttempts++;
133
- await Promise.delay(this.options.getBrowserTimeout - (Date.now() - startTime));
129
+ await Bluebird.delay(this.options.getBrowserTimeout - (Date.now() - startTime));
134
130
  throw error;
135
131
  }
136
132
  }, this.options.getBrowserRetries);
@@ -154,7 +150,7 @@ class BaseWorker {
154
150
  this.options.gridData.host = gridInfo.host;
155
151
  this.options.gridData.failedGetBrowserAttempts = failedGetBrowserAttempts;
156
152
  const getSessionTimeout = Math.max(this.lambdatestService.getSessionTimeout, this.options.getSessionTimeout);
157
- const getBrowserRes = await Promise.resolve()
153
+ const getBrowserRes = await Bluebird.resolve()
158
154
  .log('before getBrowserOnce')
159
155
  .then(() => this.getBrowserOnce(testRunHandler, customExtensionLocalLocation, player, gridInfo))
160
156
  .log('after getBrowserOnce')
@@ -169,7 +165,7 @@ class BaseWorker {
169
165
  player.onDone();
170
166
 
171
167
  if (!(error instanceof PageNotAvailableError)) {
172
- await Promise.delay(this.options.getBrowserTimeout - (Date.now() - startTime));
168
+ await Bluebird.delay(this.options.getBrowserTimeout - (Date.now() - startTime));
173
169
  }
174
170
  throw error;
175
171
  }
@@ -190,14 +186,15 @@ class BaseWorker {
190
186
  }
191
187
 
192
188
  async runTest(testRunHandler, customExtensionLocalLocation, shouldRerun) {
189
+ perf.log('inside runTest');
193
190
  const projectId = this.userData && this.userData.projectId;
194
191
  const quarantineResult = this.handleQuarantine(testRunHandler);
195
192
  if (quarantineResult) {
196
193
  return quarantineResult;
197
194
  }
198
195
 
199
- perf.log('before runTest onStart');
200
- await this.onStart(this.id, testRunHandler.getTestId(), testRunHandler.getTestResultId(), shouldRerun, testRunHandler.getRetryKey());
196
+ perf.log('before runTest onTestStarted');
197
+ await this.onTestStarted(this.id, testRunHandler.getTestId(), testRunHandler.getTestResultId(), shouldRerun, testRunHandler.getRetryKey());
201
198
  const testPlayer = await this.getTestPlayer(testRunHandler, customExtensionLocalLocation);
202
199
 
203
200
  try {
@@ -207,12 +204,12 @@ class BaseWorker {
207
204
  }
208
205
  }
209
206
 
210
- runTestCleanup() {
211
- return Promise.resolve();
207
+ async runTestCleanup() {
208
+ return undefined;
212
209
  }
213
210
 
214
211
  onQueueCompleted() {
215
- return Promise.resolve();
212
+ return undefined;
216
213
  }
217
214
 
218
215
  run() {
@@ -238,9 +235,9 @@ class BaseWorker {
238
235
  try {
239
236
  const testRetryKey = testRunHandler.getRetryKey();
240
237
  testResult.testRetryKey = testRetryKey;
241
- await this.onResult(this.id, this.testId, testResult, sessionId, shouldRerun);
242
- if (!(this.options.lightweightMode && this.options.lightweightMode.general)) {
243
- await Promise.delay(DELAY_BETWEEN_TESTS);
238
+ await this.onTestCompleted(this.id, this.testId, testResult, sessionId, shouldRerun);
239
+ if (this.executionQueue.hasMoreTests() && !(this.options.lightweightMode && this.options.lightweightMode.general)) {
240
+ await Bluebird.delay(DELAY_BETWEEN_TESTS);
244
241
  }
245
242
  await this.runTestCleanup();
246
243
  if (shouldRerun) {
@@ -340,7 +337,7 @@ class BaseWorker {
340
337
 
341
338
  const projectId = this.userData && this.userData.projectId;
342
339
  const { errorType, reason } = buildError(err);
343
- testResultService.updateTestResult(projectId, this.testResultId, this.testId, {
340
+ testimServicesApi.updateTestResult(projectId, this.testResultId, this.testId, {
344
341
  status: testRunStatus.COMPLETED,
345
342
  success: false,
346
343
  reason,
@@ -390,10 +387,12 @@ class BaseWorker {
390
387
  !disableRemoteStep && remoteStepService.joinToRemoteStep(this.testResultId),
391
388
  !disableResults && testResultService.joinToTestResult(this.testResultId, this.testId),
392
389
  ])
393
- .log('after join room, before runTest')
394
390
  .then(() => this.runTest(testRunHandler, this.customExtensionLocalLocation, shouldRerun))
395
391
  .then(testResult => onRunComplete(testResult, testRunHandler))
396
- .log('After onRunComplete')
392
+ .then(result => {
393
+ perf.log('After onRunComplete');
394
+ return result;
395
+ })
397
396
  .catch(runError => recoverTestResults(runError, testRunHandler))
398
397
  .finally(() => {
399
398
  if (!disableRemoteStep) {
@@ -15,7 +15,7 @@ describe('BaseWorker', () => {
15
15
  let testPlayerMock;
16
16
 
17
17
  beforeEach(() => {
18
- worker = new BaseWorker();
18
+ worker = new BaseWorker(null, {});
19
19
  worker.userData = {};
20
20
  worker.options = { gridData: {}, browser: 'chrome', company: { companyId: 'companyId' }, getBrowserTimeout: 1000, getSessionTimeout: 100, getBrowserRetries: 10 };
21
21
  worker.testRunConfig = {};
@@ -44,9 +44,8 @@ class WorkerExtensionSingleBrowser extends WorkerExtension {
44
44
  return quarantineResult;
45
45
  }
46
46
 
47
- perf.log('before runTest onStart single browser');
48
-
49
- await this.onStart(this.id, testRunHandler.getTestId(), testRunHandler.getTestResultId(), shouldRerun, testRunHandler.getRetryKey());
47
+ perf.log('before runTest onTestStarted single browser');
48
+ await this.onTestStarted(this.id, testRunHandler.getTestId(), testRunHandler.getTestResultId(), shouldRerun, testRunHandler.getRetryKey());
50
49
  const testPlayer = await this.getTestPlayer(testRunHandler, customExtensionLocalLocation);
51
50
 
52
51
  testRunHandler.markClearBrowser();
@@ -54,6 +53,10 @@ class WorkerExtensionSingleBrowser extends WorkerExtension {
54
53
  }
55
54
 
56
55
  async runTestCleanup() {
56
+ if (!this.executionQueue.hasMoreTests()) {
57
+ await this.onQueueCompleted();
58
+ return;
59
+ }
57
60
  if (this.options.lightweightMode && this.options.lightweightMode.general) {
58
61
  await Bluebird.delay(DELAY_BETWEEN_TESTS);
59
62
  }
@@ -1,46 +0,0 @@
1
- const Promise = require('bluebird');
2
- const utils = require('../../utils');
3
- const logger = require('../logger').getLogger("apk-uploader");
4
- const {ArgError} = require('../../errors');
5
- const reporter = require("../../reports/reporter");
6
-
7
- class ApkUploader {
8
- constructor() {
9
- this._appId = null;
10
- this._isInFlightRequest = null;
11
- }
12
-
13
- uploadApk(vendorAppId, apkFullPath, apkTimeout, projectId, gridData) {
14
- if (this._appId) {
15
- return Promise.resolve(this.getAppCaps());
16
- }
17
- if (this._isInFlightRequest) {
18
- return this._isInFlightRequest;
19
- }
20
-
21
- this._isInFlightRequest = (vendorAppId
22
- ? Promise.resolve(vendorAppId)
23
- : utils.runWithRetries(() => this.uploadFile(gridData, apkFullPath, apkTimeout, projectId)))
24
- .then(res => {
25
- reporter.onUploadApkFinished(res);
26
- this._appId = res;
27
- return this.getAppCaps();
28
- })
29
- .catch(err => {
30
- logger.error("failed to upload apk", {errStack: err.stack});
31
- throw new ArgError(`Failed to upload apk: ${apkFullPath}`);
32
- })
33
- .finally(() => this._isInFlightRequest = null);
34
- return this._isInFlightRequest;
35
- }
36
-
37
- uploadFile(key, apkFullPath, apkTimeout) {
38
- throw new Error("not implemented");
39
- }
40
-
41
- getAppCaps() {
42
- throw new Error("not implemented");
43
- }
44
- }
45
-
46
- module.exports = ApkUploader;
@@ -1,68 +0,0 @@
1
- const testObjectApkUploader = require('./testObjectApkUploader');
2
- const saucelabsApkUploader = require('./saucelabsApkUploader');
3
- const deviceFarmApkUploader = require('./deviceFarmApkUploader');
4
-
5
- const reporter = require("../../reports/reporter");
6
- const path = require('path');
7
- const Promise = require('bluebird');
8
- const logger = require('../../commons/logger').getLogger("apk-uploader-factory");
9
- const { ArgError } = require('../../errors');
10
-
11
- function uploadApk(browserOptions) {
12
- const { apkLocation, apkTimeout, ipaLocation, ipaTimeout, project, vendorAppId, gridData } = browserOptions;
13
-
14
- if(gridData.type === "hostAndPort") {
15
- return Promise.resolve();
16
- }
17
-
18
- if(!apkLocation && !ipaLocation && !vendorAppId) {
19
- logger.info("App location is not defined");
20
- throw new ArgError("No App provided. Please specify the location of the APK/IPA to use in this run using the --apk-location flag or the --ipa-location flag");
21
- }
22
-
23
- let appFullPath, appTimeout;
24
-
25
- if(apkLocation) {
26
- appFullPath = path.resolve(apkLocation);
27
- appTimeout = apkTimeout;
28
- } else if(ipaLocation) {
29
- appFullPath = path.resolve(ipaLocation);
30
- appTimeout = ipaTimeout;
31
- }
32
- const projectId = project;
33
-
34
- reporter.onUploadApk(appFullPath, vendorAppId);
35
-
36
- if (gridData.type === "testimMobile") {
37
- return deviceFarmApkUploader.uploadApk(vendorAppId, appFullPath, appTimeout, projectId, gridData);
38
- }
39
-
40
- if (gridData.type === "testobject") {
41
- return testObjectApkUploader.uploadApk(vendorAppId, appFullPath, appTimeout, projectId, gridData);
42
- }
43
-
44
- if (gridData.type === "saucelabs") {
45
- return saucelabsApkUploader.uploadApk(vendorAppId, appFullPath, appTimeout, projectId, gridData);
46
- }
47
-
48
- throw new ArgError(`Failed to find matched provider: ${gridData.type}`);
49
- }
50
-
51
- function getAppCaps(gridInfo) {
52
- if (gridInfo.type === "testobject") {
53
- return testObjectApkUploader.getAppCaps();
54
- }
55
-
56
- if (gridInfo.type === "saucelabs") {
57
- return saucelabsApkUploader.getAppCaps();
58
- }
59
-
60
- if (gridInfo.type === "testimMobile") {
61
- return deviceFarmApkUploader.getAppCaps();
62
- }
63
- }
64
-
65
- module.exports = {
66
- uploadApk,
67
- getAppCaps
68
- };
@@ -1,41 +0,0 @@
1
- const path = require('path');
2
- const crypto = require('crypto');
3
- const ApkUploader = require('./apkUploader');
4
- const httpRequest = require('../httpRequest');
5
- const Promise = require('bluebird');
6
- const fs = Promise.promisifyAll(require('fs'));
7
- const testimServicesApi = require('../testimServicesApi');
8
-
9
- class DeviceFarmApkUploader extends ApkUploader {
10
- constructor() {
11
- super();
12
- }
13
-
14
- uploadFile({arn}, apkFullPath, apkTimeout, projectId) {
15
- let apkPathObj = {};
16
- try {
17
- apkPathObj = path.parse(apkFullPath);
18
- } catch (error) {
19
- return Promise.reject(new TypeError('Apk path is not valid.'));
20
- }
21
- return fs.readFileAsync(apkFullPath)
22
- .then(apkBuffer => {
23
- const apkHash = crypto.createHash('md5').update(apkBuffer).digest("hex");
24
- const apkName = `${apkHash}${apkPathObj.ext}`;
25
- return Promise.all([testimServicesApi.getAppUploadUrl(projectId, arn, apkName), apkBuffer]);
26
- })
27
- .spread((app, apkBuffer) => {
28
- if(app.status === 'SUCCEEDED') {
29
- return app.arn;
30
- }
31
- return httpRequest.put(app.url, apkBuffer, {"Content-Type": "application/octet-stream"}, apkTimeout)
32
- .then(() => app.arn);
33
- });
34
- }
35
-
36
- getAppCaps() {
37
- return {appArn: this._appId};
38
- }
39
- }
40
-
41
- module.exports = new DeviceFarmApkUploader();