@testim/testim-cli 3.289.0 → 3.290.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 (217) hide show
  1. package/cli.js +22390 -122
  2. package/cli.js.map +1 -0
  3. package/npm-shrinkwrap.json +1951 -203
  4. package/package.json +9 -5
  5. package/OverrideTestDataBuilder.js +0 -117
  6. package/agent/routers/cliJsCode/index.js +0 -13
  7. package/agent/routers/cliJsCode/router.js +0 -63
  8. package/agent/routers/cliJsCode/service.js +0 -705
  9. package/agent/routers/codim/router.js +0 -69
  10. package/agent/routers/codim/router.test.js +0 -60
  11. package/agent/routers/codim/service.js +0 -193
  12. package/agent/routers/general/index.js +0 -36
  13. package/agent/routers/hybrid/registerRoutes.js +0 -81
  14. package/agent/routers/index.js +0 -56
  15. package/agent/routers/playground/router.js +0 -77
  16. package/agent/routers/playground/service.js +0 -96
  17. package/agent/routers/standalone-browser/registerRoutes.js +0 -47
  18. package/agent/server.js +0 -150
  19. package/cdpTestRunner.js +0 -86
  20. package/chromiumInstaller.js +0 -91
  21. package/cli/isCiRun.js +0 -10
  22. package/cli/onExit.js +0 -65
  23. package/cli/writeStackTrace.js +0 -27
  24. package/cliAgentMode.js +0 -384
  25. package/codim/codim-cli.js +0 -91
  26. package/codim/codim-npm-package/index.ts +0 -427
  27. package/codim/codim-npm-package/package-lock.json +0 -14
  28. package/codim/codim-npm-package/package.json +0 -14
  29. package/codim/hybrid-utils.js +0 -28
  30. package/codim/measure-perf.js +0 -41
  31. package/codim/template.js/.idea/workspace.xml +0 -57
  32. package/codim/template.js/.vscode/launch.json +0 -53
  33. package/codim/template.ts/.idea/workspace.xml +0 -57
  34. package/codim/template.ts/.vscode/launch.json +0 -55
  35. package/commons/AbortError.js +0 -12
  36. package/commons/SeleniumPerfStats.js +0 -58
  37. package/commons/chrome-launcher.js +0 -15
  38. package/commons/chromedriverWrapper.js +0 -70
  39. package/commons/config.js +0 -39
  40. package/commons/constants.js +0 -67
  41. package/commons/detectDebugger.js +0 -19
  42. package/commons/featureAvailabilityService.js +0 -26
  43. package/commons/featureFlags.js +0 -132
  44. package/commons/getSessionPlayerRequire.js +0 -28
  45. package/commons/httpRequest.js +0 -261
  46. package/commons/httpRequestCounters.js +0 -98
  47. package/commons/httpRequestCounters.test.js +0 -38
  48. package/commons/initializeUserWithAuth.js +0 -55
  49. package/commons/lazyRequire.js +0 -105
  50. package/commons/logUtils.js +0 -15
  51. package/commons/logUtils.test.js +0 -21
  52. package/commons/logger.js +0 -178
  53. package/commons/mockNetworkRuleFileSchema.json +0 -140
  54. package/commons/npmWrapper.js +0 -174
  55. package/commons/npmWrapper.test.js +0 -374
  56. package/commons/performance-logger.js +0 -71
  57. package/commons/preloadTests.js +0 -29
  58. package/commons/prepareRunner.js +0 -85
  59. package/commons/prepareRunner.test.js +0 -144
  60. package/commons/prepareRunnerAndTestimStartUtils.js +0 -198
  61. package/commons/prepareRunnerAndTestimStartUtils.test.js +0 -73
  62. package/commons/requireWithFallback.js +0 -25
  63. package/commons/runnerFileCache.js +0 -204
  64. package/commons/socket/baseSocketServiceSocketIO.js +0 -197
  65. package/commons/socket/realDataService.js +0 -59
  66. package/commons/socket/realDataServiceSocketIO.js +0 -33
  67. package/commons/socket/remoteStepService.js +0 -55
  68. package/commons/socket/remoteStepServiceSocketIO.js +0 -61
  69. package/commons/socket/socketService.js +0 -175
  70. package/commons/socket/testResultService.js +0 -62
  71. package/commons/socket/testResultServiceSocketIO.js +0 -64
  72. package/commons/testimAnalytics.js +0 -40
  73. package/commons/testimCloudflare.js +0 -83
  74. package/commons/testimCloudflare.test.js +0 -185
  75. package/commons/testimCustomToken.js +0 -124
  76. package/commons/testimDesiredCapabilitiesBuilder.js +0 -647
  77. package/commons/testimNgrok.js +0 -90
  78. package/commons/testimNgrok.test.js +0 -140
  79. package/commons/testimServicesApi.js +0 -631
  80. package/commons/testimTunnel.js +0 -73
  81. package/commons/testimTunnel.test.js +0 -172
  82. package/commons/xhr2.js +0 -897
  83. package/coverage/SummaryToObjectReport.js +0 -19
  84. package/coverage/jsCoverage.js +0 -252
  85. package/credentialsManager.js +0 -142
  86. package/errors.js +0 -161
  87. package/executionQueue.js +0 -37
  88. package/fixLocalBuild.js +0 -24
  89. package/inputFileUtils.js +0 -103
  90. package/lib/coralogix-winston.transport.js +0 -99
  91. package/player/SeleniumProtocolError.js +0 -100
  92. package/player/WebDriverHttpRequest.js +0 -177
  93. package/player/WebdriverioWebDriverApi.js +0 -671
  94. package/player/appiumTestPlayer.js +0 -90
  95. package/player/chromeLauncherTestPlayer.js +0 -67
  96. package/player/constants.js +0 -332
  97. package/player/extensionTestPlayer.js +0 -32
  98. package/player/findElementStrategy.js +0 -154
  99. package/player/scripts/isElementDisplayed.js +0 -252
  100. package/player/seleniumTestPlayer.js +0 -140
  101. package/player/services/frameLocator.js +0 -170
  102. package/player/services/mobileFrameLocatorMock.js +0 -32
  103. package/player/services/playbackTimeoutCalculator.js +0 -175
  104. package/player/services/portSelector.js +0 -19
  105. package/player/services/tabService.js +0 -551
  106. package/player/services/tabServiceMock.js +0 -167
  107. package/player/services/windowCreationListener.js +0 -8
  108. package/player/stepActions/RefreshStepAction.js +0 -16
  109. package/player/stepActions/apiStepAction.js +0 -89
  110. package/player/stepActions/baseCliJsStepAction.js +0 -51
  111. package/player/stepActions/baseJsStepAction.js +0 -277
  112. package/player/stepActions/cliConditionStepAction.js +0 -11
  113. package/player/stepActions/cliJsStepAction.js +0 -11
  114. package/player/stepActions/dropFileStepAction.js +0 -34
  115. package/player/stepActions/evaluateExpressionStepAction.js +0 -52
  116. package/player/stepActions/extensionOnlyStepAction.js +0 -12
  117. package/player/stepActions/extractTextStepAction.js +0 -19
  118. package/player/stepActions/hoverStepAction.js +0 -55
  119. package/player/stepActions/inputFileStepAction.js +0 -199
  120. package/player/stepActions/jsCodeStepAction.js +0 -11
  121. package/player/stepActions/jsConditionStepAction.js +0 -11
  122. package/player/stepActions/locateStepAction.js +0 -159
  123. package/player/stepActions/mouseStepAction.js +0 -370
  124. package/player/stepActions/navigationStepAction.js +0 -29
  125. package/player/stepActions/nodePackageStepAction.js +0 -47
  126. package/player/stepActions/pixelValidationStepAction.js +0 -39
  127. package/player/stepActions/scripts/dispatchEvents.js +0 -282
  128. package/player/stepActions/scripts/doClick.js +0 -221
  129. package/player/stepActions/scripts/doDragPath.js +0 -225
  130. package/player/stepActions/scripts/doubleClick.js +0 -119
  131. package/player/stepActions/scripts/dropEvent.js +0 -63
  132. package/player/stepActions/scripts/focusElement.js +0 -46
  133. package/player/stepActions/scripts/html5dragAction.js +0 -56
  134. package/player/stepActions/scripts/html5dragActionV2.js +0 -312
  135. package/player/stepActions/scripts/runCode.js +0 -147
  136. package/player/stepActions/scripts/scroll.js +0 -90
  137. package/player/stepActions/scripts/selectOption.js +0 -51
  138. package/player/stepActions/scripts/setText.js +0 -415
  139. package/player/stepActions/scripts/wheel.js +0 -61
  140. package/player/stepActions/scrollStepAction.js +0 -96
  141. package/player/stepActions/selectOptionStepAction.js +0 -49
  142. package/player/stepActions/sfdcRecordedStepAction.js +0 -24
  143. package/player/stepActions/sfdcStepAction.js +0 -28
  144. package/player/stepActions/sleepStepAction.js +0 -12
  145. package/player/stepActions/specialKeyStepAction.js +0 -52
  146. package/player/stepActions/stepAction.js +0 -73
  147. package/player/stepActions/stepActionRegistrar.js +0 -111
  148. package/player/stepActions/submitStepAction.js +0 -12
  149. package/player/stepActions/tdkHybridStepAction.js +0 -18
  150. package/player/stepActions/textStepAction.js +0 -110
  151. package/player/stepActions/textValidationStepAction.js +0 -64
  152. package/player/stepActions/wheelStepAction.js +0 -41
  153. package/player/utils/cookieUtils.js +0 -39
  154. package/player/utils/eyeSdkService.js +0 -250
  155. package/player/utils/imageCaptureUtils.js +0 -267
  156. package/player/utils/screenshotUtils.js +0 -68
  157. package/player/utils/stepActionUtils.js +0 -90
  158. package/player/utils/windowUtils.js +0 -195
  159. package/player/webDriverUtils.js +0 -40
  160. package/player/webDriverUtils.test.js +0 -116
  161. package/player/webdriver.js +0 -976
  162. package/polyfills/Array.prototype.at.js +0 -13
  163. package/polyfills/index.js +0 -13
  164. package/processHandler.js +0 -79
  165. package/processHandler.test.js +0 -55
  166. package/reports/chromeReporter.js +0 -17
  167. package/reports/consoleReporter.js +0 -190
  168. package/reports/debugReporter.js +0 -82
  169. package/reports/jsonReporter.js +0 -55
  170. package/reports/junitReporter.js +0 -183
  171. package/reports/reporter.js +0 -166
  172. package/reports/reporterUtils.js +0 -54
  173. package/reports/teamCityReporter.js +0 -73
  174. package/runOptions.d.ts +0 -305
  175. package/runOptions.js +0 -1288
  176. package/runOptionsAgentFlow.js +0 -87
  177. package/runOptionsUtils.js +0 -60
  178. package/runner.js +0 -355
  179. package/runners/ParallelWorkerManager.js +0 -284
  180. package/runners/TestPlanRunner.js +0 -419
  181. package/runners/buildCodeTests.js +0 -159
  182. package/runners/runnerUtils.js +0 -81
  183. package/services/analyticsService.js +0 -96
  184. package/services/branchService.js +0 -29
  185. package/services/gridService.js +0 -357
  186. package/services/gridService.test.js +0 -357
  187. package/services/labFeaturesService.js +0 -64
  188. package/services/lambdatestService.js +0 -227
  189. package/services/lambdatestService.test.js +0 -353
  190. package/services/localRCASaver.js +0 -124
  191. package/stepPlayers/cliJsStepPlayback.js +0 -40
  192. package/stepPlayers/hybridStepPlayback.js +0 -140
  193. package/stepPlayers/nodePackageStepPlayback.js +0 -28
  194. package/stepPlayers/playwrightHybridStepPlayback.js +0 -61
  195. package/stepPlayers/puppeteerHybridStepPlayback.js +0 -76
  196. package/stepPlayers/remoteStepPlayback.js +0 -80
  197. package/stepPlayers/seleniumHybridStepPlayback.js +0 -84
  198. package/stepPlayers/tdkHybridStepPlayback.js +0 -112
  199. package/testRunHandler.js +0 -603
  200. package/testRunStatus.js +0 -567
  201. package/testimNpmDriver.js +0 -52
  202. package/utils/argsUtils.js +0 -91
  203. package/utils/argsUtils.test.js +0 -32
  204. package/utils/fsUtils.js +0 -174
  205. package/utils/index.js +0 -197
  206. package/utils/promiseUtils.js +0 -85
  207. package/utils/stringUtils.js +0 -98
  208. package/utils/stringUtils.test.js +0 -22
  209. package/utils/timeUtils.js +0 -25
  210. package/utils/utils.test.js +0 -27
  211. package/workers/BaseWorker.js +0 -498
  212. package/workers/BaseWorker.test.js +0 -186
  213. package/workers/WorkerAppium.js +0 -180
  214. package/workers/WorkerExtension.js +0 -192
  215. package/workers/WorkerExtensionSingleBrowser.js +0 -77
  216. package/workers/WorkerSelenium.js +0 -253
  217. 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
- });
@@ -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
- };
@@ -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
- });
@@ -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;