@testim/testim-cli 3.267.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/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 Promise = require('bluebird');
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 {*} test
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
- getTestStatus() {
65
+ get testStatus() {
66
66
  return this._testStatus;
67
67
  }
68
68
 
69
- getRunMode() {
69
+ get runMode() {
70
70
  return this._options.mode;
71
71
  }
72
72
 
73
- getAutomationMode() {
73
+ get automationMode() {
74
74
  return this._code ? 'codeful' : 'codeless';
75
75
  }
76
76
 
77
- getCode() {
77
+ get code() {
78
78
  return this._code;
79
79
  }
80
80
 
81
- getRunConfig() {
81
+ get runConfig() {
82
82
  return this._runConfig;
83
83
  }
84
84
 
85
- getTestResultId() {
85
+ get testResultId() {
86
86
  return this._testResultId;
87
87
  }
88
88
 
89
- getBaseUrl() {
89
+ get baseUrl() {
90
90
  return this._baseUrl;
91
91
  }
92
92
 
93
- getExecutionId() {
93
+ get executionId() {
94
94
  return this._executionId;
95
95
  }
96
96
 
97
- getExecutionName() {
97
+ get executionName() {
98
98
  return this._executionName;
99
99
  }
100
100
 
101
- getNativeAppData() {
102
- if (this._nativeApp && !this._options.baseUrl) {
103
- return this._nativeApp;
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
- const url = this._options.baseUrl || this.getBaseUrl();
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
- getBranch() {
120
+ get branch() {
119
121
  return this._branch;
120
122
  }
121
123
 
122
- getSfdcCredential() {
124
+ get sfdcCredential() {
123
125
  return this._options.sfdcCredential;
124
126
  }
125
127
 
126
- getRemoteRunId() {
128
+ get remoteRunId() {
127
129
  return this._remoteRunId;
128
130
  }
129
131
 
130
- getOverrideTestConfigId() {
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.getRemoteRunId(),
152
- previousTestResultId: this.getPreviousTestResultId(),
153
- testRetryCount: this.getRetryCount(),
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.getRunData();
181
+ const runData = this.runData;
195
182
  runRequestParams.lightweightMode.isRunDataSentInUrl = canSendRunDataOverUrl(runData);
196
183
  const stringifiedLength = JSON.stringify(runData).length;
197
- const testId = this.getTestId();
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.getTestId()}) run URL length: ${url.length}`);
205
+ logger.info(`Test (${this.testId}) run URL length: ${url.length}`);
231
206
  return url;
232
207
  }
233
208
 
234
- setSessionId(sessionId) {
209
+ set sessionId(sessionId) {
235
210
  this._sessionId = sessionId;
236
211
  }
237
212
 
238
- getSessionId() {
213
+ get sessionId() {
239
214
  return this._sessionId;
240
215
  }
241
216
 
242
- getTestId() {
217
+ get testId() {
243
218
  return this._testId;
244
219
  }
245
220
 
246
- getTestName() {
221
+ get testName() {
247
222
  return this._testName;
248
223
  }
249
224
 
250
- getRunParams() {
225
+ get runParams() {
251
226
  return this._options.runParams[this._testResultId] || {};
252
227
  }
253
228
 
254
- getRunData() {
229
+ get runData() {
255
230
  return {
256
- userParamsData: this.getRunParams(),
231
+ userParamsData: this.runParams,
257
232
  overrideTestConfigId: this._overrideTestConfigId || null,
258
233
  };
259
234
  }
260
235
 
261
- clearTestResult() {
262
- const runData = this.getRunData();
263
- if (this.getRunMode() === CLI_MODE.EXTENSION) {
264
- runData.code = this.getCode();
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 Promise.resolve();
248
+ return undefined;
274
249
  }
275
250
 
276
- this.clearTestResultFinished = testimServicesApi.uploadRunDataArtifact(this._options.project, this._testId, this._testResultId, runData)
277
- .catch(err => {
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
- .then(async (runDataUrl) => {
282
- // make sure the execution is created by now
283
- await this._testRunStatus.waitForExecutionStartedFinished();
284
- // we probably can save this backend call by initializing the execution
285
- return testimServicesApi.clearTestResult(this._options.project, this._testResultId, this._testId, {
286
- name: this._testName,
287
- resultId: this._testResultId,
288
- status: 'pending',
289
- retryCount: this._retryCount,
290
- runDataUrl, // links the run data url to the test.
291
- runData: runDataUrl ? undefined : runData, // put runData in mongo if we fail to upload to S3.
292
- testRetryKey: this.getRetryKey(),
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
- getRetryKey() {
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.getRetryKey());
338
+ return testResult.testRetryKey && (testResult.testRetryKey !== this.retryKey);
359
339
  }
360
340
 
361
341
  validateRunConfig() {
362
- const baseUrl = this.getBaseUrl();
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.getRetryKey()}`, {
392
- resultId: this.getTestResultId(),
393
- testId: this.getTestId(),
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._testResultId;
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.getTestResultId(),
403
- testId: this.getTestId(),
404
- testRetryKey: this.getRetryKey(),
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._testId;
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
- testimServicesApi.getTestResults(testId, resultId, projectId, branch).then(restResult => {
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
- }).catch((err) => {
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._testId;
442
- const resultId = this._testResultId;
443
- const projectId = this._options.project;
444
- const branch = this.getBranch();
445
- return testimServicesApi.getTestResults(testId, resultId, projectId, branch)
446
- .then(testResult => {
447
- const expectedStatuses = ['running', 'completed'];
448
- if (expectedStatuses.includes(testResult.status)) {
449
- logger.info(`get status: ${testResult.status} after not get test started status`, { testId, resultId, branch });
450
- return testResult;
451
- }
452
- logger.error(`test not start test status: ${testResult.status} (expected [${expectedStatuses.join(', ')}])`, { testId, resultId, branch });
453
- throw new Error(timeoutMessages.TEST_START_TIMEOUT_MSG);
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 (!this._options.disableSockets) {
463
- return Promise.resolve(testResultService.leaveTestResult(this._testResultId, this._testId));
435
+ async onCompletedCleanup() {
436
+ if (this._options.disableSockets) {
437
+ return undefined;
464
438
  }
465
- return Promise.resolve();
439
+ return await testResultService.leaveTestResult(this._testResultId, this._testId);
466
440
  }
467
441
 
468
- onCompleted() {
442
+ async onCompleted() {
469
443
  let onConnected;
470
- return new Promise(resolve => {
471
- if (this._wasCompletedOnStartedCheck && !this.isRetryKeyMismatch(this._wasCompletedOnStartedCheck)) {
472
- logger.info('test was already completed in on started check', {
473
- resultId: this.getTestResultId(),
474
- testId: this.getTestId(),
475
- });
476
- resolve(this._wasCompletedOnStartedCheck);
477
- return;
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
- if (!this._options.disableSockets) {
481
- testResultService.listenToTestResult(this._testResultId, this._testId, testResult => {
482
- if (this.isRetryKeyMismatch(testResult)) {
483
- logger.warn(`ignoring result update for on completed due to retry key mismatch, got ${testResult.testRetryKey}, current is ${this.getRetryKey()}`, {
484
- resultId: this.getTestResultId(),
485
- testId: this.getTestId(),
486
- });
487
- return;
488
- }
489
- if (testResult.status === 'completed') {
490
- testResult.resultId = this._testResultId;
491
- resolve(testResult);
492
- }
493
- });
494
- }
495
- const debounceDelay = this._options.disableSockets ? 0 : Math.floor(10000 + (Math.random() * 5000));
496
- const maxWait = this._options.disableSockets ? 0 : Math.floor(60000 + (Math.random() * 15000));
497
- onConnected = _.debounce(() => testimServicesApi.getTestResults(this._testId, this._testResultId, this._options.project, this.getBranch())
498
- .then(testResult => {
499
- if (this.isRetryKeyMismatch(testResult)) {
500
- logger.warn(`ignoring result update for on completed (in reconnect) due to retry key mismatch, got ${testResult.testRetryKey}, current is ${this.getRetryKey()}`, {
501
- resultId: this.getTestResultId(),
502
- testId: this.getTestId(),
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
- if (testResult && testResult.status === 'completed') {
507
- logger.info('Socket reconnected - Test complete', { testId: this._testId, resultId: this._testResultId, projectId: this._options.project });
508
- testResult.resultId = this._testResultId;
509
- resolve(testResult);
510
- return true;
511
- }
512
- return false;
513
- })
514
- .catch(err => logger.warn('Error while trying to check status on socket connect', err)), debounceDelay, { maxWait });
515
- if (!this._options.disableSockets) {
516
- testResultService.on('socket-connected', onConnected);
517
- } else {
518
- const waitForTestEnd = () => {
519
- setTimeout(async () => {
520
- try {
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
- } else {
510
+ } catch (err) {
511
+ logger.error('failed to check is complete', { err });
529
512
  waitForTestEnd();
530
513
  }
531
- } catch (err) {
532
- logger.error('failed to check is complete', { err });
533
- waitForTestEnd();
534
- }
535
- }, 3000);
536
- };
537
- waitForTestEnd();
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._testResultId, step => {
549
- remoteStepPlayback.executeStep(this._options, browser, step, this._testResultId);
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
- getRetryCount() {
544
+ get retryCount() {
564
545
  return this._retryCount;
565
546
  }
566
547
 
567
- getPreviousTestResultId() {
548
+ get previousTestResultId() {
568
549
  return this._previousTestResultId;
569
550
  }
570
551
 
571
552
  isAllowReportTestResultRetries() {
572
- return Boolean(_(this._options).get('company.activePlan.premiumFeatures.allowReportTestResultRetries'));
553
+ return Boolean(this._options.company?.activePlan?.premiumFeatures?.allowReportTestResultRetries);
573
554
  }
574
555
 
575
556
  async onRetry() {
576
- this._previousTestResultId = this._testResultId;
557
+ this._previousTestResultId = this.testResultId;
577
558
 
578
559
  if (!this.isAllowReportTestResultRetries()) {
579
560
  return;