@testim/testim-cli 3.234.0 → 3.237.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.
@@ -8,7 +8,6 @@ const config = require('../commons/config');
8
8
  const ExecutionQueue = require('../executionQueue');
9
9
 
10
10
  const testimCustomToken = require('../commons/testimCustomToken');
11
- const testimServicesApi = require('../commons/testimServicesApi');
12
11
  const labFeaturesService = require('../services/labFeaturesService');
13
12
  const perf = require('../commons/performance-logger');
14
13
  const { StopRunOnError } = require('../errors');
@@ -51,6 +50,8 @@ class ParallelWorkerManager {
51
50
  return undefined;
52
51
  }
53
52
 
53
+ let stoppedOnError = false;
54
+ let runningTests = 0;
54
55
  const runAndWaitToComplete = token => new Promise((resolve, reject) => {
55
56
  const projectId = options.project;
56
57
  const executionQueue = new ExecutionQueue(executionId, executionName, testList, options, branchToUse, testStatus);
@@ -69,6 +70,7 @@ class ParallelWorkerManager {
69
70
  const sessionType = utils.getSessionType(options);
70
71
 
71
72
  const onTestStarted = (wid, testId, resultId, isRerun, testRetryKey) => {
73
+ runningTests++;
72
74
  analyticsService.analyticsTestStart({
73
75
  authData,
74
76
  executionId,
@@ -151,11 +153,12 @@ class ParallelWorkerManager {
151
153
  isStartUp,
152
154
  });
153
155
  if (stopOnError && !testResult.success) {
154
- reject(new StopRunOnError());
155
- throw new StopRunOnError();
156
+ executionQueue.stop();
157
+ stoppedOnError = true;
156
158
  }
159
+ runningTests--;
157
160
  const completedTests = Object.keys(combinedTestResults).length;
158
- if (completedTests === testCount) {
161
+ if (completedTests === testCount || (stopOnError && runningTests === 0)) {
159
162
  resolve(combinedTestResults);
160
163
  return undefined;
161
164
  }
@@ -165,8 +168,9 @@ class ParallelWorkerManager {
165
168
  const onTestIgnored = (wid, testResult) => {
166
169
  combinedTestResults[testResult.resultId] = testResult;
167
170
  testStatus.onTestIgnored(wid, testResult.resultId);
171
+ runningTests--;
168
172
  const completedTests = Object.keys(combinedTestResults).length;
169
- if (completedTests === testCount) {
173
+ if (completedTests === testCount || (stopOnError && runningTests === 0)) {
170
174
  resolve(combinedTestResults);
171
175
  }
172
176
  };
@@ -196,7 +200,11 @@ class ParallelWorkerManager {
196
200
 
197
201
  try {
198
202
  const token = await testimCustomToken.getCustomTokenV3();
199
- return await runAndWaitToComplete(token);
203
+ const result = await runAndWaitToComplete(token);
204
+ if (stoppedOnError) {
205
+ throw new StopRunOnError();
206
+ }
207
+ return result;
200
208
  } catch (err) {
201
209
  logger.error('failed running parallel workers', { executionId, err });
202
210
  throw err;
@@ -20,7 +20,6 @@ const { getSuite, calcTestResultStatus, validateConfig } = require('./runnerUtil
20
20
  const { StopRunOnError, ArgError } = require('../errors');
21
21
  const Logger = require('../commons/logger');
22
22
  const perf = require('../commons/performance-logger');
23
- const featureFlags = require('../commons/featureFlags');
24
23
 
25
24
  const guid = utils.guid;
26
25
  const logger = Logger.getLogger('test-plan-runner');
@@ -35,14 +34,14 @@ class TestPlanRunner {
35
34
  const executionResults = {};
36
35
  const authData = testimCustomToken.getTokenV3UserData();
37
36
 
38
- const runBeforeTests = (beforeTests, testStatus, executionId, executionName, tpOptions, branchToUse, authData) => {
39
- const workerCount = 1;
37
+ const runBeforeTests = () => {
38
+ const workerCount = tpOptions.beforeParallel || 1;
40
39
  const stopOnError = true;
41
40
  return this.workerManager.runTests(beforeTests, testStatus, executionId, executionName, tpOptions, branchToUse, authData, workerCount, stopOnError)
42
41
  .then(beforeTestsResults => Object.assign(executionResults, beforeTestsResults));
43
42
  };
44
43
 
45
- const runTestPlanTests = (tests, testStatus, executionId, executionName, tpOptions, branchToUse, authData) => {
44
+ const runTestPlanTests = () => {
46
45
  const workerCount = config.TESTIM_CONCURRENT_WORKER_COUNT || tpOptions.parallel;
47
46
  const stopOnError = false;
48
47
  perf.log('right before this.workerManager.runTests');
@@ -51,30 +50,30 @@ class TestPlanRunner {
51
50
  .then(testsResults => Object.assign(executionResults, testsResults));
52
51
  };
53
52
 
54
- const runAfterTests = (afterTests, testStatus, executionId, executionName, tpOptions, branchToUse, authData) => {
55
- const workerCount = 1;
53
+ const runAfterTests = () => {
54
+ const workerCount = tpOptions.afterParallel || 1;
56
55
  const stopOnError = false;
57
56
  return this.workerManager.runTests(afterTests, testStatus, executionId, executionName, tpOptions, branchToUse, authData, workerCount, stopOnError)
58
57
  .then(afterTestsResults => Object.assign(executionResults, afterTestsResults));
59
58
  };
60
59
 
61
- function catchBeforeTestsFailed(executionId) {
60
+ function catchBeforeTestsFailed() {
62
61
  return testStatus.markAllQueuedTests(executionId, constants.runnerTestStatus.ABORTED, 'aborted', false);
63
62
  }
64
63
 
65
64
  const sessionType = utils.getSessionType(tpOptions);
66
65
  analyticsService.analyticsExecsStart({ authData, executionId, projectId: tpOptions.project, sessionType });
67
66
  perf.log('right before runBeforeTests');
68
- return runBeforeTests(beforeTests, testStatus, executionId, executionName, tpOptions, branchToUse, authData)
67
+ return runBeforeTests()
69
68
  .log('right before runTestPlanTests')
70
- .then(() => runTestPlanTests(tests, testStatus, executionId, executionName, tpOptions, branchToUse, authData))
69
+ .then(() => runTestPlanTests())
71
70
  .log('right after runTestPlanTests')
72
- .then(() => runAfterTests(afterTests, testStatus, executionId, executionName, tpOptions, branchToUse, authData))
71
+ .then(() => runAfterTests())
73
72
  .then(() => executionResults)
74
73
  .catch(err => {
75
74
  logger.error('error running test plan', { err });
76
75
  if (err instanceof StopRunOnError) {
77
- return catchBeforeTestsFailed(executionId);
76
+ return catchBeforeTestsFailed();
78
77
  }
79
78
  throw err;
80
79
  })
@@ -1,13 +1,12 @@
1
1
  const os = require('os');
2
- const { spawn } = require('child_process');
2
+ const childProcess = require('child_process');
3
3
  const Promise = require('bluebird');
4
4
  const fse = require('fs-extra');
5
5
  const portfinder = require('portfinder');
6
6
  const ms = require('ms');
7
7
 
8
- const {
9
- downloadAndSave, unzipFile, guid, runWithRetries, isURL,
10
- } = require('../utils');
8
+ const { guid, isURL } = require('../utils');
9
+ const utils = require('../utils');
11
10
  const { gridTypes, CLI_MODE } = require('../commons/constants');
12
11
  const httpRequest = require('../commons/httpRequest');
13
12
  const { ArgError } = require('../errors');
@@ -86,8 +85,8 @@ class LambdatestService {
86
85
  throw new Error(`tunnel on ${os.platform() + os.arch()} platform is not supported.`);
87
86
  }
88
87
  const zipLocation = `${LT_TUNNEL_BINARY_DIRECTORY}.zip`;
89
- await downloadAndSave(`${LT_TUNNEL_BINARY_ORIGIN}/${downloadUrl}`, zipLocation);
90
- await unzipFile(zipLocation, LT_TUNNEL_BINARY_DIRECTORY);
88
+ await utils.downloadAndSave(`${LT_TUNNEL_BINARY_ORIGIN}/${downloadUrl}`, zipLocation);
89
+ await utils.unzipFile(zipLocation, LT_TUNNEL_BINARY_DIRECTORY);
91
90
  }
92
91
 
93
92
  static async connectTunnel(runnerOptions) {
@@ -136,7 +135,11 @@ class LambdatestService {
136
135
  }
137
136
  }
138
137
 
139
- LambdatestService.tunnel = spawn('./LT', tunnelArgs, { cwd: LT_TUNNEL_BINARY_DIRECTORY });
138
+ if (runnerOptions.externalLambdatestMitm) {
139
+ tunnelArgs = [...tunnelArgs, '--mitm'];
140
+ }
141
+
142
+ LambdatestService.tunnel = childProcess.spawn('./LT', tunnelArgs, { cwd: LT_TUNNEL_BINARY_DIRECTORY });
140
143
 
141
144
  let stdoutResult = '';
142
145
  let stderrResult = '';
@@ -151,7 +154,7 @@ class LambdatestService {
151
154
 
152
155
  // verify that LT tunnel strated successfully
153
156
  try {
154
- const ltInfo = await runWithRetries(() => httpRequest.get(`http://127.0.0.1:${infoAPIPort}/api/v1.0/info`, {}, {}, undefined, { skipProxy: true }), 30, 2000);
157
+ const ltInfo = await utils.runWithRetries(() => httpRequest.get(`http://127.0.0.1:${infoAPIPort}/api/v1.0/info`, {}, {}, undefined, { skipProxy: true }), 30, 2000);
155
158
  logger.info('LT tunnel info', ltInfo);
156
159
  } catch (err) {
157
160
  logger.error('Failed to start LT tunnel', { err, stdoutResult, stderrResult });
@@ -166,7 +169,7 @@ class LambdatestService {
166
169
  return new Promise((resolve, reject) => {
167
170
  LambdatestService.tunnel.on('close', (code) => {
168
171
  if (code) {
169
- reject();
172
+ reject(new Error(`tunnel process exited with code ${code}`));
170
173
  }
171
174
  resolve();
172
175
  });
@@ -207,6 +210,7 @@ class LambdatestService {
207
210
  build: executionId,
208
211
  name: `${testResultId} - ${testName}`,
209
212
  platform: LambdatestService.lambdatestConfig.PLATFORM,
213
+ // eslint-disable-next-line camelcase
210
214
  selenium_version: LambdatestService.lambdatestConfig.SELENIUM_VERSION,
211
215
  resolution: LambdatestService.lambdatestConfig.RESOLUTION,
212
216
  timezone: LambdatestService.lambdatestConfig.TIMEZONE,
@@ -1,11 +1,38 @@
1
1
  const { expect, sinon } = require('../../test/utils/testUtils');
2
2
  const servicesApi = require('../commons/testimServicesApi');
3
+ const httpRequest = require('../commons/httpRequest');
3
4
  const LambdatestService = require('./lambdatestService');
5
+ const utils = require('../utils');
6
+ const fse = require('fs-extra');
7
+ const os = require('os');
8
+ const childProcess = require('child_process');
9
+ const EventEmitter = require('events');
10
+ const portfinder = require('portfinder');
11
+ const { getExtensionsUrl } = require('../runOptionsUtils');
12
+
13
+ class Process extends EventEmitter {
14
+ constructor() {
15
+ super();
16
+ this.stdout = new EventEmitter();
17
+ this.stderr = new EventEmitter();
18
+ }
19
+ setCode(code) {
20
+ this.code = code;
21
+ }
22
+ kill() {
23
+ this.emit('close', this.code);
24
+ }
25
+ }
4
26
 
5
27
  describe('LambdatestService', () => {
28
+ let sandbox;
6
29
  let lambdatestService;
7
30
  beforeEach(() => {
8
31
  lambdatestService = new LambdatestService();
32
+ sandbox = sinon.createSandbox();
33
+ });
34
+ afterEach(() => {
35
+ sandbox.restore();
9
36
  });
10
37
 
11
38
  describe('isLambdatestGrid', () => {
@@ -86,8 +113,240 @@ describe('LambdatestService', () => {
86
113
  lambdatestService.isActive = false;
87
114
  expect(lambdatestService.getSessionRetries).to.be.equal(null);
88
115
  });
116
+
117
+ it('should not return lt special selenium capabilities when inactove', () => {
118
+ lambdatestService.isActive = false;
119
+ expect(lambdatestService.getCapabilities({})).to.eql({});
120
+ });
121
+
122
+ it('should return lt selenium capabilities', () => {
123
+ const browser = utils.guid();
124
+ const executionId = utils.guid();
125
+ const testResultId = utils.guid();
126
+ const testName = utils.guid();
127
+
128
+ lambdatestService.isActive = true;
129
+ LambdatestService.lambdatestConfig = {
130
+ CAPABILITIES: { [browser]: { specificBrowserCaps: 123 } },
131
+ };
132
+
133
+ expect(lambdatestService.getCapabilities({}, browser, executionId, testResultId, testName)).to.shallowDeepEqual({
134
+ build: executionId,
135
+ name: `${testResultId} - ${testName}`,
136
+ platform: LambdatestService.lambdatestConfig.PLATFORM,
137
+ // eslint-disable-next-line camelcase
138
+ selenium_version: LambdatestService.lambdatestConfig.SELENIUM_VERSION,
139
+ resolution: LambdatestService.lambdatestConfig.RESOLUTION,
140
+ timezone: LambdatestService.lambdatestConfig.TIMEZONE,
141
+ specificBrowserCaps: 123,
142
+ console: true,
143
+ queueTimeout: 300,
144
+ });
145
+ });
146
+
147
+ it('should return lt tunnel name as part of selenium capabilities', () => {
148
+ lambdatestService.isActive = true;
149
+ LambdatestService.lambdatestConfig = { CAPABILITIES: {} };
150
+ LambdatestService.tunnelName = utils.guid();
151
+
152
+ expect(lambdatestService.getCapabilities({})).to.shallowDeepEqual({
153
+ tunnel: true,
154
+ tunnelName: LambdatestService.tunnelName,
155
+ });
156
+ });
157
+
158
+ it('should load testim extension when it is not set', () => {
159
+ lambdatestService.isActive = true;
160
+ LambdatestService.lambdatestConfig = { CAPABILITIES: {} };
161
+ LambdatestService.tunnelName = utils.guid();
162
+
163
+ expect(lambdatestService.getCapabilities({ mode: 'extension' }, 'chrome')).to.shallowDeepEqual({ loadExtension: [getExtensionsUrl({}, true).chrome] });
164
+ expect(lambdatestService.getCapabilities({ mode: 'extension' }, 'somOtherBrowser')).to.shallowDeepEqual({ loadExtension: [] });
165
+ });
166
+
167
+ it('should load testim extension when passing extensionPath flag', () => {
168
+ lambdatestService.isActive = true;
169
+ LambdatestService.lambdatestConfig = { CAPABILITIES: {} };
170
+ LambdatestService.tunnelName = utils.guid();
171
+
172
+ const extensionPath = 'http://localhost:1234/extension.zip';
173
+ expect(lambdatestService.getCapabilities({ mode: 'extension', extensionPath })).to.shallowDeepEqual({ loadExtension: [extensionPath] });
174
+ });
175
+
176
+ it('should load testim extension when passing installCustomExtension flag', () => {
177
+ lambdatestService.isActive = true;
178
+ LambdatestService.lambdatestConfig = { CAPABILITIES: {} };
179
+ LambdatestService.tunnelName = utils.guid();
180
+
181
+ const installCustomExtension = 'http://localhost:1234/extension.zip';
182
+ expect(lambdatestService.getCapabilities({ mode: 'extension', installCustomExtension })).to.shallowDeepEqual({ loadExtension: [installCustomExtension] });
183
+ });
184
+ });
185
+
186
+ describe('prepareTunnel', () => {
187
+ it('should do nothing when tunnel binary already exists', async () => {
188
+ sandbox.stub(fse, 'pathExists').resolves(true);
189
+ const chmod = sandbox.stub(fse, 'chmodSync');
190
+ await LambdatestService.prepareTunnel();
191
+ expect(chmod).not.to.have.been.called;
192
+ });
193
+
194
+ it('should throw when unsupported os', async () => {
195
+ sandbox.stub(fse, 'pathExists').resolves(false);
196
+ sandbox.stub(os, 'platform').returns('wtf');
197
+ sandbox.stub(os, 'arch').returns('wtf');
198
+
199
+ await expect(LambdatestService.prepareTunnel()).to.be.rejectedWith(Error, 'tunnel on wtfwtf platform is not supported.');
200
+ });
201
+
202
+ it('should download and extract tunnel binary', async () => {
203
+ sandbox.stub(fse, 'pathExists').resolves(false);
204
+ sandbox.stub(os, 'platform').returns('win32');
205
+ sandbox.stub(os, 'arch').returns('x64');
206
+
207
+ const download = sandbox.stub(utils, 'downloadAndSave');
208
+ const unzip = sandbox.stub(utils, 'unzipFile').resolves();
209
+
210
+ await LambdatestService.prepareTunnel();
211
+ sinon.assert.calledOnce(unzip);
212
+ sinon.assert.calledOnce(download);
213
+ expect(download.args[0][0]).to.startWith('https://downloads.lambdatest.com/tunnel/');
214
+ });
89
215
  });
90
216
 
91
217
  describe('connectTunnel', () => {
218
+ let prepareTunnelStub;
219
+ let spawnStub;
220
+ let credentials;
221
+ let httpGetStub;
222
+ let processMock;
223
+
224
+ beforeEach(() => {
225
+ processMock = new Process();
226
+ credentials = { gridUsername: utils.guid(), gridPassword: utils.guid() };
227
+ sandbox.stub(portfinder, 'getPortPromise').resolves(1234);
228
+ prepareTunnelStub = sandbox.stub(LambdatestService, 'prepareTunnel');
229
+ spawnStub = sandbox.stub(childProcess, 'spawn').returns(processMock);
230
+ httpGetStub = sandbox.stub(httpRequest, 'get').resolves({});
231
+ });
232
+
233
+ it('should do nothing when using externalLambdatestTunnelId', async () => {
234
+ await LambdatestService.connectTunnel({ externalLambdatestTunnelId: 123 });
235
+ sinon.assert.neverCalledWith(prepareTunnelStub);
236
+ sinon.assert.neverCalledWith(spawnStub);
237
+ });
238
+
239
+ it('should prepare the tunnel', async () => {
240
+ await LambdatestService.connectTunnel({ ...credentials });
241
+ sinon.assert.calledOnce(prepareTunnelStub);
242
+ });
243
+
244
+ it('should reject when no credentials', async () => {
245
+ await expect(LambdatestService.connectTunnel({ })).to.be.rejectedWith(Error, 'tunnel requires username and password');
246
+ });
247
+
248
+ it('should spawn the tunnel process', async () => {
249
+ await LambdatestService.connectTunnel({ ...credentials });
250
+ sinon.assert.calledOnce(spawnStub);
251
+ expect(spawnStub.args[0][1]).to.eql([
252
+ '--tunnelName', LambdatestService.tunnelName, '--infoAPIPort', 1234,
253
+ '--user', credentials.gridUsername, '--key', credentials.gridPassword,
254
+ ]);
255
+ });
256
+
257
+ it('should accept tunnelUser and tunnelKey on gridData', async () => {
258
+ credentials = { gridData: { tunnelUser: utils.guid(), tunnelKey: utils.guid() } };
259
+ await LambdatestService.connectTunnel({ ...credentials });
260
+ sinon.assert.calledOnce(spawnStub);
261
+ expect(spawnStub.args[0][1]).to.eql([
262
+ '--tunnelName', LambdatestService.tunnelName, '--infoAPIPort', 1234,
263
+ '--user', credentials.gridData.tunnelUser, '--key', credentials.gridData.tunnelKey,
264
+ ]);
265
+ });
266
+
267
+ it('should allow using externalLambdatestUseWss', async () => {
268
+ await LambdatestService.connectTunnel({ ...credentials, externalLambdatestUseWss: true });
269
+ sinon.assert.calledOnce(spawnStub);
270
+ expect(spawnStub.args[0][1].join(' ')).to.contain('--mode ws');
271
+ });
272
+
273
+ it('should allow using proxyUri', async () => {
274
+ global.proxyUri = 'http://localhost';
275
+ await LambdatestService.connectTunnel({ ...credentials });
276
+ sinon.assert.calledOnce(spawnStub);
277
+ expect(spawnStub.args[0][1].join(' ')).to.contain('--proxy-host localhost');
278
+ global.proxyUri = undefined;
279
+ });
280
+
281
+ it('should allow using proxyUri port and credentials', async () => {
282
+ global.proxyUri = 'http://user:pass@localhost:1234';
283
+ await LambdatestService.connectTunnel({ ...credentials });
284
+ sinon.assert.calledOnce(spawnStub);
285
+ expect(spawnStub.args[0][1].join(' ')).to.contain('--proxy-host localhost');
286
+ expect(spawnStub.args[0][1].join(' ')).to.contain('--proxy-port 1234');
287
+ expect(spawnStub.args[0][1].join(' ')).to.contain('--proxy-user user');
288
+ expect(spawnStub.args[0][1].join(' ')).to.contain('--proxy-pass pass');
289
+ global.proxyUri = undefined;
290
+ });
291
+
292
+ it('should throw when proxyUri is invalid', async () => {
293
+ global.proxyUri = 'i am invalid';
294
+ await expect(LambdatestService.connectTunnel({ ...credentials })).to.be.rejectedWith(Error, 'proxy url is invalid');
295
+ global.proxyUri = undefined;
296
+ });
297
+
298
+ it('should allow using externalLambdatestDisableAutomationTunneling', async () => {
299
+ await LambdatestService.connectTunnel({ ...credentials, externalLambdatestDisableAutomationTunneling: true });
300
+ sinon.assert.calledOnce(spawnStub);
301
+ expect(spawnStub.args[0][1].join(' ')).to.contain('--bypassHosts run.testim.io,services.testim.io,api.coralogix.com,conf.rollout.io,statestore.rollout.io,push.rollout.io,analytic.rollout.io,res.cloudinary.com');
302
+ });
303
+
304
+ it('should allow using externalLambdatestMitm', async () => {
305
+ await LambdatestService.connectTunnel({ ...credentials, externalLambdatestMitm: true });
306
+ sinon.assert.calledOnce(spawnStub);
307
+ expect(spawnStub.args[0][1].join(' ')).to.contain('--mitm');
308
+ });
309
+
310
+ it('should verify tunnel started', async () => {
311
+ await LambdatestService.connectTunnel({ ...credentials });
312
+ sinon.assert.calledOnce(httpGetStub);
313
+ });
314
+ it('should throw when tunnel did not start', async () => {
315
+ sandbox.stub(utils, 'runWithRetries').rejects(new Error('tunnel did not start'));
316
+ await expect(LambdatestService.connectTunnel({ ...credentials })).to.be.rejectedWith(Error, 'tunnel did not start');
317
+ processMock.stdout.emit('data', '');
318
+ processMock.stderr.emit('data', '');
319
+ });
320
+ });
321
+
322
+ describe('disconnectTunnel', () => {
323
+ let processMock;
324
+ let killStub;
325
+
326
+ beforeEach(() => {
327
+ processMock = new Process();
328
+ killStub = sandbox.stub(processMock, 'kill').callThrough();
329
+ sandbox.stub(childProcess, 'spawn').returns(processMock);
330
+ sandbox.stub(LambdatestService, 'prepareTunnel');
331
+ sandbox.stub(httpRequest, 'get').resolves({});
332
+ });
333
+
334
+ it('should kill the tunnel', async () => {
335
+ await LambdatestService.connectTunnel({ tunnel: true, company: {}, gridUsername: utils.guid(), gridPassword: utils.guid() });
336
+ await LambdatestService.disconnectTunnel({ company: {} });
337
+ sinon.assert.calledOnce(killStub);
338
+ });
339
+
340
+ it('should reject when killing the tunnel fails', async () => {
341
+ processMock.setCode(1);
342
+ await LambdatestService.connectTunnel({ tunnel: true, company: {}, gridUsername: utils.guid(), gridPassword: utils.guid() });
343
+ await expect(LambdatestService.disconnectTunnel({ company: {} })).to.be.rejectedWith(Error);
344
+ sinon.assert.calledOnce(killStub);
345
+ });
346
+
347
+ it('should do nothing when using externalLambdatestTunnelId', async () => {
348
+ await LambdatestService.disconnectTunnel({ externalLambdatestTunnelId: 123 });
349
+ sinon.assert.neverCalledWith(killStub);
350
+ });
92
351
  });
93
352
  });
package/testRunStatus.js CHANGED
@@ -174,7 +174,21 @@ RunStatus.prototype.testStartReport = function (test, executionId, testRetryKey)
174
174
  }
175
175
  return runHook(this.options.beforeTest, Object.assign({}, test, { exportsGlobal: this.exportsGlobal }), this.options.userData.loginData.token)
176
176
  .then(async params => {
177
- this.options.runParams[test.resultId] = test.config.testData = Object.assign({}, test.config.testData, this.exportsGlobal, this.fileUserParamsData, this.beforeSuiteParams, params);
177
+ // Temporary Sapiens log (SUP-3192)
178
+ if (this.options.projectData && this.options.projectData.projectId === 'fZ63D61PRQQVvvtGY6Ue' && this.options.suites && this.options.suites.includes('Sanity')) {
179
+ logger.info('testRunStatus - testStartReport', {
180
+ 'test.config.testData': test.config.testData,
181
+ 'this.exportsGlobal': this.exportsGlobal,
182
+ 'this.fileUserParamsData': this.fileUserParamsData,
183
+ 'this.beforeSuiteParams': this.beforeSuiteParams,
184
+ params,
185
+ executionId,
186
+ 'test.testId': test.testId,
187
+ 'test.resultId': test.resultId,
188
+ });
189
+ }
190
+ test.config.testData = Object.assign({}, test.config.testData, this.exportsGlobal, this.fileUserParamsData, this.beforeSuiteParams, params);
191
+ this.options.runParams[test.resultId] = test.config.testData;
178
192
  test.startTime = Date.now();
179
193
  await this.updateTestStatusRunning(test, executionId, testRetryKey);
180
194
 
@@ -234,8 +248,10 @@ RunStatus.prototype.testEnd = function (wid, result, executionId, sessionId, isR
234
248
  const duration = (result.endTime - result.startTime) || 0;
235
249
  test.sessionId = sessionId;
236
250
  test.startTime = result.startTime || test.startTime || Date.now();
237
- test.duration = result.duration = duration;
238
- result.failureReason = test.failureReason = result.failureReason || result.reason;
251
+ test.duration = duration;
252
+ result.duration = duration;
253
+ test.failureReason = result.failureReason || result.reason;
254
+ result.failureReason = test.failureReason;
239
255
  test.failurePath = result.failurePath;
240
256
  test.resultId = result.resultId;
241
257
  test.success = result.success;
@@ -245,8 +261,9 @@ RunStatus.prototype.testEnd = function (wid, result, executionId, sessionId, isR
245
261
  }
246
262
 
247
263
  test.resultUrl = utils.getTestUrl(this.options.editorUrl, this.options.project, test.testId, test.resultId, this.branchToUse);
264
+ test.status = this.calcResultText(result);
248
265
 
249
- test.status = result.status = this.calcResultText(result);
266
+ result.status = test.status;
250
267
  result.name = test.name;
251
268
  result.testStatus = test.testStatus;
252
269
  result.testId = result.testId || test.testId;
@@ -263,7 +280,19 @@ RunStatus.prototype.testEnd = function (wid, result, executionId, sessionId, isR
263
280
  const isCodeMode = this.options.files.length > 0;
264
281
  reporter.onTestFinished(test, wid, isRerun, isCodeMode);
265
282
 
266
- this.exportsGlobal = Object.assign({}, this.exportsGlobal, result.exportsGlobal);
283
+ const afterMerge = Object.assign({}, this.exportsGlobal, result.exportsGlobal);
284
+ // Temporary Sapiens log (SUP-3192)
285
+ if (this.options.projectData && this.options.projectData.projectId === 'fZ63D61PRQQVvvtGY6Ue' && this.options.suites && this.options.suites.includes('Sanity')) {
286
+ logger.info('testRunStatus - testEnd', {
287
+ 'this.exportsGlobal': this.exportsGlobal,
288
+ 'result.exportsGlobal': result.exportsGlobal,
289
+ afterMerge,
290
+ executionId,
291
+ 'test.testId': test.testId,
292
+ 'test.resultId': test.resultId,
293
+ });
294
+ }
295
+ this.exportsGlobal = afterMerge;
267
296
  return test;
268
297
  };
269
298