@testim/testim-cli 3.193.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.
- package/README.md +1 -1
- package/cli/onExit.js +12 -1
- package/cli.js +5 -1
- package/codim/codim-npm-package/index.ts +1 -0
- package/commons/constants.js +0 -25
- package/commons/featureFlags.js +2 -0
- package/commons/socket/testResultService.js +4 -14
- package/commons/testimAnalytics.js +0 -1
- package/commons/testimDesiredCapabilitiesBuilder.js +0 -95
- package/commons/testimServicesApi.js +10 -80
- package/executionQueue.js +7 -4
- package/npm-shrinkwrap.json +956 -520
- package/package.json +3 -1
- package/player/chromeLauncherTestPlayer.js +5 -2
- package/player/stepActions/apiStepAction.js +1 -0
- package/player/stepActions/baseJsStepAction.js +5 -1
- package/player/stepActions/pixelValidationStepAction.js +28 -0
- package/player/stepActions/salesforceAutoLoginStepAction.js +39 -0
- package/player/stepActions/scripts/focusElement.js +5 -3
- package/player/stepActions/stepActionRegistrar.js +5 -47
- package/player/utils/eyeSdkService.js +230 -0
- package/reports/consoleReporter.js +0 -20
- package/reports/reporter.js +0 -21
- package/runOptions.js +13 -89
- package/runner.js +9 -46
- package/runners/{strategies/LocalStrategy.js → ParallelWorkerManager.js} +60 -66
- package/runners/TestPlanRunner.js +286 -67
- package/runners/runnerUtils.js +73 -0
- package/{runners/strategies/BaseStrategy.js → services/analyticsService.js} +41 -28
- package/services/gridService.js +24 -16
- package/services/gridService.test.js +21 -21
- package/services/lambdatestService.js +1 -1
- package/testRunHandler.js +1 -1
- package/testRunStatus.js +30 -20
- package/utils.js +5 -5
- package/workers/BaseWorker.js +38 -39
- package/workers/BaseWorker.test.js +1 -1
- package/workers/WorkerExtensionSingleBrowser.js +6 -3
- package/commons/apkUploader/apkUploader.js +0 -46
- package/commons/apkUploader/apkUploaderFactory.js +0 -68
- package/commons/apkUploader/deviceFarmApkUploader.js +0 -41
- package/commons/apkUploader/saucelabsApkUploader.js +0 -36
- package/commons/apkUploader/testObjectApkUploader.js +0 -34
- package/player/mobile/mobileTestPlayer.js +0 -80
- package/player/mobile/mobileWebDriver.js +0 -155
- package/player/mobile/services/frameLocatorMock.js +0 -18
- package/player/mobile/services/mobilePortSelector.js +0 -22
- package/player/mobile/services/mobileTabService.js +0 -241
- package/player/mobile/utils/mobileScreenshotUtils.js +0 -46
- package/player/mobile/utils/mobileWindowUtils.js +0 -84
- package/player/stepActions/mobile/android/androidLocateStepAction.js +0 -122
- package/player/stepActions/mobile/android/androidLongClickStepAction.js +0 -12
- package/player/stepActions/mobile/android/androidScrollStepAction.js +0 -134
- package/player/stepActions/mobile/android/androidSpecialKeyStepAction.js +0 -22
- package/player/stepActions/mobile/android/androidSwipeStepAction.js +0 -32
- package/player/stepActions/mobile/androidGlobalActionStepAction.js +0 -12
- package/player/stepActions/mobile/androidTapStepAction.js +0 -19
- package/player/stepActions/mobile/androidTextChangeStepAction.js +0 -23
- package/player/stepActions/mobile/ios/iosLocateStepAction.js +0 -124
- package/player/stepActions/mobile/ios/iosScrollStepAction.js +0 -76
- package/runners/AnonymousTestPlanRunner.js +0 -106
- package/runners/BaseRunner.js +0 -42
- package/runners/BaseTestPlanRunner.js +0 -194
- package/runners/DeviceFarmRemoteRunner.js +0 -50
- package/runners/SchedulerRemoteRunner.js +0 -47
- package/runners/strategies/DeviceFarmStrategy.js +0 -195
- package/runners/strategies/LocalDeviceFarmStrategy.js +0 -12
- package/runners/strategies/LocalTestStrategy.js +0 -14
- package/runners/strategies/Strategy.js +0 -17
- package/workers/WorkerAppium.js +0 -70
|
@@ -175,19 +175,19 @@ describe('gridService', () => {
|
|
|
175
175
|
|
|
176
176
|
describe('getGridSlot', () => {
|
|
177
177
|
let getGridByIdStub;
|
|
178
|
-
let
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 () => {
|
|
@@ -16,7 +16,7 @@ const { getExtensionsUrl } = require('../runOptionsUtils');
|
|
|
16
16
|
|
|
17
17
|
const logger = require('../commons/logger').getLogger('lambdatestService');
|
|
18
18
|
|
|
19
|
-
const LT_TUNNEL_BINARY_ORIGIN = 'https://downloads.lambdatest.com/tunnel/
|
|
19
|
+
const LT_TUNNEL_BINARY_ORIGIN = 'https://downloads.lambdatest.com/tunnel/v3';
|
|
20
20
|
const LT_TUNNEL_BINARY_PATHNAME = {
|
|
21
21
|
win32ia32: 'windows/32bit/LT_Windows.zip',
|
|
22
22
|
win32x64: 'windows/64bit/LT_Windows.zip',
|
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
|
|
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
|
@@ -47,7 +47,7 @@ const RunStatus = function (testInfoList, options, testPlanId, branchToUse) {
|
|
|
47
47
|
this.executionStartedPromise = Promise.resolve();
|
|
48
48
|
|
|
49
49
|
const browserNames = utils.getUniqBrowsers(options, testInfoList);
|
|
50
|
-
const runnerMode = options.lightweightMode ? options.lightweightMode.type : options.mode;
|
|
50
|
+
const runnerMode = options.lightweightMode ? options.lightweightMode.type : options.mode;
|
|
51
51
|
this.execConfig = {
|
|
52
52
|
parallel: TESTIM_CONCURRENT_WORKER_COUNT || options.parallel || 1,
|
|
53
53
|
browser: browserNames,
|
|
@@ -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(
|
|
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
|
|
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,24 +274,37 @@ RunStatus.prototype.testEnd = function (wid, result, executionId, sessionId, isR
|
|
|
277
274
|
return test;
|
|
278
275
|
};
|
|
279
276
|
|
|
280
|
-
RunStatus.prototype.testEndReport = function (test, executionId, result) {
|
|
277
|
+
RunStatus.prototype.testEndReport = async function (test, executionId, result, testResultUpdates) {
|
|
281
278
|
const globalParameters = result.exportsGlobal;
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
279
|
+
try {
|
|
280
|
+
try {
|
|
281
|
+
await runHook(this.options.afterTest, Object.assign({}, test, { globalParameters }));
|
|
282
|
+
} catch (err) {
|
|
283
|
+
logger.error('HOOK threw an error', { test: test.testId, err });
|
|
284
|
+
// eslint-disable-next-line no-console
|
|
285
|
+
console.error('HOOK threw an error', err); // show the customer that his hook failed.
|
|
286
|
+
}
|
|
287
|
+
if (this.options.lightweightMode && this.options.lightweightMode.onlyTestIdsNoSuite) {
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
|
|
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);
|
|
299
|
+
} catch (err) {
|
|
300
|
+
logger.error('Failed to update test finished', { err });
|
|
301
|
+
throw err;
|
|
302
|
+
}
|
|
293
303
|
};
|
|
294
304
|
|
|
295
|
-
RunStatus.prototype.testEndAndReport = function (wid, result, executionId, sessionId, isRerun) {
|
|
305
|
+
RunStatus.prototype.testEndAndReport = function (wid, result, executionId, sessionId, isRerun, testResultUpdates) {
|
|
296
306
|
const test = this.testEnd(wid, result, executionId, sessionId, isRerun);
|
|
297
|
-
return this.testEndReport(test, executionId, result);
|
|
307
|
+
return this.testEndReport(test, executionId, result, testResultUpdates);
|
|
298
308
|
};
|
|
299
309
|
|
|
300
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: '
|
|
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: '
|
|
42
|
-
{ browserName: 'Edge Chromium', bs: { browser: 'Edge', browser_version: '
|
|
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
|
|
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()];
|
package/workers/BaseWorker.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
200
|
-
await this.
|
|
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
|
|
207
|
+
async runTestCleanup() {
|
|
208
|
+
return undefined;
|
|
212
209
|
}
|
|
213
210
|
|
|
214
211
|
onQueueCompleted() {
|
|
215
|
-
return
|
|
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.
|
|
242
|
-
if (!(this.options.lightweightMode && this.options.lightweightMode.general)) {
|
|
243
|
-
await
|
|
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
|
-
|
|
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
|
-
.
|
|
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
|
|
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
|
-
};
|