@testim/testim-cli 3.266.0 → 3.268.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/commons/socket/baseSocketServiceSocketIO.js +25 -2
- package/commons/socket/testResultService.js +0 -1
- package/commons/testimDesiredCapabilitiesBuilder.js +14 -7
- package/commons/testimServicesApi.js +31 -5
- package/npm-shrinkwrap.json +38 -38
- package/package.json +1 -1
- package/reports/consoleReporter.js +6 -2
- package/runOptions.d.ts +4 -3
- package/runOptions.js +3 -2
- package/runner.js +1 -1
- package/runners/ParallelWorkerManager.js +66 -12
- package/runners/TestPlanRunner.js +2 -4
- package/services/gridService.js +48 -36
- package/services/gridService.test.js +11 -18
- package/testRunHandler.js +216 -235
- package/testRunStatus.js +9 -1
- package/testimNpmDriver.js +15 -18
- package/workers/BaseWorker.js +98 -44
- package/workers/BaseWorker.test.js +2 -7
- package/workers/WorkerAppium.js +64 -24
- package/workers/WorkerExtension.js +11 -5
- package/workers/WorkerExtensionSingleBrowser.js +12 -2
- package/workers/WorkerSelenium.js +29 -20
package/testRunHandler.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const pRetry = require('p-retry');
|
|
4
3
|
const _ = require('lodash');
|
|
4
|
+
const pRetry = require('p-retry');
|
|
5
|
+
const utils = require('./utils');
|
|
6
|
+
const config = require('./commons/config');
|
|
7
|
+
const perf = require('./commons/performance-logger');
|
|
8
|
+
const analytics = require('./commons/testimAnalytics');
|
|
5
9
|
const testimCustomToken = require('./commons/testimCustomToken');
|
|
10
|
+
const testimServicesApi = require('./commons/testimServicesApi');
|
|
11
|
+
const remoteStepPlayback = require('./stepPlayers/remoteStepPlayback');
|
|
6
12
|
const remoteStepService = require('./commons/socket/remoteStepService');
|
|
7
13
|
const testResultService = require('./commons/socket/testResultService');
|
|
8
|
-
const testimServicesApi = require('./commons/testimServicesApi');
|
|
9
|
-
const { timeoutMessages, CLI_MODE } = require('./commons/constants');
|
|
10
|
-
const logger = require('./commons/logger').getLogger('test-run-handler');
|
|
11
|
-
const perf = require('./commons/performance-logger');
|
|
12
14
|
const { URL } = require('url');
|
|
13
|
-
const
|
|
14
|
-
const remoteStepPlayback = require('./stepPlayers/remoteStepPlayback');
|
|
15
|
-
const utils = require('./utils');
|
|
16
|
-
const config = require('./commons/config');
|
|
17
|
-
const analytics = require('./commons/testimAnalytics');
|
|
18
|
-
const { SeleniumPerfStats } = require('./commons/SeleniumPerfStats');
|
|
15
|
+
const { getLogger } = require('./commons/logger');
|
|
19
16
|
const { preloadTests } = require('./commons/preloadTests');
|
|
17
|
+
const { timeoutMessages, CLI_MODE } = require('./commons/constants');
|
|
18
|
+
const { SeleniumPerfStats } = require('./commons/SeleniumPerfStats');
|
|
20
19
|
|
|
20
|
+
const logger = getLogger('test-run-handler');
|
|
21
21
|
const RETRIES_ON_TIMEOUT = 3;
|
|
22
22
|
const MAX_LIGHTWEIGHT_MODE_RUN_DATA_SIZE = 20 * 1000; // max size, in characters, of stringified run data sent over URL params. Chosen arbitrarily, this value should be changed according to data.
|
|
23
23
|
const canSendRunDataOverUrl = (runData) => JSON.stringify(runData).length < MAX_LIGHTWEIGHT_MODE_RUN_DATA_SIZE;
|
|
@@ -26,7 +26,7 @@ class TestRun {
|
|
|
26
26
|
/**
|
|
27
27
|
* @param {string} executionId
|
|
28
28
|
* @param {string} executionName
|
|
29
|
-
* @param {
|
|
29
|
+
* @param {import('./runners/TestPlanRunner').ExecutionList[number]} test
|
|
30
30
|
* @param {import('./runOptions').RunnerOptions} options
|
|
31
31
|
* @param {string} branchToUse
|
|
32
32
|
* @param {import('./testRunStatus')} testRunStatus
|
|
@@ -58,76 +58,78 @@ class TestRun {
|
|
|
58
58
|
this.seleniumPerfStats = new SeleniumPerfStats();
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
waitForExecutionStartedFinished() {
|
|
62
|
-
return this._testRunStatus.waitForExecutionStartedFinished() && this.clearTestResultFinished;
|
|
61
|
+
async waitForExecutionStartedFinished() {
|
|
62
|
+
return await this._testRunStatus.waitForExecutionStartedFinished() && await this.clearTestResultFinished;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
get testStatus() {
|
|
66
66
|
return this._testStatus;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
get runMode() {
|
|
70
70
|
return this._options.mode;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
get automationMode() {
|
|
74
74
|
return this._code ? 'codeful' : 'codeless';
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
get code() {
|
|
78
78
|
return this._code;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
get runConfig() {
|
|
82
82
|
return this._runConfig;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
get testResultId() {
|
|
86
86
|
return this._testResultId;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
get baseUrl() {
|
|
90
90
|
return this._baseUrl;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
get executionId() {
|
|
94
94
|
return this._executionId;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
get executionName() {
|
|
98
98
|
return this._executionName;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
getNativeAppData() {
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
async getNativeAppData() {
|
|
102
|
+
const { appId, baseUrl } = this._options;
|
|
103
|
+
if (baseUrl && !this._nativeApp && !appId) {
|
|
104
|
+
const url = baseUrl || this.baseUrl;
|
|
105
|
+
if (!url) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const [packageName, activity] = url.split(':');
|
|
109
|
+
return {
|
|
110
|
+
packageName,
|
|
111
|
+
activity,
|
|
112
|
+
};
|
|
104
113
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (!url) {
|
|
108
|
-
return null;
|
|
114
|
+
if (this._nativeApp && !appId) {
|
|
115
|
+
return this._nativeApp;
|
|
109
116
|
}
|
|
110
|
-
|
|
111
|
-
const [packageName, activity] = url.split(':');
|
|
112
|
-
return {
|
|
113
|
-
packageName,
|
|
114
|
-
activity,
|
|
115
|
-
};
|
|
117
|
+
return null;
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
|
|
120
|
+
get branch() {
|
|
119
121
|
return this._branch;
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
|
|
124
|
+
get sfdcCredential() {
|
|
123
125
|
return this._options.sfdcCredential;
|
|
124
126
|
}
|
|
125
127
|
|
|
126
|
-
|
|
128
|
+
get remoteRunId() {
|
|
127
129
|
return this._remoteRunId;
|
|
128
130
|
}
|
|
129
131
|
|
|
130
|
-
|
|
132
|
+
get overrideTestConfigId() {
|
|
131
133
|
return this._overrideTestConfigId;
|
|
132
134
|
}
|
|
133
135
|
|
|
@@ -148,53 +150,38 @@ class TestRun {
|
|
|
148
150
|
baseUrl: this._baseUrl,
|
|
149
151
|
branch: this._branch,
|
|
150
152
|
servicesUrl: config.EXTENSION_SERVICES_HOST,
|
|
151
|
-
remoteRunId: this.
|
|
152
|
-
previousTestResultId: this.
|
|
153
|
-
testRetryCount: this.
|
|
153
|
+
remoteRunId: this.remoteRunId,
|
|
154
|
+
previousTestResultId: this.previousTestResultId,
|
|
155
|
+
testRetryCount: this.retryCount,
|
|
156
|
+
...(this.code && { isCodeMode: true, testName: this.testName }),
|
|
157
|
+
...(this._options.shouldMonitorPerformance && { shouldMonitorPerformance: true }),
|
|
158
|
+
...(this._options.company && {
|
|
159
|
+
companyId: this._options.company.companyId,
|
|
160
|
+
onprem: this._options.company.onprem,
|
|
161
|
+
storageBaseUrl: this._options.company.storageBaseUrl,
|
|
162
|
+
storageType: this._options.company.storageType,
|
|
163
|
+
planType: this._options.company.planType,
|
|
164
|
+
isPOC: this._options.company.isPOC,
|
|
165
|
+
isStartUp: this._options.company.isStartUp,
|
|
166
|
+
}),
|
|
167
|
+
...(this._options.collectCodeCoverage && { codeCoverageUrlFilter: this._options.codeCoverageUrlFilter || `${this.baseUrl}*` }),
|
|
168
|
+
...(this._options.disableMockNetwork && { disableMockNetwork: this._options.disableMockNetwork }),
|
|
169
|
+
...(this._options.lightweightMode && { lightweightMode: this._options.lightweightMode }),
|
|
170
|
+
...(this.clearBrowser && { clearBrowser: true }),
|
|
171
|
+
...(this._options.localRCASaver && { localRCASaver: this._options.localRCASaver }),
|
|
172
|
+
...(this.sfdcCredential && { sfdcCredential: this.sfdcCredential }),
|
|
154
173
|
};
|
|
155
174
|
|
|
156
|
-
if (this._code) {
|
|
157
|
-
runRequestParams.isCodeMode = true;
|
|
158
|
-
runRequestParams.testName = this._testName;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (this._options.shouldMonitorPerformance) {
|
|
162
|
-
runRequestParams.shouldMonitorPerformance = true;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (this._options.company) {
|
|
166
|
-
runRequestParams.companyId = this._options.company.companyId;
|
|
167
|
-
runRequestParams.onprem = this._options.company.onprem;
|
|
168
|
-
runRequestParams.storageBaseUrl = this._options.company.storageBaseUrl;
|
|
169
|
-
runRequestParams.storageType = this._options.company.storageType;
|
|
170
|
-
runRequestParams.planType = this._options.company.planType;
|
|
171
|
-
runRequestParams.isPOC = this._options.company.isPOC;
|
|
172
|
-
runRequestParams.isStartUp = this._options.company.isStartUp;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (this._options.collectCodeCoverage) {
|
|
176
|
-
if (this._options.codeCoverageUrlFilter) {
|
|
177
|
-
runRequestParams.codeCoverageUrlFilter = this._options.codeCoverageUrlFilter;
|
|
178
|
-
} else {
|
|
179
|
-
runRequestParams.codeCoverageUrlFilter = `${this._baseUrl}*`;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
175
|
if (this._options.disableMockNetwork) {
|
|
184
|
-
runRequestParams.disableMockNetwork = this._options.disableMockNetwork;
|
|
185
176
|
analytics.trackWithCIUser('user-disable-mock');
|
|
186
177
|
}
|
|
187
178
|
|
|
188
|
-
if (this._options.lightweightMode) {
|
|
189
|
-
runRequestParams.lightweightMode = this._options.lightweightMode;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
179
|
if (this._options.lightweightMode?.general) {
|
|
193
180
|
runRequestParams.company = this._options.company;
|
|
194
|
-
const runData = this.
|
|
181
|
+
const runData = this.runData;
|
|
195
182
|
runRequestParams.lightweightMode.isRunDataSentInUrl = canSendRunDataOverUrl(runData);
|
|
196
183
|
const stringifiedLength = JSON.stringify(runData).length;
|
|
197
|
-
const testId = this.
|
|
184
|
+
const testId = this.testId;
|
|
198
185
|
if (runRequestParams.lightweightMode.isRunDataSentInUrl) {
|
|
199
186
|
runRequestParams.runData = runData;
|
|
200
187
|
logger.info(`Run data sent as URL param, test id: ${testId} run data length: ${stringifiedLength}`);
|
|
@@ -209,59 +196,47 @@ class TestRun {
|
|
|
209
196
|
runRequestParams.preloadedTest = preloadedTests[runRequestParams.testId];
|
|
210
197
|
}
|
|
211
198
|
|
|
212
|
-
if (this.clearBrowser) {
|
|
213
|
-
runRequestParams.clearBrowser = true;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (this._options.localRCASaver) {
|
|
217
|
-
runRequestParams.localRCASaver = this._options.localRCASaver;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (this.getSfdcCredential()) {
|
|
221
|
-
runRequestParams.sfdcCredential = this.getSfdcCredential();
|
|
222
|
-
}
|
|
223
|
-
|
|
224
199
|
return runRequestParams;
|
|
225
200
|
}
|
|
226
201
|
|
|
227
202
|
async getRunTestUrl() {
|
|
228
203
|
const runRequestParams = await this.getRunRequestParams();
|
|
229
204
|
const url = `https://run.testim.io/?params=${encodeURIComponent(JSON.stringify(runRequestParams))}`;
|
|
230
|
-
logger.info(`Test (${this.
|
|
205
|
+
logger.info(`Test (${this.testId}) run URL length: ${url.length}`);
|
|
231
206
|
return url;
|
|
232
207
|
}
|
|
233
208
|
|
|
234
|
-
|
|
209
|
+
set sessionId(sessionId) {
|
|
235
210
|
this._sessionId = sessionId;
|
|
236
211
|
}
|
|
237
212
|
|
|
238
|
-
|
|
213
|
+
get sessionId() {
|
|
239
214
|
return this._sessionId;
|
|
240
215
|
}
|
|
241
216
|
|
|
242
|
-
|
|
217
|
+
get testId() {
|
|
243
218
|
return this._testId;
|
|
244
219
|
}
|
|
245
220
|
|
|
246
|
-
|
|
221
|
+
get testName() {
|
|
247
222
|
return this._testName;
|
|
248
223
|
}
|
|
249
224
|
|
|
250
|
-
|
|
225
|
+
get runParams() {
|
|
251
226
|
return this._options.runParams[this._testResultId] || {};
|
|
252
227
|
}
|
|
253
228
|
|
|
254
|
-
|
|
229
|
+
get runData() {
|
|
255
230
|
return {
|
|
256
|
-
userParamsData: this.
|
|
231
|
+
userParamsData: this.runParams,
|
|
257
232
|
overrideTestConfigId: this._overrideTestConfigId || null,
|
|
258
233
|
};
|
|
259
234
|
}
|
|
260
235
|
|
|
261
|
-
clearTestResult() {
|
|
262
|
-
const runData = this.
|
|
263
|
-
if (this.
|
|
264
|
-
runData.code = this.
|
|
236
|
+
async clearTestResult() {
|
|
237
|
+
const runData = this.runData;
|
|
238
|
+
if (this.runMode === CLI_MODE.EXTENSION) {
|
|
239
|
+
runData.code = this.code;
|
|
265
240
|
}
|
|
266
241
|
|
|
267
242
|
if (this._options.mockNetworkRules) {
|
|
@@ -270,28 +245,33 @@ class TestRun {
|
|
|
270
245
|
const mustClearPreviousStepResults = (this._timeoutRetryCount > 1 || this._retryCount > 1);
|
|
271
246
|
|
|
272
247
|
if (this._options.lightweightMode?.disableResults && !mustClearPreviousStepResults && canSendRunDataOverUrl(runData)) {
|
|
273
|
-
return
|
|
248
|
+
return undefined;
|
|
274
249
|
}
|
|
275
250
|
|
|
276
|
-
|
|
277
|
-
|
|
251
|
+
const uploadRunDataArtifactAndFallbackToEmptyString = async () => {
|
|
252
|
+
try {
|
|
253
|
+
return await testimServicesApi.uploadRunDataArtifact(this._options.project, this._testId, this._testResultId, runData);
|
|
254
|
+
} catch (err) {
|
|
278
255
|
logger.error('failed to upload run data artifact (runner)', { err });
|
|
279
256
|
return '';
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
const clearTestResultFinished = async () => {
|
|
260
|
+
const runDataUrl = await uploadRunDataArtifactAndFallbackToEmptyString();
|
|
261
|
+
// make sure the execution is created by now
|
|
262
|
+
await this._testRunStatus.waitForExecutionStartedFinished();
|
|
263
|
+
// we probably can save this backend call by initializing the execution
|
|
264
|
+
return testimServicesApi.clearTestResult(this._options.project, this._testResultId, this._testId, {
|
|
265
|
+
name: this._testName,
|
|
266
|
+
resultId: this._testResultId,
|
|
267
|
+
status: 'pending',
|
|
268
|
+
retryCount: this._retryCount,
|
|
269
|
+
runDataUrl, // links the run data url to the test.
|
|
270
|
+
runData: runDataUrl ? undefined : runData, // put runData in mongo if we fail to upload to S3.
|
|
271
|
+
testRetryKey: this.retryKey,
|
|
294
272
|
});
|
|
273
|
+
};
|
|
274
|
+
this.clearTestResultFinished = clearTestResultFinished();
|
|
295
275
|
return this.clearTestResultFinished;
|
|
296
276
|
}
|
|
297
277
|
|
|
@@ -299,7 +279,7 @@ class TestRun {
|
|
|
299
279
|
return this._retryCount < this._maxRetryCount;
|
|
300
280
|
}
|
|
301
281
|
|
|
302
|
-
|
|
282
|
+
get retryKey() {
|
|
303
283
|
return `${this._retryCount}:${this._timeoutRetryCount}`;
|
|
304
284
|
}
|
|
305
285
|
|
|
@@ -335,7 +315,7 @@ class TestRun {
|
|
|
335
315
|
if (!result.value) {
|
|
336
316
|
throw new Error('runTestimTest not available on global scope');
|
|
337
317
|
}
|
|
338
|
-
}, { retries: 100, minTimeout: 30 });
|
|
318
|
+
}, { retries: 100, minTimeout: 30, factor: 1 });
|
|
339
319
|
|
|
340
320
|
perf.log('after wait for runTestimTest function');
|
|
341
321
|
const { result } = await cdpTestRunner.cdpCommand(
|
|
@@ -355,12 +335,11 @@ class TestRun {
|
|
|
355
335
|
}
|
|
356
336
|
|
|
357
337
|
isRetryKeyMismatch(testResult) {
|
|
358
|
-
return testResult.testRetryKey && (testResult.testRetryKey !== this.
|
|
338
|
+
return testResult.testRetryKey && (testResult.testRetryKey !== this.retryKey);
|
|
359
339
|
}
|
|
360
340
|
|
|
361
341
|
validateRunConfig() {
|
|
362
|
-
const baseUrl = this
|
|
363
|
-
const { browserValue } = this.getRunConfig();
|
|
342
|
+
const { baseUrl, runConfig: { browserValue } } = this;
|
|
364
343
|
|
|
365
344
|
if (baseUrl && browserValue === 'safari') {
|
|
366
345
|
let parsedUrl;
|
|
@@ -378,6 +357,7 @@ class TestRun {
|
|
|
378
357
|
}
|
|
379
358
|
}
|
|
380
359
|
|
|
360
|
+
/** @param {number} startTimeout */
|
|
381
361
|
onStarted(startTimeout) {
|
|
382
362
|
return new Promise(resolve => {
|
|
383
363
|
// We can't leave the test result as it may remove other listeners as well
|
|
@@ -388,20 +368,20 @@ class TestRun {
|
|
|
388
368
|
return;
|
|
389
369
|
}
|
|
390
370
|
if (this.isRetryKeyMismatch(testResult)) {
|
|
391
|
-
logger.warn(`ignoring result update for on started due to retry key mismatch, got ${testResult.testRetryKey}, current is ${this.
|
|
392
|
-
resultId: this.
|
|
393
|
-
testId: this.
|
|
371
|
+
logger.warn(`ignoring result update for on started due to retry key mismatch, got ${testResult.testRetryKey}, current is ${this.retryKey}`, {
|
|
372
|
+
resultId: this.testResultId,
|
|
373
|
+
testId: this.testId,
|
|
394
374
|
});
|
|
395
375
|
return;
|
|
396
376
|
}
|
|
397
377
|
if (['running', 'completed'].includes(testResult.status)) {
|
|
398
|
-
testResult.resultId = this.
|
|
378
|
+
testResult.resultId = this.testResultId;
|
|
399
379
|
if (testResult.status === 'completed') {
|
|
400
380
|
logger.info('setting _wasCompletedOnStartedCheck to true', {
|
|
401
381
|
testResult,
|
|
402
|
-
resultId: this.
|
|
403
|
-
testId: this.
|
|
404
|
-
testRetryKey: this.
|
|
382
|
+
resultId: this.testResultId,
|
|
383
|
+
testId: this.testId,
|
|
384
|
+
testRetryKey: this.retryKey,
|
|
405
385
|
});
|
|
406
386
|
this._wasCompletedOnStartedCheck = testResult;
|
|
407
387
|
}
|
|
@@ -411,24 +391,22 @@ class TestRun {
|
|
|
411
391
|
};
|
|
412
392
|
if (this._options.disableSockets) {
|
|
413
393
|
const timeLimit = Date.now() + startTimeout;
|
|
414
|
-
const checkIfDone = () => {
|
|
394
|
+
const checkIfDone = async () => {
|
|
415
395
|
if (Date.now() > timeLimit) {
|
|
416
396
|
return;
|
|
417
397
|
}
|
|
418
|
-
const testId = this
|
|
419
|
-
const resultId = this._testResultId;
|
|
420
|
-
const projectId = this._options.project;
|
|
421
|
-
const branch = this.getBranch();
|
|
398
|
+
const { testId, testResultId, branch, _options: { project: projectId } } = this;
|
|
422
399
|
|
|
423
|
-
|
|
400
|
+
try {
|
|
401
|
+
const restResult = await testimServicesApi.getTestResults(testId, testResultId, projectId, branch);
|
|
424
402
|
resolveResult(restResult);
|
|
425
403
|
if (!reportedStart) {
|
|
426
404
|
setTimeout(checkIfDone, 3000);
|
|
427
405
|
}
|
|
428
|
-
}
|
|
406
|
+
} catch (err) {
|
|
429
407
|
logger.error('failed to check if done', { err });
|
|
430
408
|
setTimeout(checkIfDone, 3000);
|
|
431
|
-
}
|
|
409
|
+
}
|
|
432
410
|
};
|
|
433
411
|
setTimeout(checkIfDone, 3000);
|
|
434
412
|
} else {
|
|
@@ -437,116 +415,119 @@ class TestRun {
|
|
|
437
415
|
});
|
|
438
416
|
}
|
|
439
417
|
|
|
440
|
-
checkViaRestAPIIfTestStarted() {
|
|
441
|
-
const testId = this
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
.catch(err => {
|
|
456
|
-
logger.error('failed to get test result after test start timeout', { err, testId, resultId, branch });
|
|
457
|
-
throw new Error(timeoutMessages.TEST_START_TIMEOUT_MSG);
|
|
458
|
-
});
|
|
418
|
+
async checkViaRestAPIIfTestStarted() {
|
|
419
|
+
const { testId, testResultId, _options: { project: projectId }, branch } = this;
|
|
420
|
+
try {
|
|
421
|
+
const testResult = testimServicesApi.getTestResults(testId, testResultId, projectId, branch);
|
|
422
|
+
const expectedStatuses = ['running', 'completed'];
|
|
423
|
+
if (expectedStatuses.includes(testResult.status)) {
|
|
424
|
+
logger.info(`get status: ${testResult.status} after not get test started status`, { testId, testResultId, branch });
|
|
425
|
+
return testResult;
|
|
426
|
+
}
|
|
427
|
+
logger.error(`test not start test status: ${testResult.status} (expected [${expectedStatuses.join(', ')}])`, { testId, testResultId, branch });
|
|
428
|
+
throw new Error(timeoutMessages.TEST_START_TIMEOUT_MSG);
|
|
429
|
+
} catch (err) {
|
|
430
|
+
logger.error('failed to get test result after test start timeout', { err, testId, testResultId, branch });
|
|
431
|
+
throw new Error(timeoutMessages.TEST_START_TIMEOUT_MSG);
|
|
432
|
+
}
|
|
459
433
|
}
|
|
460
434
|
|
|
461
|
-
onCompletedCleanup() {
|
|
462
|
-
if (
|
|
463
|
-
return
|
|
435
|
+
async onCompletedCleanup() {
|
|
436
|
+
if (this._options.disableSockets) {
|
|
437
|
+
return undefined;
|
|
464
438
|
}
|
|
465
|
-
return
|
|
439
|
+
return await testResultService.leaveTestResult(this._testResultId, this._testId);
|
|
466
440
|
}
|
|
467
441
|
|
|
468
|
-
onCompleted() {
|
|
442
|
+
async onCompleted() {
|
|
469
443
|
let onConnected;
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
444
|
+
try {
|
|
445
|
+
const res = await new Promise(resolve => {
|
|
446
|
+
if (this._wasCompletedOnStartedCheck && !this.isRetryKeyMismatch(this._wasCompletedOnStartedCheck)) {
|
|
447
|
+
logger.info('test was already completed in on started check', {
|
|
448
|
+
resultId: this.testResultId,
|
|
449
|
+
testId: this.testId,
|
|
450
|
+
});
|
|
451
|
+
resolve(this._wasCompletedOnStartedCheck);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
479
454
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
455
|
+
if (!this._options.disableSockets) {
|
|
456
|
+
testResultService.listenToTestResult(this._testResultId, this._testId, testResult => {
|
|
457
|
+
if (this.isRetryKeyMismatch(testResult)) {
|
|
458
|
+
logger.warn(`ignoring result update for on completed due to retry key mismatch, got ${testResult.testRetryKey}, current is ${this.retryKey}`, {
|
|
459
|
+
resultId: this.testResultId,
|
|
460
|
+
testId: this.testId,
|
|
461
|
+
});
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
if (testResult.status === 'completed') {
|
|
465
|
+
testResult.resultId = this._testResultId;
|
|
466
|
+
resolve(testResult);
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
const debounceDelay = this._options.disableSockets ? 0 : Math.floor(10000 + (Math.random() * 5000));
|
|
471
|
+
const maxWait = this._options.disableSockets ? 0 : Math.floor(60000 + (Math.random() * 15000));
|
|
472
|
+
onConnected = _.debounce(async () => {
|
|
473
|
+
try {
|
|
474
|
+
const testResult = await testimServicesApi.getTestResults(this._testId, this._testResultId, this._options.project, this.branch);
|
|
475
|
+
if (this.isRetryKeyMismatch(testResult)) {
|
|
476
|
+
logger.warn(`ignoring result update for on completed (in reconnect) due to retry key mismatch, got ${testResult.testRetryKey}, current is ${this.retryKey}`, {
|
|
477
|
+
resultId: this.testResultId,
|
|
478
|
+
testId: this.testId,
|
|
479
|
+
});
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
if (testResult?.status === 'completed') {
|
|
483
|
+
logger.info('Socket reconnected - Test complete', { testId: this._testId, resultId: this._testResultId, projectId: this._options.project });
|
|
484
|
+
testResult.resultId = this._testResultId;
|
|
485
|
+
resolve(testResult);
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
488
|
+
return false;
|
|
489
|
+
} catch (err) {
|
|
490
|
+
logger.warn('Error while trying to check status on socket connect', err);
|
|
504
491
|
return false;
|
|
505
492
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const { isComplete } = await testimServicesApi.isTestResultCompleted(this._testResultId, this._options.project, this.getRetryKey());
|
|
522
|
-
if (isComplete) {
|
|
523
|
-
const isDone = await onConnected();
|
|
524
|
-
if (!isDone) {
|
|
525
|
-
logger.warn('onConnected returned false even though isComplete was true');
|
|
493
|
+
}, debounceDelay, { maxWait });
|
|
494
|
+
if (!this._options.disableSockets) {
|
|
495
|
+
testResultService.on('socket-connected', onConnected);
|
|
496
|
+
} else {
|
|
497
|
+
const waitForTestEnd = () => {
|
|
498
|
+
setTimeout(async () => {
|
|
499
|
+
try {
|
|
500
|
+
const { isComplete } = await testimServicesApi.isTestResultCompleted(this._testResultId, this._options.project, this.retryKey);
|
|
501
|
+
if (isComplete) {
|
|
502
|
+
const isDone = await onConnected();
|
|
503
|
+
if (!isDone) {
|
|
504
|
+
logger.warn('onConnected returned false even though isComplete was true');
|
|
505
|
+
waitForTestEnd();
|
|
506
|
+
}
|
|
507
|
+
} else {
|
|
526
508
|
waitForTestEnd();
|
|
527
509
|
}
|
|
528
|
-
}
|
|
510
|
+
} catch (err) {
|
|
511
|
+
logger.error('failed to check is complete', { err });
|
|
529
512
|
waitForTestEnd();
|
|
530
513
|
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
514
|
+
}, 3000);
|
|
515
|
+
};
|
|
516
|
+
waitForTestEnd();
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
await this.onCompletedCleanup();
|
|
520
|
+
return res;
|
|
521
|
+
} finally {
|
|
522
|
+
if (onConnected && !this._options.disableSockets) {
|
|
523
|
+
testResultService.off('socket-connected', onConnected);
|
|
538
524
|
}
|
|
539
|
-
}
|
|
540
|
-
.then(async res => {
|
|
541
|
-
await this.onCompletedCleanup();
|
|
542
|
-
return res;
|
|
543
|
-
})
|
|
544
|
-
.finally(() => onConnected && !this._options.disableSockets && testResultService.off('socket-connected', onConnected));
|
|
525
|
+
}
|
|
545
526
|
}
|
|
546
527
|
|
|
547
528
|
listenToRemoteStep(browser) {
|
|
548
|
-
remoteStepService.listenToRemoteStep(this.
|
|
549
|
-
remoteStepPlayback.executeStep(this._options, browser, step, this.
|
|
529
|
+
remoteStepService.listenToRemoteStep(this.testResultId, step => {
|
|
530
|
+
remoteStepPlayback.executeStep(this._options, browser, step, this.testResultId);
|
|
550
531
|
});
|
|
551
532
|
}
|
|
552
533
|
|
|
@@ -560,20 +541,20 @@ class TestRun {
|
|
|
560
541
|
return this.onRetry();
|
|
561
542
|
}
|
|
562
543
|
|
|
563
|
-
|
|
544
|
+
get retryCount() {
|
|
564
545
|
return this._retryCount;
|
|
565
546
|
}
|
|
566
547
|
|
|
567
|
-
|
|
548
|
+
get previousTestResultId() {
|
|
568
549
|
return this._previousTestResultId;
|
|
569
550
|
}
|
|
570
551
|
|
|
571
552
|
isAllowReportTestResultRetries() {
|
|
572
|
-
return Boolean(
|
|
553
|
+
return Boolean(this._options.company?.activePlan?.premiumFeatures?.allowReportTestResultRetries);
|
|
573
554
|
}
|
|
574
555
|
|
|
575
556
|
async onRetry() {
|
|
576
|
-
this._previousTestResultId = this.
|
|
557
|
+
this._previousTestResultId = this.testResultId;
|
|
577
558
|
|
|
578
559
|
if (!this.isAllowReportTestResultRetries()) {
|
|
579
560
|
return;
|