@testim/testim-cli 3.289.0 → 3.290.0-beta
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/cli.js +22390 -122
- package/cli.js.map +1 -0
- package/npm-shrinkwrap.json +8 -29129
- package/package.json +16 -10
- package/OverrideTestDataBuilder.js +0 -117
- package/agent/routers/cliJsCode/index.js +0 -13
- package/agent/routers/cliJsCode/router.js +0 -63
- package/agent/routers/cliJsCode/service.js +0 -705
- package/agent/routers/codim/router.js +0 -69
- package/agent/routers/codim/router.test.js +0 -60
- package/agent/routers/codim/service.js +0 -193
- package/agent/routers/general/index.js +0 -36
- package/agent/routers/hybrid/registerRoutes.js +0 -81
- package/agent/routers/index.js +0 -56
- package/agent/routers/playground/router.js +0 -77
- package/agent/routers/playground/service.js +0 -96
- package/agent/routers/standalone-browser/registerRoutes.js +0 -47
- package/agent/server.js +0 -150
- package/cdpTestRunner.js +0 -86
- package/chromiumInstaller.js +0 -91
- package/cli/isCiRun.js +0 -10
- package/cli/onExit.js +0 -65
- package/cli/writeStackTrace.js +0 -27
- package/cliAgentMode.js +0 -384
- package/codim/codim-cli.js +0 -91
- package/codim/codim-npm-package/index.ts +0 -427
- package/codim/codim-npm-package/package-lock.json +0 -14
- package/codim/codim-npm-package/package.json +0 -14
- package/codim/hybrid-utils.js +0 -28
- package/codim/measure-perf.js +0 -41
- package/codim/template.js/.idea/workspace.xml +0 -57
- package/codim/template.js/.vscode/launch.json +0 -53
- package/codim/template.ts/.idea/workspace.xml +0 -57
- package/codim/template.ts/.vscode/launch.json +0 -55
- package/commons/AbortError.js +0 -12
- package/commons/SeleniumPerfStats.js +0 -58
- package/commons/chrome-launcher.js +0 -15
- package/commons/chromedriverWrapper.js +0 -70
- package/commons/config.js +0 -39
- package/commons/constants.js +0 -67
- package/commons/detectDebugger.js +0 -19
- package/commons/featureAvailabilityService.js +0 -26
- package/commons/featureFlags.js +0 -132
- package/commons/getSessionPlayerRequire.js +0 -28
- package/commons/httpRequest.js +0 -261
- package/commons/httpRequestCounters.js +0 -98
- package/commons/httpRequestCounters.test.js +0 -38
- package/commons/initializeUserWithAuth.js +0 -55
- package/commons/lazyRequire.js +0 -105
- package/commons/logUtils.js +0 -15
- package/commons/logUtils.test.js +0 -21
- package/commons/logger.js +0 -178
- package/commons/mockNetworkRuleFileSchema.json +0 -140
- package/commons/npmWrapper.js +0 -174
- package/commons/npmWrapper.test.js +0 -374
- package/commons/performance-logger.js +0 -71
- package/commons/preloadTests.js +0 -29
- package/commons/prepareRunner.js +0 -85
- package/commons/prepareRunner.test.js +0 -144
- package/commons/prepareRunnerAndTestimStartUtils.js +0 -198
- package/commons/prepareRunnerAndTestimStartUtils.test.js +0 -73
- package/commons/requireWithFallback.js +0 -25
- package/commons/runnerFileCache.js +0 -204
- package/commons/socket/baseSocketServiceSocketIO.js +0 -197
- package/commons/socket/realDataService.js +0 -59
- package/commons/socket/realDataServiceSocketIO.js +0 -33
- package/commons/socket/remoteStepService.js +0 -55
- package/commons/socket/remoteStepServiceSocketIO.js +0 -61
- package/commons/socket/socketService.js +0 -175
- package/commons/socket/testResultService.js +0 -62
- package/commons/socket/testResultServiceSocketIO.js +0 -64
- package/commons/testimAnalytics.js +0 -40
- package/commons/testimCloudflare.js +0 -83
- package/commons/testimCloudflare.test.js +0 -185
- package/commons/testimCustomToken.js +0 -124
- package/commons/testimDesiredCapabilitiesBuilder.js +0 -647
- package/commons/testimNgrok.js +0 -90
- package/commons/testimNgrok.test.js +0 -140
- package/commons/testimServicesApi.js +0 -631
- package/commons/testimTunnel.js +0 -73
- package/commons/testimTunnel.test.js +0 -172
- package/commons/xhr2.js +0 -897
- package/coverage/SummaryToObjectReport.js +0 -19
- package/coverage/jsCoverage.js +0 -252
- package/credentialsManager.js +0 -142
- package/errors.js +0 -161
- package/executionQueue.js +0 -37
- package/fixLocalBuild.js +0 -24
- package/inputFileUtils.js +0 -103
- package/lib/coralogix-winston.transport.js +0 -99
- package/player/SeleniumProtocolError.js +0 -100
- package/player/WebDriverHttpRequest.js +0 -177
- package/player/WebdriverioWebDriverApi.js +0 -671
- package/player/appiumTestPlayer.js +0 -90
- package/player/chromeLauncherTestPlayer.js +0 -67
- package/player/constants.js +0 -332
- package/player/extensionTestPlayer.js +0 -32
- package/player/findElementStrategy.js +0 -154
- package/player/scripts/isElementDisplayed.js +0 -252
- package/player/seleniumTestPlayer.js +0 -140
- package/player/services/frameLocator.js +0 -170
- package/player/services/mobileFrameLocatorMock.js +0 -32
- package/player/services/playbackTimeoutCalculator.js +0 -175
- package/player/services/portSelector.js +0 -19
- package/player/services/tabService.js +0 -551
- package/player/services/tabServiceMock.js +0 -167
- package/player/services/windowCreationListener.js +0 -8
- package/player/stepActions/RefreshStepAction.js +0 -16
- package/player/stepActions/apiStepAction.js +0 -89
- package/player/stepActions/baseCliJsStepAction.js +0 -51
- package/player/stepActions/baseJsStepAction.js +0 -277
- package/player/stepActions/cliConditionStepAction.js +0 -11
- package/player/stepActions/cliJsStepAction.js +0 -11
- package/player/stepActions/dropFileStepAction.js +0 -34
- package/player/stepActions/evaluateExpressionStepAction.js +0 -52
- package/player/stepActions/extensionOnlyStepAction.js +0 -12
- package/player/stepActions/extractTextStepAction.js +0 -19
- package/player/stepActions/hoverStepAction.js +0 -55
- package/player/stepActions/inputFileStepAction.js +0 -199
- package/player/stepActions/jsCodeStepAction.js +0 -11
- package/player/stepActions/jsConditionStepAction.js +0 -11
- package/player/stepActions/locateStepAction.js +0 -159
- package/player/stepActions/mouseStepAction.js +0 -370
- package/player/stepActions/navigationStepAction.js +0 -29
- package/player/stepActions/nodePackageStepAction.js +0 -47
- package/player/stepActions/pixelValidationStepAction.js +0 -39
- package/player/stepActions/scripts/dispatchEvents.js +0 -282
- package/player/stepActions/scripts/doClick.js +0 -221
- package/player/stepActions/scripts/doDragPath.js +0 -225
- package/player/stepActions/scripts/doubleClick.js +0 -119
- package/player/stepActions/scripts/dropEvent.js +0 -63
- package/player/stepActions/scripts/focusElement.js +0 -46
- package/player/stepActions/scripts/html5dragAction.js +0 -56
- package/player/stepActions/scripts/html5dragActionV2.js +0 -312
- package/player/stepActions/scripts/runCode.js +0 -147
- package/player/stepActions/scripts/scroll.js +0 -90
- package/player/stepActions/scripts/selectOption.js +0 -51
- package/player/stepActions/scripts/setText.js +0 -415
- package/player/stepActions/scripts/wheel.js +0 -61
- package/player/stepActions/scrollStepAction.js +0 -96
- package/player/stepActions/selectOptionStepAction.js +0 -49
- package/player/stepActions/sfdcRecordedStepAction.js +0 -24
- package/player/stepActions/sfdcStepAction.js +0 -28
- package/player/stepActions/sleepStepAction.js +0 -12
- package/player/stepActions/specialKeyStepAction.js +0 -52
- package/player/stepActions/stepAction.js +0 -73
- package/player/stepActions/stepActionRegistrar.js +0 -111
- package/player/stepActions/submitStepAction.js +0 -12
- package/player/stepActions/tdkHybridStepAction.js +0 -18
- package/player/stepActions/textStepAction.js +0 -110
- package/player/stepActions/textValidationStepAction.js +0 -64
- package/player/stepActions/wheelStepAction.js +0 -41
- package/player/utils/cookieUtils.js +0 -39
- package/player/utils/eyeSdkService.js +0 -250
- package/player/utils/imageCaptureUtils.js +0 -267
- package/player/utils/screenshotUtils.js +0 -68
- package/player/utils/stepActionUtils.js +0 -90
- package/player/utils/windowUtils.js +0 -195
- package/player/webDriverUtils.js +0 -40
- package/player/webDriverUtils.test.js +0 -116
- package/player/webdriver.js +0 -976
- package/polyfills/Array.prototype.at.js +0 -13
- package/polyfills/index.js +0 -13
- package/processHandler.js +0 -79
- package/processHandler.test.js +0 -55
- package/reports/chromeReporter.js +0 -17
- package/reports/consoleReporter.js +0 -190
- package/reports/debugReporter.js +0 -82
- package/reports/jsonReporter.js +0 -55
- package/reports/junitReporter.js +0 -183
- package/reports/reporter.js +0 -166
- package/reports/reporterUtils.js +0 -54
- package/reports/teamCityReporter.js +0 -73
- package/runOptions.d.ts +0 -305
- package/runOptions.js +0 -1288
- package/runOptionsAgentFlow.js +0 -87
- package/runOptionsUtils.js +0 -60
- package/runner.js +0 -355
- package/runners/ParallelWorkerManager.js +0 -284
- package/runners/TestPlanRunner.js +0 -419
- package/runners/buildCodeTests.js +0 -159
- package/runners/runnerUtils.js +0 -81
- package/services/analyticsService.js +0 -96
- package/services/branchService.js +0 -29
- package/services/gridService.js +0 -357
- package/services/gridService.test.js +0 -357
- package/services/labFeaturesService.js +0 -64
- package/services/lambdatestService.js +0 -227
- package/services/lambdatestService.test.js +0 -353
- package/services/localRCASaver.js +0 -124
- package/stepPlayers/cliJsStepPlayback.js +0 -40
- package/stepPlayers/hybridStepPlayback.js +0 -140
- package/stepPlayers/nodePackageStepPlayback.js +0 -28
- package/stepPlayers/playwrightHybridStepPlayback.js +0 -61
- package/stepPlayers/puppeteerHybridStepPlayback.js +0 -76
- package/stepPlayers/remoteStepPlayback.js +0 -80
- package/stepPlayers/seleniumHybridStepPlayback.js +0 -84
- package/stepPlayers/tdkHybridStepPlayback.js +0 -112
- package/testRunHandler.js +0 -603
- package/testRunStatus.js +0 -567
- package/testimNpmDriver.js +0 -52
- package/utils/argsUtils.js +0 -91
- package/utils/argsUtils.test.js +0 -32
- package/utils/fsUtils.js +0 -174
- package/utils/index.js +0 -197
- package/utils/promiseUtils.js +0 -85
- package/utils/stringUtils.js +0 -98
- package/utils/stringUtils.test.js +0 -22
- package/utils/timeUtils.js +0 -25
- package/utils/utils.test.js +0 -27
- package/workers/BaseWorker.js +0 -498
- package/workers/BaseWorker.test.js +0 -186
- package/workers/WorkerAppium.js +0 -180
- package/workers/WorkerExtension.js +0 -192
- package/workers/WorkerExtensionSingleBrowser.js +0 -77
- package/workers/WorkerSelenium.js +0 -253
- package/workers/workerUtils.js +0 -20
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const chai = require('chai'); // eslint-disable-line import/no-extraneous-dependencies
|
|
2
|
-
const stringUtils = require('./stringUtils');
|
|
3
|
-
|
|
4
|
-
const expect = chai.expect;
|
|
5
|
-
|
|
6
|
-
describe('stringUtils', () => {
|
|
7
|
-
describe('getTestUrl', () => {
|
|
8
|
-
it('should create properly escaped test URL', () => {
|
|
9
|
-
expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test')).to.equal('http://localhost:8080/#/project/project/branch/master/test/test');
|
|
10
|
-
expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test', 'result')).to.equal('http://localhost:8080/#/project/project/branch/master/test/test?result-id=result');
|
|
11
|
-
expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', null)).to.equal('http://localhost:8080/#/project/project/branch/master/test/test?result-id=result');
|
|
12
|
-
expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', 'normal-branch-name'))
|
|
13
|
-
.to.equal('http://localhost:8080/#/project/project/branch/normal-branch-name/test/test?result-id=result');
|
|
14
|
-
expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', 'branch/with/slashes'))
|
|
15
|
-
.to.equal('http://localhost:8080/#/project/project/branch/branch%2Fwith%2Fslashes/test/test?result-id=result');
|
|
16
|
-
expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', 'branch with spaces'))
|
|
17
|
-
.to.equal('http://localhost:8080/#/project/project/branch/branch%20with%20spaces/test/test?result-id=result');
|
|
18
|
-
expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', 'encoded%20branch'))
|
|
19
|
-
.to.equal('http://localhost:8080/#/project/project/branch/encoded%2520branch/test/test?result-id=result');
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
});
|
package/utils/timeUtils.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
const moment = require('moment');
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @param {moment.DurationInputArg1} ms
|
|
9
|
-
*/
|
|
10
|
-
function getDuration(ms) {
|
|
11
|
-
const duration = moment.duration(ms);
|
|
12
|
-
return `${duration.hours()}:${duration.minutes()}:${duration.seconds()}.${duration.milliseconds()}`;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @param {moment.DurationInputArg1} ms
|
|
17
|
-
*/
|
|
18
|
-
function getDurationSec(ms) {
|
|
19
|
-
return moment.duration(ms).asSeconds();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
module.exports = {
|
|
23
|
-
getDuration,
|
|
24
|
-
getDurationSec,
|
|
25
|
-
};
|
package/utils/utils.test.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
const chai = require('chai'); // eslint-disable-line import/no-extraneous-dependencies
|
|
2
|
-
const utils = require('./index');
|
|
3
|
-
|
|
4
|
-
const expect = chai.expect;
|
|
5
|
-
|
|
6
|
-
describe('utils', () => {
|
|
7
|
-
describe('calcPercentile', () => {
|
|
8
|
-
it('should calc some precentiles', () => {
|
|
9
|
-
// Arrange:
|
|
10
|
-
const arr = [4, 5, 1, 2, 7, 8, 3, 6, 9, 10];
|
|
11
|
-
|
|
12
|
-
// Act:
|
|
13
|
-
const p0 = utils.calcPercentile(arr, 0);
|
|
14
|
-
const p50 = utils.calcPercentile(arr, 50);
|
|
15
|
-
const p90 = utils.calcPercentile(arr, 90);
|
|
16
|
-
const p95 = utils.calcPercentile(arr, 95);
|
|
17
|
-
const p100 = utils.calcPercentile(arr, 100);
|
|
18
|
-
|
|
19
|
-
// Assert:
|
|
20
|
-
expect(p0).to.eql(1);
|
|
21
|
-
expect(p50).to.eql(5);
|
|
22
|
-
expect(p90).to.eql(9);
|
|
23
|
-
expect(p95).to.eql(10);
|
|
24
|
-
expect(p100).to.eql(10);
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
});
|
package/workers/BaseWorker.js
DELETED
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const moment = require('moment');
|
|
4
|
-
const pRetry = require('p-retry');
|
|
5
|
-
const ms = require('ms');
|
|
6
|
-
|
|
7
|
-
const { timeoutMessages, testRunStatus, stepResult, runnerTestStatus, CLI_MODE } = require('../commons/constants');
|
|
8
|
-
const logger = require('../commons/logger').getLogger('base-worker');
|
|
9
|
-
const testResultService = require('../commons/socket/testResultService');
|
|
10
|
-
const remoteStepService = require('../commons/socket/remoteStepService');
|
|
11
|
-
const { isNetworkHealthy, didNetworkConnectivityTestFail } = require('../commons/httpRequest');
|
|
12
|
-
const testimServicesApi = require('../commons/testimServicesApi');
|
|
13
|
-
const gridService = require('../services/gridService');
|
|
14
|
-
const LambdatestService = require('../services/lambdatestService');
|
|
15
|
-
const reporter = require('../reports/reporter');
|
|
16
|
-
const utils = require('../utils');
|
|
17
|
-
const { releasePlayer } = require('./workerUtils');
|
|
18
|
-
const featureFlags = require('../commons/featureFlags');
|
|
19
|
-
const perf = require('../commons/performance-logger');
|
|
20
|
-
const {
|
|
21
|
-
SeleniumError, StopRunOnError, GridError, GetBrowserError, NotImplementedError, PageNotAvailableError, GridConcurrencyError,
|
|
22
|
-
} = require('../errors');
|
|
23
|
-
|
|
24
|
-
const { GET_BROWSER_TIMEOUT_MSG, TEST_START_TIMEOUT_MSG, TEST_COMPLETE_TIMEOUT_MSG } = timeoutMessages;
|
|
25
|
-
const { SETUP_TIMEOUT, NETWORK_ERROR, GRID_ERROR, BROWSER_CLOSED, SELENIUM_ERROR, UNKNOWN_ERROR } = stepResult;
|
|
26
|
-
|
|
27
|
-
const DELAY_BETWEEN_TESTS = ms('1s');
|
|
28
|
-
let ordinal = 1;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* @param {string} testId
|
|
32
|
-
* @param {string} testName
|
|
33
|
-
* @param {string} resultId
|
|
34
|
-
* @param {string} reason
|
|
35
|
-
*/
|
|
36
|
-
function buildFailureResult(testId, testName, resultId, reason) {
|
|
37
|
-
return {
|
|
38
|
-
testId,
|
|
39
|
-
reason,
|
|
40
|
-
name: testName,
|
|
41
|
-
resultId,
|
|
42
|
-
success: false,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
class BaseWorker {
|
|
47
|
-
/**
|
|
48
|
-
* @param {import('../executionQueue')} executionQueue
|
|
49
|
-
* @param {import('../runOptions').RunnerOptions} options
|
|
50
|
-
* @param {string=} customExtensionLocalLocation
|
|
51
|
-
* @param {string} executionId
|
|
52
|
-
* @param {(workerId: number, testId: string, resultId: string, isRerun: boolean | undefined, testRetryKey: `${number}:${number}` ) => Promise<any>} onTestStarted
|
|
53
|
-
* @param {(workerId: number, testId: string, testResult: any, sessionId: string, shouldRerun: boolean | undefined) => Promise<void>} onTestCompleted
|
|
54
|
-
* @param {import('../testRunStatus')['onGridSlot']} onGridSlot
|
|
55
|
-
* @param {(workerId: number, testResult: { name: string; testId: string; resultId: string; runnerStatus: 'SKIPPED'; testStatus: 'quarantine' }) => void} onTestIgnored
|
|
56
|
-
* @param {boolean=} releaseSlotOnTestFinished
|
|
57
|
-
*/
|
|
58
|
-
constructor(executionQueue, options, customExtensionLocalLocation, executionId, onTestStarted, onTestCompleted, onGridSlot, onTestIgnored, releaseSlotOnTestFinished = true) {
|
|
59
|
-
this.lambdatestService = new LambdatestService();
|
|
60
|
-
|
|
61
|
-
this.id = BaseWorker.getWorkerId();
|
|
62
|
-
this.executionQueue = executionQueue;
|
|
63
|
-
this.customExtensionLocalLocation = customExtensionLocalLocation;
|
|
64
|
-
|
|
65
|
-
this.isCodeMode = options.files && options.files.length > 0;
|
|
66
|
-
this.baseUrl = options.baseUrl;
|
|
67
|
-
this.isRegressionBaselineRun = options.isRegressionBaselineRun;
|
|
68
|
-
this.testRunTimeout = options.timeout;
|
|
69
|
-
this.onTestStarted = onTestStarted;
|
|
70
|
-
this.onTestCompleted = onTestCompleted;
|
|
71
|
-
this.onGridSlot = onGridSlot;
|
|
72
|
-
this.onTestIgnored = onTestIgnored;
|
|
73
|
-
this.releaseSlotOnTestFinished = releaseSlotOnTestFinished;
|
|
74
|
-
|
|
75
|
-
this.userData = options.userData;
|
|
76
|
-
this.executionId = executionId;
|
|
77
|
-
this.options = options;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
static getWorkerId() {
|
|
81
|
-
return ordinal++;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* @param {string} browser
|
|
86
|
-
* @param {import('../testRunHandler')} testRunHandler
|
|
87
|
-
*/
|
|
88
|
-
async getGridSlot(browser, testRunHandler) {
|
|
89
|
-
const slot = await gridService.getGridSlot(browser, testRunHandler.executionId, this.options, this.id);
|
|
90
|
-
this.onGridSlot(testRunHandler.testResultId, slot);
|
|
91
|
-
return slot;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** @param {import('../testRunHandler')} testRunHandler */
|
|
95
|
-
async getSlotOnce(testRunHandler) {
|
|
96
|
-
const { browserValue } = this.testRunConfig;
|
|
97
|
-
reporter.onGetSlot(this.id, browserValue || 'chrome');
|
|
98
|
-
const gridInfo = await this.getGridSlot(browserValue, testRunHandler);
|
|
99
|
-
return gridInfo;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* @abstract
|
|
104
|
-
* @returns {import('../player/appiumTestPlayer') | import('../player/seleniumTestPlayer') | import('../player/extensionTestPlayer') | import('../player/chromeLauncherTestPlayer')}
|
|
105
|
-
*/
|
|
106
|
-
initPlayer() {
|
|
107
|
-
throw new NotImplementedError(true);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async getBrowserOnce() {
|
|
111
|
-
throw new NotImplementedError(true);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @param {import('../testRunHandler')} testRunHandler
|
|
116
|
-
* @param {ReturnType<typeof this['initPlayer']>} player
|
|
117
|
-
*/
|
|
118
|
-
async runTestOnce(testRunHandler, player) {
|
|
119
|
-
testRunHandler.sessionId = player.getSessionId();
|
|
120
|
-
logger.info('Test run started', {
|
|
121
|
-
testId: testRunHandler.testId,
|
|
122
|
-
resultId: testRunHandler.testResultId,
|
|
123
|
-
seleniumSession: player.getSessionId(),
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
return await testRunHandler.clearTestResult();
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** @param {import('../testRunHandler')} testRunHandler */
|
|
130
|
-
handleQuarantine(testRunHandler) {
|
|
131
|
-
if (!utils.isQuarantineAndNotRemoteRun({ testStatus: testRunHandler.testStatus }, this.options)) {
|
|
132
|
-
return undefined;
|
|
133
|
-
}
|
|
134
|
-
const testResult = {
|
|
135
|
-
name: testRunHandler.testName,
|
|
136
|
-
testId: testRunHandler.testId,
|
|
137
|
-
resultId: testRunHandler.testResultId,
|
|
138
|
-
runnerStatus: runnerTestStatus.SKIPPED,
|
|
139
|
-
testStatus: testRunHandler.testStatus,
|
|
140
|
-
};
|
|
141
|
-
this.onTestIgnored(this.id, testResult);
|
|
142
|
-
return testResult;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
setSessionTimeout() {
|
|
146
|
-
if (this.options.mode === CLI_MODE.APPIUM) {
|
|
147
|
-
return Math.max(ms('180s'), this.options.getSessionTimeout);
|
|
148
|
-
}
|
|
149
|
-
return Math.max(this.lambdatestService.getSessionTimeout, this.options.getSessionTimeout);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* @param {import('../testRunHandler')} testRunHandler
|
|
154
|
-
* @param {string=} customExtensionLocalLocation
|
|
155
|
-
*/
|
|
156
|
-
async getTestPlayer(testRunHandler, customExtensionLocalLocation) {
|
|
157
|
-
const projectId = this.userData?.projectId;
|
|
158
|
-
let testPlayer;
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
perf.log('before getSlotOnce retries');
|
|
162
|
-
let failedGetSlotAttempts = 0;
|
|
163
|
-
|
|
164
|
-
let gridInfo = await pRetry(async () => {
|
|
165
|
-
const startTime = Date.now();
|
|
166
|
-
try {
|
|
167
|
-
return await utils.promiseTimeout(this.getSlotOnce(testRunHandler), this.options.getBrowserTimeout, timeoutMessages.GET_BROWSER_TIMEOUT_MSG);
|
|
168
|
-
} catch (error) {
|
|
169
|
-
const logDetails = { testId: this.testId, testResultId: this.testResultId, executionId: this.executionId };
|
|
170
|
-
if (error instanceof GridConcurrencyError) {
|
|
171
|
-
logger.info('could not get grid slot due to concurrency issue', logDetails);
|
|
172
|
-
} else {
|
|
173
|
-
logger.error('error getting grid slot', { error, ...logDetails });
|
|
174
|
-
}
|
|
175
|
-
failedGetSlotAttempts++;
|
|
176
|
-
await utils.delay(this.options.getBrowserTimeout - (Date.now() - startTime));
|
|
177
|
-
throw error;
|
|
178
|
-
}
|
|
179
|
-
}, { retries: this.options.getBrowserRetries - 1, minTimeout: 0, factor: 1 });
|
|
180
|
-
perf.log('after getSlotOnce retries');
|
|
181
|
-
|
|
182
|
-
perf.log('before getBrowserOnce retries');
|
|
183
|
-
const getBrowserRetriesNumber = this.options.getBrowserRetries - failedGetSlotAttempts;
|
|
184
|
-
if (!getBrowserRetriesNumber) {
|
|
185
|
-
throw new Error('No free browser slots in desired grid');
|
|
186
|
-
}
|
|
187
|
-
let failedGetBrowserAttempts = 0;
|
|
188
|
-
testPlayer = await pRetry(async () => {
|
|
189
|
-
const startTime = Date.now();
|
|
190
|
-
const player = this.initPlayer(testRunHandler);
|
|
191
|
-
try {
|
|
192
|
-
gridInfo = await gridService.handleHybridOrVendorIfNeeded(
|
|
193
|
-
this.options, gridInfo, this.testRunConfig, this.lambdatestService, { maxRetries: getBrowserRetriesNumber, currentRetry: failedGetBrowserAttempts + 1 },
|
|
194
|
-
);
|
|
195
|
-
this.options.gridData.provider = gridInfo.provider;
|
|
196
|
-
this.options.gridData.host = gridInfo.host;
|
|
197
|
-
this.options.gridData.failedGetBrowserAttempts = failedGetBrowserAttempts;
|
|
198
|
-
const getSessionTimeout = this.setSessionTimeout();
|
|
199
|
-
perf.log('before getBrowserOnce');
|
|
200
|
-
const getBrowserRes = await utils.promiseTimeout(
|
|
201
|
-
this.getBrowserOnce(testRunHandler, customExtensionLocalLocation, player, gridInfo),
|
|
202
|
-
getSessionTimeout,
|
|
203
|
-
timeoutMessages.GET_BROWSER_TIMEOUT_MSG,
|
|
204
|
-
);
|
|
205
|
-
perf.log('after getBrowserOnce');
|
|
206
|
-
reporter.onGetBrowserSuccess(this.id, projectId);
|
|
207
|
-
return player || getBrowserRes;
|
|
208
|
-
} catch (error) {
|
|
209
|
-
const grid = { provider: gridInfo.provider, host: gridInfo.host, failedGetBrowserAttempts, id: this.options.gridData.gridId, type: gridInfo.type };
|
|
210
|
-
const instanceType = this.options.mode === CLI_MODE.APPIUM ? 'device' : 'browser';
|
|
211
|
-
logger.error(`error getting ${instanceType} from grid`, { error, testId: this.testId, testResultId: this.testResultId, executionId: this.executionId, grid });
|
|
212
|
-
reporter.onGetBrowserFailure(this.id, projectId, ++failedGetBrowserAttempts);
|
|
213
|
-
player.onDone();
|
|
214
|
-
|
|
215
|
-
if (!(error instanceof PageNotAvailableError)) {
|
|
216
|
-
await utils.delay(this.options.getBrowserTimeout - (Date.now() - startTime));
|
|
217
|
-
}
|
|
218
|
-
throw error;
|
|
219
|
-
}
|
|
220
|
-
}, { retries: getBrowserRetriesNumber - 1, minTimeout: 0, factor: 1 });
|
|
221
|
-
perf.log('after getBrowserOnce retries');
|
|
222
|
-
} catch (err) {
|
|
223
|
-
await releasePlayer(this.id, this.releaseSlotOnTestFinished, projectId, testPlayer);
|
|
224
|
-
if (err instanceof PageNotAvailableError) {
|
|
225
|
-
throw err;
|
|
226
|
-
}
|
|
227
|
-
if (err instanceof GridError) {
|
|
228
|
-
throw new GetBrowserError(err, GRID_ERROR);
|
|
229
|
-
}
|
|
230
|
-
throw new GetBrowserError(err, SELENIUM_ERROR);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return testPlayer;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* @param {import('../testRunHandler')} testRunHandler
|
|
238
|
-
* @param {string=} customExtensionLocalLocation
|
|
239
|
-
* @param {boolean=} shouldRerun
|
|
240
|
-
*/
|
|
241
|
-
async runTest(testRunHandler, customExtensionLocalLocation, shouldRerun) {
|
|
242
|
-
perf.log('inside runTest');
|
|
243
|
-
const projectId = this.userData?.projectId;
|
|
244
|
-
const quarantineResult = this.handleQuarantine(testRunHandler);
|
|
245
|
-
if (quarantineResult) {
|
|
246
|
-
return quarantineResult;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
perf.log('before runTest onTestStarted');
|
|
250
|
-
const test = await this.onTestStarted(this.id, testRunHandler.testId, testRunHandler.testResultId, shouldRerun, testRunHandler.retryKey);
|
|
251
|
-
testRunHandler._baseUrl = test.config.baseUrl;
|
|
252
|
-
|
|
253
|
-
const testPlayer = await this.getTestPlayer(testRunHandler, customExtensionLocalLocation);
|
|
254
|
-
try {
|
|
255
|
-
return await this.runTestOnce(testRunHandler, testPlayer);
|
|
256
|
-
} finally {
|
|
257
|
-
await releasePlayer(this.id, this.releaseSlotOnTestFinished, projectId, testPlayer);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
async runTestCleanup() {
|
|
262
|
-
return undefined;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
onQueueCompleted() {
|
|
266
|
-
return undefined;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
run() {
|
|
270
|
-
const runNextTest = () => process.nextTick(() => this.run());
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* @param {*} testResult
|
|
274
|
-
* @param {import('../testRunHandler')} testRunHandler
|
|
275
|
-
* @param {Error=} err
|
|
276
|
-
*/
|
|
277
|
-
const onRunComplete = async (testResult, testRunHandler, err) => {
|
|
278
|
-
if (utils.isQuarantineAndNotRemoteRun(testResult, this.options)) {
|
|
279
|
-
return runNextTest();
|
|
280
|
-
}
|
|
281
|
-
const sessionId = testRunHandler.sessionId;
|
|
282
|
-
|
|
283
|
-
const isTimeoutError = (timeoutMsg) => err.message.includes(timeoutMsg);
|
|
284
|
-
const isIgnoreErrors = err && (err instanceof GetBrowserError);
|
|
285
|
-
const isTimeoutErrors = err && (isTimeoutError(TEST_START_TIMEOUT_MSG) || isTimeoutError(TEST_COMPLETE_TIMEOUT_MSG));
|
|
286
|
-
|
|
287
|
-
const shouldRerun =
|
|
288
|
-
!testResult.success &&
|
|
289
|
-
(
|
|
290
|
-
(testRunHandler.hasMoreRetries() && !isIgnoreErrors && !isTimeoutErrors) ||
|
|
291
|
-
(isTimeoutErrors && testRunHandler.hasMoreTimeoutRetries())
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
try {
|
|
295
|
-
const testRetryKey = testRunHandler.retryKey;
|
|
296
|
-
testResult.testRetryKey = testRetryKey;
|
|
297
|
-
await this.onTestCompleted(this.id, this.testId, testResult, sessionId, shouldRerun);
|
|
298
|
-
if (this.executionQueue.hasMoreTests() && !this.options.lightweightMode?.general) {
|
|
299
|
-
await utils.delay(DELAY_BETWEEN_TESTS);
|
|
300
|
-
}
|
|
301
|
-
await this.runTestCleanup();
|
|
302
|
-
if (shouldRerun) {
|
|
303
|
-
if (isTimeoutErrors) {
|
|
304
|
-
await testRunHandler.startNewTimeoutRetry();
|
|
305
|
-
} else {
|
|
306
|
-
await testRunHandler.startNewRetry();
|
|
307
|
-
}
|
|
308
|
-
logger.info(`retry test id: ${this.testId} name: ${this.testName} again`, {
|
|
309
|
-
testId: this.testId,
|
|
310
|
-
testName: this.testName,
|
|
311
|
-
isTimeoutErrors,
|
|
312
|
-
testRetryKey,
|
|
313
|
-
totalRetries: testRunHandler._totalRetryCount,
|
|
314
|
-
});
|
|
315
|
-
this.testResultId = testRunHandler.testResultId;
|
|
316
|
-
return await runTestAndCalcResult(testRunHandler, shouldRerun);
|
|
317
|
-
}
|
|
318
|
-
return await runNextTest();
|
|
319
|
-
} catch (error) {
|
|
320
|
-
if (error instanceof StopRunOnError) {
|
|
321
|
-
return undefined;
|
|
322
|
-
}
|
|
323
|
-
logger.error('failed to process test result', { error });
|
|
324
|
-
runNextTest();
|
|
325
|
-
return undefined;
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
const getNetworkErrorMessage = () => 'Due to network connectivity issues, Testim CLI has been unable to connect to the grid.\n' +
|
|
329
|
-
`Please make sure the CLI has stable access to the internet. ${didNetworkConnectivityTestFail() ? '(Internal: network connectivity test failed)' : ''}`;
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* @param {Error} err
|
|
333
|
-
* @param {boolean} wasNetworkHealthy
|
|
334
|
-
*/
|
|
335
|
-
const buildError = (err, wasNetworkHealthy) => {
|
|
336
|
-
const instanceType = this.options.mode === CLI_MODE.APPIUM ? 'device' : 'browser';
|
|
337
|
-
if (!wasNetworkHealthy && featureFlags.flags.errorMessageOnBadNetwork.isEnabled()) {
|
|
338
|
-
return {
|
|
339
|
-
errorType: NETWORK_ERROR,
|
|
340
|
-
reason: getNetworkErrorMessage(),
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const msg = err instanceof Error ? err.message : err;
|
|
345
|
-
if (msg.includes(GET_BROWSER_TIMEOUT_MSG)) {
|
|
346
|
-
return { errorType: SETUP_TIMEOUT, reason: `Test couldn't get ${instanceType}` };
|
|
347
|
-
}
|
|
348
|
-
if (msg.includes(TEST_START_TIMEOUT_MSG)) {
|
|
349
|
-
return { errorType: SETUP_TIMEOUT, reason: "Test couldn't be started" };
|
|
350
|
-
}
|
|
351
|
-
if (msg.includes(TEST_COMPLETE_TIMEOUT_MSG)) {
|
|
352
|
-
if (!this.testRunTimeout) {
|
|
353
|
-
return { errorType: SETUP_TIMEOUT, reason: 'Test timeout reached: test is too long' };
|
|
354
|
-
}
|
|
355
|
-
const duration = moment.duration(this.testRunTimeout, 'milliseconds');
|
|
356
|
-
const minutesCount = Math.floor(duration.asMinutes());
|
|
357
|
-
const secondsCount = duration.seconds();
|
|
358
|
-
const minutesTimeoutStr = minutesCount > 0 ? ` ${minutesCount} min` : '';
|
|
359
|
-
const secondsTimoutStr = secondsCount > 0 ? ` ${secondsCount} sec` : '';
|
|
360
|
-
return { errorType: SETUP_TIMEOUT, reason: `Test timeout reached (timeout:${minutesTimeoutStr}${secondsTimoutStr}): test is too long` };
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
if (err instanceof GetBrowserError && err.type) {
|
|
364
|
-
if (err.type === GRID_ERROR) {
|
|
365
|
-
return { errorType: GRID_ERROR, reason: `Test couldn't get ${instanceType} from grid - ${err.message}` };
|
|
366
|
-
}
|
|
367
|
-
if (err.type === SELENIUM_ERROR) {
|
|
368
|
-
return { errorType: SELENIUM_ERROR, reason: `Failed to create new session - ${err.message}` };
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
if (err.type === BROWSER_CLOSED) {
|
|
373
|
-
return { errorType: SELENIUM_ERROR, reason: 'Session terminated, it is possible that the cli could not connect to the grid to send keep-alive requests for a prolonged period' };
|
|
374
|
-
}
|
|
375
|
-
if (err.failure && err.failure instanceof SeleniumError) {
|
|
376
|
-
return { errorType: SELENIUM_ERROR, reason: `Test couldn't get ${instanceType} from grid - ${err.failure.message}` };
|
|
377
|
-
}
|
|
378
|
-
if (/SeleniumError: connect ECONNREFUSED/.test(err.message) || /Couldn't connect to selenium server/.test(err.message)) {
|
|
379
|
-
return { errorType: SELENIUM_ERROR, reason: 'Failed to connect to the grid, please check if the grid is accessible from your network' };
|
|
380
|
-
}
|
|
381
|
-
if (/terminated due to FORWARDING_TO_NODE_FAILED/.test(err.message)) {
|
|
382
|
-
return { errorType: SELENIUM_ERROR, reason: 'Session terminated, it is likely that the grid is out of memory or not responding, please try to rerun the test' };
|
|
383
|
-
}
|
|
384
|
-
if (/terminated due to PROXY_REREGISTRATION/.test(err.message)) {
|
|
385
|
-
return { errorType: SELENIUM_ERROR, reason: 'Session terminated, it is likely that the grid is not responding, please try to rerun the test' };
|
|
386
|
-
}
|
|
387
|
-
if (/forwarding the new session cannot find : Capabilities/.test(err.message)) {
|
|
388
|
-
return { errorType: SELENIUM_ERROR, reason: `Session could not be created, please check that the ${instanceType} you requested is supported in your plan` };
|
|
389
|
-
}
|
|
390
|
-
return { errorType: UNKNOWN_ERROR, reason: msg };
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* @param {Error} err
|
|
395
|
-
* @param {import('../testRunHandler')} testRunHandler
|
|
396
|
-
*/
|
|
397
|
-
const onRunError = async (err, testRunHandler) => {
|
|
398
|
-
const wasNetworkHealthy = await isNetworkHealthy();
|
|
399
|
-
if (!wasNetworkHealthy && featureFlags.flags.warnOnBadNetwork.isEnabled()) {
|
|
400
|
-
// intentional, we want to log to stderr:
|
|
401
|
-
// eslint-disable-next-line no-console
|
|
402
|
-
console.warn(getNetworkErrorMessage());
|
|
403
|
-
}
|
|
404
|
-
logger.warn('error on run', { err });
|
|
405
|
-
|
|
406
|
-
const projectId = this.userData?.projectId;
|
|
407
|
-
const { errorType, reason } = buildError(err, wasNetworkHealthy);
|
|
408
|
-
testimServicesApi.updateTestResult(projectId, this.testResultId, this.testId, {
|
|
409
|
-
status: testRunStatus.COMPLETED,
|
|
410
|
-
success: false,
|
|
411
|
-
reason,
|
|
412
|
-
errorType,
|
|
413
|
-
testRetryKey: testRunHandler.retryKey,
|
|
414
|
-
setupStepResult: { status: testRunStatus.COMPLETED, success: false, reason, errorType },
|
|
415
|
-
}, testRunHandler.remoteRunId);
|
|
416
|
-
await onRunComplete(buildFailureResult(this.testId, this.testName, this.testResultId, reason), testRunHandler, err);
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* @param {Error} runError
|
|
421
|
-
* @param {import('../testRunHandler')} testRunHandler
|
|
422
|
-
*/
|
|
423
|
-
const recoverTestResults = async (runError, testRunHandler) => {
|
|
424
|
-
const testId = this.testId;
|
|
425
|
-
const resultId = this.testResultId;
|
|
426
|
-
const projectId = this.userData?.projectId;
|
|
427
|
-
const branch = this.branch;
|
|
428
|
-
if (!testId || !resultId || !projectId || !branch) {
|
|
429
|
-
// Not enough data to call the API
|
|
430
|
-
logger.warn('Test failed. Not enough data to recover results via API', { err: runError });
|
|
431
|
-
return onRunError(runError, testRunHandler);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
try {
|
|
435
|
-
const testResult = await testimServicesApi.getTestResults(testId, resultId, projectId, branch);
|
|
436
|
-
logger.warn('Test failed. Got results via API', { err: runError, testResult });
|
|
437
|
-
if (testResult && testResult.status === testRunStatus.COMPLETED) {
|
|
438
|
-
return await onRunComplete(testResult, testRunHandler);
|
|
439
|
-
}
|
|
440
|
-
throw runError;
|
|
441
|
-
} catch (err) {
|
|
442
|
-
if (err !== runError) {
|
|
443
|
-
logger.error('Failed to fetch test results from server', {
|
|
444
|
-
testId,
|
|
445
|
-
resultId,
|
|
446
|
-
projectId,
|
|
447
|
-
branch,
|
|
448
|
-
err,
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
return onRunError(runError, testRunHandler);
|
|
452
|
-
}
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
const disableResults = this.options.disableSockets || (this.options.lightweightMode?.disableResults && (this.options.useChromeLauncher || this.options.mode !== 'extension'));
|
|
456
|
-
const disableRemoteStep = this.options.disableSockets || (this.options.lightweightMode?.disableRemoteStep);
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* @param {import('../testRunHandler')} testRunHandler
|
|
460
|
-
* @param {boolean=} shouldRerun
|
|
461
|
-
*/
|
|
462
|
-
const runTestAndCalcResult = async (testRunHandler, shouldRerun) => {
|
|
463
|
-
try {
|
|
464
|
-
await Promise.all([
|
|
465
|
-
!disableRemoteStep && remoteStepService.joinToRemoteStep(this.testResultId),
|
|
466
|
-
!disableResults && testResultService.joinToTestResult(this.testResultId, this.testId),
|
|
467
|
-
]);
|
|
468
|
-
testRunHandler.validateRunConfig();
|
|
469
|
-
const testResult = await this.runTest(testRunHandler, this.customExtensionLocalLocation, shouldRerun);
|
|
470
|
-
const result = await onRunComplete(testResult, testRunHandler);
|
|
471
|
-
perf.log('After onRunComplete');
|
|
472
|
-
return result;
|
|
473
|
-
} catch (runError) {
|
|
474
|
-
return recoverTestResults(runError, testRunHandler);
|
|
475
|
-
} finally {
|
|
476
|
-
if (!disableRemoteStep) {
|
|
477
|
-
remoteStepService.unlistenToRemoteStep(this.testResultId);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
};
|
|
481
|
-
|
|
482
|
-
const testRunHandler = this.executionQueue.getNext();
|
|
483
|
-
|
|
484
|
-
if (!testRunHandler) { // no more tests to run
|
|
485
|
-
return this.onQueueCompleted();
|
|
486
|
-
}
|
|
487
|
-
this.testId = testRunHandler.testId;
|
|
488
|
-
this.testName = testRunHandler.testName;
|
|
489
|
-
this.testResultId = testRunHandler.testResultId;
|
|
490
|
-
this.overrideTestConfigId = testRunHandler.overrideTestConfigId;
|
|
491
|
-
this.testRunConfig = testRunHandler.runConfig;
|
|
492
|
-
this.branch = testRunHandler.branch;
|
|
493
|
-
|
|
494
|
-
return runTestAndCalcResult(testRunHandler);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
module.exports = BaseWorker;
|