@testim/testim-cli 3.198.0 → 3.202.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testim/testim-cli",
3
- "version": "3.198.0",
3
+ "version": "3.202.0",
4
4
  "description": "Command line interface for running Testing on your CI",
5
5
  "author": "Oren Rubin",
6
6
  "contributors": [{
@@ -36,7 +36,7 @@
36
36
  "typescript": "3.9.3"
37
37
  },
38
38
  "lazyDependencies": {
39
- "ngrok": "3.2.3",
39
+ "ngrok": "3.4.0",
40
40
  "webpack": "4.43.0",
41
41
  "playwright": "0.12.1",
42
42
  "puppeteer": "2.1.1",
@@ -60,8 +60,8 @@
60
60
  "bluebird": "3.7.2",
61
61
  "bluebird-retry": "0.11.0",
62
62
  "body-parser": "1.19.0",
63
- "bunyan": "1.8.15",
64
- "bunyan-prettystream": "0.1.3",
63
+ "winston": "3.3.3",
64
+ "winston-transport": "4.4.0",
65
65
  "chalk": "4.1.0",
66
66
  "chrome-launcher": "0.13.4",
67
67
  "combine-source-map": "0.8.0",
@@ -79,10 +79,10 @@
79
79
  "glob": "7.1.6",
80
80
  "istanbul-lib-report": "3.0.0",
81
81
  "istanbul-reports": "3.0.2",
82
- "jimp": "0.2.28",
82
+ "jimp": "0.16.1",
83
83
  "jsdom": "16.7.0",
84
84
  "jsonwebtoken": "8.5.1",
85
- "lodash": "4.17.20",
85
+ "lodash": "4.17.21",
86
86
  "memory-fs": "0.5.0",
87
87
  "memorystream": "0.3.1",
88
88
  "mkdirp": "1.0.4",
@@ -105,9 +105,9 @@
105
105
  "superagent-proxy": "3.0.0",
106
106
  "test-exclude": "6.0.0",
107
107
  "threads": "0.12.0",
108
- "ua-parser-js": "0.7.22",
108
+ "ua-parser-js": "0.7.28",
109
109
  "validate-npm-package-name": "3.0.0",
110
- "ws": "7.3.0",
110
+ "ws": "8.2.3",
111
111
  "xml2js": "0.4.23",
112
112
  "yaml": "1.10.0"
113
113
  },
@@ -7,9 +7,9 @@ const logger = require('../../commons/logger').getLogger('pixel-validation-step-
7
7
 
8
8
  class PixelValidationStepAction extends StepAction {
9
9
  async performAction() {
10
- const { shouldUseVisualGrid, applitoolsSdkConfig: config } = this.context;
10
+ const { shouldUseVisualGrid, applitoolsSdkConfig: config, testResultId } = this.context;
11
11
  this.runContext = this.context.getRunContext(undefined);
12
- const eyeManager = await eyeSdkService.getManager(shouldUseVisualGrid, this.context.config.applitoolsConcurrency || 5);
12
+ const eyeManager = await eyeSdkService.getManager(shouldUseVisualGrid, this.context.config.applitoolsConcurrency || 5, testResultId);
13
13
  const targetElementData = this.getTarget() || {};
14
14
  try {
15
15
  const openedEye = await eyeManager.openEyes({ driver: this.driver.client, config });
@@ -7,7 +7,7 @@ class SalesforceAutoLoginStepAction extends NavigationStepAction {
7
7
  try {
8
8
  salesforceUrl = await this.updateBaseUrl(salesforceUrl);
9
9
  await this.driver.url(salesforceUrl);
10
- await Promise.delay(1000); // wait a little for the page to load (fixes screenshots and clicking on elements in username verification screen)
10
+ await Promise.delay(5000); // wait a little for the page to load (fixes screenshots and clicking on elements in username verification screen)
11
11
  let newUrl = await this.driver.getUrl();
12
12
  // Verify username screen
13
13
  const isUsernameVerificationNeeded = newUrl.includes(this.step.USERNAME_VERIFICATION_PATH_ID);
@@ -22,7 +22,7 @@ class SalesforceAutoLoginStepAction extends NavigationStepAction {
22
22
  await Promise.delay(5000);
23
23
  newUrl = await this.driver.getUrl(); // If we managed to continue correctly we want to get the new redirected url
24
24
  }
25
- await Promise.delay(700);
25
+ await Promise.delay(1500);
26
26
  return {
27
27
  success: true,
28
28
  newUrl,
@@ -24,7 +24,6 @@ const TdkHybridStepAction = require('./tdkHybridStepAction');
24
24
  const PixelValidationStepAction = require('./pixelValidationStepAction');
25
25
 
26
26
  const SalesforceAutoLoginStepAction = require('./salesforceAutoLoginStepAction');
27
- const SalesforceApexActionStepAction = require('./salesforceApexActionStepAction');
28
27
 
29
28
  const CliJsStepAction = require('./cliJsStepAction');
30
29
  const CliConditionStepAction = require('./cliConditionStepAction');
@@ -87,7 +86,6 @@ module.exports = function (driver, stepActionFactory, runMode) {
87
86
  'tdk-hybrid': TdkHybridStepAction,
88
87
 
89
88
  'salesforce-autologin': SalesforceAutoLoginStepAction,
90
- 'salesforce-apex-action': SalesforceApexActionStepAction,
91
89
  };
92
90
 
93
91
  register(STEP_ACTION_MAPPING, stepActionFactory);
@@ -1,5 +1,9 @@
1
1
  // https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-webdriverio-4/src/spec-driver.ts
2
2
 
3
+ /**
4
+ * @type {{ EyeSdkBuilder: typeof import('../../../../clickim/src/background/eyeSdkBuilder').EyeSdkBuilder }}
5
+ */
6
+ const sessionPlayer = require('../../commons/getSessionPlayerRequire');
3
7
  const { makeSDK } = require('@applitools/eyes-sdk-core');
4
8
  const { W3C_ELEMENT_ID } = require('../constants');
5
9
  const _ = require('lodash');
@@ -222,8 +226,10 @@ class EyeSdkService {
222
226
  VisualGridClient: require('@applitools/visual-grid-client'),
223
227
  });
224
228
  }
225
- getManager(useVisualGrid, concurrency) {
226
- return this.sdk.makeManager({ type: useVisualGrid ? 'vg' : 'classic', concurrency });
229
+ async getManager(useVisualGrid, concurrency, batchId) {
230
+ const manager = await this.sdk.makeManager({ type: useVisualGrid ? 'vg' : 'classic', concurrency });
231
+ sessionPlayer.EyeSdkBuilder.rememberCreatedBatch(batchId);
232
+ return manager;
227
233
  }
228
234
  }
229
235
 
@@ -7,6 +7,8 @@ const constants = require('../commons/constants');
7
7
  const featureAvailabilityService = require('../commons/featureAvailabilityService');
8
8
  const { getAbortedTests, getFailedTests, getPassedTests, getFailureEvaluatingCount, getSkippedCount } = require('./reporterUtils');
9
9
 
10
+ const colorize = { success: chalk.green, warn: chalk.yellow, error: chalk.red };
11
+
10
12
  class ConsoleReporter {
11
13
  constructor(options, branchToUse) {
12
14
  this.options = options;
@@ -16,12 +18,8 @@ class ConsoleReporter {
16
18
  this.branchToUse = branchToUse;
17
19
  }
18
20
 
19
- printWorkerMessage(workerId, message) {
20
- if (this.config.showWorkerNames) {
21
- console.log(`W:${workerId} ${message}`);
22
- } else {
23
- console.log(message);
24
- }
21
+ toWorkerIdPrefix(workerId) {
22
+ return this.config.showWorkerNames ? `W:${workerId}` : '';
25
23
  }
26
24
 
27
25
  printWorkerDivider() {
@@ -32,7 +30,7 @@ class ConsoleReporter {
32
30
  const type = isCodeMode ? 'File' : 'Test';
33
31
  const testIdLabel = test.isTestsContainer ? '' : `(${test.testId})`;
34
32
  const testUrlLabel = test.isTestsContainer ? '' : `url: ${chalk.underline(utils.getTestUrl(this.options.editorUrl, this.options.project, test.testId, test.resultId, this.branchToUse))}`;
35
- this.printWorkerMessage(workerId, `${type} "${test.name}" started ${testIdLabel} ${testUrlLabel}`.trim());
33
+ console.log(this.toWorkerIdPrefix(workerId), `${type} "${test.name}" started ${testIdLabel} ${testUrlLabel}`.trim());
36
34
  }
37
35
 
38
36
  onTestFinished(test, workerId, isRerun, isCodeMode) {
@@ -42,7 +40,9 @@ class ConsoleReporter {
42
40
  }
43
41
  const testStatus = test.success ? constants.runnerTestStatus.PASSED : constants.runnerTestStatus.FAILED;
44
42
  const testIdLabel = test.isTestsContainer ? ' ' : `(${test.testId})`;
45
- this.printWorkerMessage(workerId, `Test "${test.name}" finished status: ${testStatus} ${testIdLabel} duration: ${utils.getDuration(test.duration)}`);
43
+ const color = colorize[test.success ? 'success' : 'error'];
44
+
45
+ console.log(color(this.toWorkerIdPrefix(workerId), `Test "${test.name}" finished status: ${testStatus} ${testIdLabel} duration: ${utils.getDuration(test.duration)}`));
46
46
  }
47
47
 
48
48
  printAllFailedTests(failedTests) {
@@ -54,8 +54,8 @@ class ConsoleReporter {
54
54
  }
55
55
  return `${failedTest.name} : ${testUrl}`;
56
56
  });
57
- console.log('Failed runs are:');
58
- console.log(failedTestStrings.join('\n\r'));
57
+ console.log(colorize.error('Failed runs are:'));
58
+ console.log(colorize.error(failedTestStrings.join('\n\r')));
59
59
  }
60
60
  }
61
61
 
@@ -79,15 +79,20 @@ class ConsoleReporter {
79
79
  }
80
80
 
81
81
  const planName = this.buildTestPlanName(isAnonymous, testPlanName, isCodeMode);
82
+
83
+ let message;
84
+ const color = colorize[failed ? 'error' : 'success'];
85
+
82
86
  if (isCodeMode || planName.trim() === '' || planName.trim() === 'Anonymous') {
83
- this.printWorkerDivider();
84
- console.log(`Tests completed. PASSED: ${passed} FAILED: ${failed}${failedEvaluatingString} ABORTED: ${aborted}${skippedString} Duration: ${utils.getDuration(duration)} (Execution ID: ${executionId})`);
85
- this.printWorkerDivider();
87
+ message = `Tests completed. PASSED: ${passed} FAILED: ${failed}${failedEvaluatingString} ABORTED: ${aborted}${skippedString} Duration: ${utils.getDuration(duration)} (Execution ID: ${executionId})`;
86
88
  } else {
87
- this.printWorkerDivider();
88
- console.log(`Test plan${planName} completed PASSED: ${passed} FAILED: ${failed}${failedEvaluatingString} ABORTED: ${aborted}${skippedString} Duration: ${utils.getDuration(duration)} (${executionId})`);
89
- this.printWorkerDivider();
89
+ message = `Test plan${planName} completed PASSED: ${passed} FAILED: ${failed}${failedEvaluatingString} ABORTED: ${aborted}${skippedString} Duration: ${utils.getDuration(duration)} (${executionId})`;
90
90
  }
91
+
92
+ this.printWorkerDivider();
93
+ console.log(color(message));
94
+ this.printWorkerDivider();
95
+
91
96
  this.printAllFailedTests(failedTests);
92
97
  }
93
98
 
@@ -138,24 +143,24 @@ class ConsoleReporter {
138
143
  onGetSlot(workerId, browser) {
139
144
  const gridNameOrId = this.options.grid || this.options.gridId;
140
145
  if (gridNameOrId) {
141
- this.printWorkerMessage(workerId, `Get ${chalk.underline(browser)} slot from ${chalk.underline(gridNameOrId)}`);
146
+ console.log(this.toWorkerIdPrefix(workerId), `Get ${chalk.underline(browser)} slot from ${chalk.underline(gridNameOrId)}`);
142
147
  }
143
148
  }
144
149
 
145
150
  onGetSession(workerId, testName, mode) {
146
- this.printWorkerMessage(workerId, `Get browser to run ${chalk.underline(testName)}`);
151
+ console.log(this.toWorkerIdPrefix(workerId), `Get browser to run ${chalk.underline(testName)}`);
147
152
  }
148
153
 
149
154
  onWaitToTestStart(workerId) {
150
- this.printWorkerMessage(workerId, 'Wait for test start');
155
+ console.log(this.toWorkerIdPrefix(workerId), 'Wait for test start');
151
156
  }
152
157
 
153
158
  onWaitToTestComplete(workerId, isCodeMode, debuggerAddress) {
154
159
  const type = isCodeMode ? 'file' : 'test';
155
- this.printWorkerMessage(workerId, `Wait for ${type} complete`);
160
+ console.log(this.toWorkerIdPrefix(workerId), `Wait for ${type} complete`);
156
161
  if (debuggerAddress && isCodeMode) {
157
162
  // TODO(Benji) decide with Amitai what we want to do with this
158
- this.printWorkerMessage(workerId, `Chrome Debugger available at ${debuggerAddress}`);
163
+ console.log(this.toWorkerIdPrefix(workerId), `Chrome Debugger available at ${debuggerAddress}`);
159
164
  }
160
165
  }
161
166
 
@@ -166,11 +171,11 @@ class ConsoleReporter {
166
171
  // heuristic, show the message on the same attempt
167
172
  const gridNameOrId = this.options.grid || this.options.gridId;
168
173
  if (gridNameOrId) { // if the user passes a grid or a gridId - show those
169
- this.printWorkerMessage(workerId, `It is taking us some time to get a browser from the grid ${gridNameOrId}`);
174
+ console.log(colorize.warn(this.toWorkerIdPrefix(workerId), `It is taking us some time to get a browser from the grid ${gridNameOrId}`));
170
175
  } else if (this.options.usingLocalChromeDriver) {
171
- this.printWorkerMessage(workerId, 'We are having issues starting ChromeDriver for you locally');
176
+ console.log(colorize.warn(this.toWorkerIdPrefix(workerId), 'We are having issues starting ChromeDriver for you locally'));
172
177
  } else if (this.options.host) {
173
- this.printWorkerMessage(workerId, `We are having issues reaching your Selenium grid at ${this.options.host}:${this.options.port || 4444}`);
178
+ console.log(colorize.warn(this.toWorkerIdPrefix(workerId), `We are having issues reaching your Selenium grid at ${this.options.host}:${this.options.port || 4444}`));
174
179
  } else {
175
180
  // in other cases - print nothing
176
181
  }
@@ -50,9 +50,6 @@ class JunitReporter {
50
50
  }
51
51
 
52
52
  function getPrintName(testResult) {
53
- if (!featureFlags.flags.testNameTestDataInJunitReport.isEnabled()) {
54
- return testResult.name;
55
- }
56
53
  const testData = testResult.testData || {};
57
54
  const testDataNumber = typeof testData.total === 'number' ? ` - ${testData.index} / ${testData.total} Data set` : '';
58
55
  return `${testResult.name}${testDataNumber}`;
package/runner.js CHANGED
@@ -286,16 +286,7 @@ async function init(options) {
286
286
  reporter.setOptions(options, branchToUse);
287
287
  }
288
288
 
289
- function run(options, customExtensionLocalLocation) {
290
- perf.log('in runner.js run');
291
-
292
- if (options.files.length > 0 && !featureFlags.flags.enableTDKRun.isEnabled()) {
293
- throw new ArgError('run command is not supported, please sign up to the Testim Development Kit beta - https://go.testim.io/testims-new-beta-testim-tdk-testim-development-kit-testautomation');
294
- }
295
- return runRunner(options, customExtensionLocalLocation);
296
- }
297
-
298
289
  module.exports = {
299
- run,
290
+ run: runRunner,
300
291
  init: Promise.method(init),
301
292
  };
@@ -46,14 +46,14 @@ class ParallelWorkerManager {
46
46
  return Array.from(new Array(count), createWorker);
47
47
  }
48
48
 
49
- async runTests(testList, testStatus, executionId, options, branchToUse, authData, workerCount, stopOnError) {
49
+ async runTests(testList, testStatus, executionId, executionName, options, branchToUse, authData, workerCount, stopOnError) {
50
50
  if (testList && testList.length === 0) {
51
51
  return undefined;
52
52
  }
53
53
 
54
54
  const runAndWaitToComplete = token => new Promise((resolve, reject) => {
55
55
  const projectId = options.project;
56
- const executionQueue = new ExecutionQueue(executionId, testList, options, branchToUse, testStatus);
56
+ const executionQueue = new ExecutionQueue(executionId, executionName, testList, options, branchToUse, testStatus);
57
57
 
58
58
  const combinedTestResults = {};
59
59
  const testCount = testList.length;
@@ -29,30 +29,30 @@ class TestPlanRunner {
29
29
  this.workerManager = new ParallelWorkerManager(customExtensionLocalLocation);
30
30
  this.startTime = Date.now();
31
31
  }
32
- runTestAllPhases(beforeTests, tests, afterTests, branchToUse, tpOptions, executionId, testStatus) {
32
+ runTestAllPhases(beforeTests, tests, afterTests, branchToUse, tpOptions, executionId, executionName, testStatus) {
33
33
  const executionResults = {};
34
34
  const authData = testimCustomToken.getTokenV3UserData();
35
35
 
36
- const runBeforeTests = (beforeTests, testStatus, executionId, tpOptions, branchToUse, authData) => {
36
+ const runBeforeTests = (beforeTests, testStatus, executionId, executionName, tpOptions, branchToUse, authData) => {
37
37
  const workerCount = 1;
38
38
  const stopOnError = true;
39
- return this.workerManager.runTests(beforeTests, testStatus, executionId, tpOptions, branchToUse, authData, workerCount, stopOnError)
39
+ return this.workerManager.runTests(beforeTests, testStatus, executionId, executionName, tpOptions, branchToUse, authData, workerCount, stopOnError)
40
40
  .then(beforeTestsResults => Object.assign(executionResults, beforeTestsResults));
41
41
  };
42
42
 
43
- const runTestPlanTests = (tests, testStatus, executionId, tpOptions, branchToUse, authData) => {
43
+ const runTestPlanTests = (tests, testStatus, executionId, executionName, tpOptions, branchToUse, authData) => {
44
44
  const workerCount = config.TESTIM_CONCURRENT_WORKER_COUNT || tpOptions.parallel;
45
45
  const stopOnError = false;
46
46
  perf.log('right before this.workerManager.runTests');
47
- return this.workerManager.runTests(tests, testStatus, executionId, tpOptions, branchToUse, authData, workerCount, stopOnError)
47
+ return this.workerManager.runTests(tests, testStatus, executionId, executionName, tpOptions, branchToUse, authData, workerCount, stopOnError)
48
48
  .log('right after this.workerManager.runTests')
49
49
  .then(testsResults => Object.assign(executionResults, testsResults));
50
50
  };
51
51
 
52
- const runAfterTests = (afterTests, testStatus, executionId, tpOptions, branchToUse, authData) => {
52
+ const runAfterTests = (afterTests, testStatus, executionId, executionName, tpOptions, branchToUse, authData) => {
53
53
  const workerCount = 1;
54
54
  const stopOnError = false;
55
- return this.workerManager.runTests(afterTests, testStatus, executionId, tpOptions, branchToUse, authData, workerCount, stopOnError)
55
+ return this.workerManager.runTests(afterTests, testStatus, executionId, executionName, tpOptions, branchToUse, authData, workerCount, stopOnError)
56
56
  .then(afterTestsResults => Object.assign(executionResults, afterTestsResults));
57
57
  };
58
58
 
@@ -63,13 +63,19 @@ class TestPlanRunner {
63
63
  const sessionType = utils.getSessionType(tpOptions);
64
64
  analyticsService.analyticsExecsStart({ authData, executionId, projectId: tpOptions.project, sessionType });
65
65
  perf.log('right before runBeforeTests');
66
- return runBeforeTests(beforeTests, testStatus, executionId, tpOptions, branchToUse, authData)
66
+ return runBeforeTests(beforeTests, testStatus, executionId, executionName, tpOptions, branchToUse, authData)
67
67
  .log('right before runTestPlanTests')
68
- .then(() => runTestPlanTests(tests, testStatus, executionId, tpOptions, branchToUse, authData))
68
+ .then(() => runTestPlanTests(tests, testStatus, executionId, executionName, tpOptions, branchToUse, authData))
69
69
  .log('right after runTestPlanTests')
70
- .then(() => runAfterTests(afterTests, testStatus, executionId, tpOptions, branchToUse, authData))
70
+ .then(() => runAfterTests(afterTests, testStatus, executionId, executionName, tpOptions, branchToUse, authData))
71
71
  .then(() => executionResults)
72
- .catch(StopRunOnError, () => catchBeforeTestsFailed(executionId));
72
+ .catch(err => {
73
+ logger.error('error running test plan', { err });
74
+ if (err instanceof StopRunOnError) {
75
+ return catchBeforeTestsFailed(executionId);
76
+ }
77
+ throw err;
78
+ });
73
79
  }
74
80
 
75
81
  async initRealDataService(projectId) {
@@ -144,10 +150,6 @@ class TestPlanRunner {
144
150
 
145
151
  const isCodeMode = tpOptions.files.length > 0;
146
152
 
147
- if (isCodeMode && tpOptions.mode === constants.CLI_MODE.SELENIUM) {
148
- // in selenium mode we don't need to wait for the runner and clickim to sync, so we don't need to wait for reports.
149
- testStatus.setAsyncReporting(true);
150
- }
151
153
  const testListInfoPromise = tpOptions.lightweightMode && tpOptions.lightweightMode.onlyTestIdsNoSuite ?
152
154
  { beforeTests, tests, afterTests } :
153
155
  testStatus.executionStart(executionId, projectId, this.startTime, testPlanName);
@@ -165,7 +167,7 @@ class TestPlanRunner {
165
167
  }
166
168
 
167
169
  perf.log('before runTestAllPhases');
168
- const results = await this.runTestAllPhases(testListInfo.beforeTests, testListInfo.tests, testListInfo.afterTests, branch, tpOptions, executionId, testStatus);
170
+ const results = await this.runTestAllPhases(testListInfo.beforeTests, testListInfo.tests, testListInfo.afterTests, branch, tpOptions, executionId, testPlanName || 'All Tests', testStatus);
169
171
  const childResults = await Bluebird.resolve(childTestResults)
170
172
  .timeout(TDK_CHILD_RESULTS_TIMEOUT)
171
173
  .catch(async () => {
@@ -50,13 +50,12 @@ function getSerializableObject(grid) {
50
50
  (tunnel && grid.hybrid.external && grid.hybrid.external[grid.hybrid.tunnel] && grid.hybrid.external[grid.hybrid.tunnel].user) : user;
51
51
  const tunnelKey = type === gridTypes.HYBRID ?
52
52
  (tunnel && grid.hybrid.external && grid.hybrid.external[grid.hybrid.tunnel] && grid.hybrid.external[grid.hybrid.tunnel].key) : key;
53
- const arn = (grid && grid.external && grid.external.arn) || (tunnel && grid.hybrid.external && grid.hybrid.external[grid.hybrid.tunnel] && grid.hybrid.external[grid.hybrid.tunnel].arn);
54
53
  const name = grid && grid.name;
55
54
  const gridId = grid && (grid._id || grid.gridId);
56
55
  const provider = grid && grid.provider;
57
56
 
58
57
  return {
59
- host, port, path, protocol, accessToken, slotId, gridId, tunnel, user, key, type, name, arn, provider, tunnelUser, tunnelKey,
58
+ host, port, path, protocol, accessToken, slotId, gridId, tunnel, user, key, type, name, provider, tunnelUser, tunnelKey,
60
59
  };
61
60
  }
62
61
 
@@ -245,10 +244,6 @@ async function getTestPlanGridData(options) {
245
244
  if (testPlanGrids.includes(undefined)) {
246
245
  throw new ArgError('failed to find one of the test plan defined grid');
247
246
  }
248
- const gridTypes = _(testPlanGrids).map(grid => grid.type).uniq().value();
249
- if (gridTypes.includes('testimMobile') && gridTypes.length > 1) {
250
- throw new ArgError('Test plans cannot include two different grid types');
251
- }
252
247
  return getSerializableObject(_.first(testPlanGrids));
253
248
  }
254
249
 
@@ -6,6 +6,7 @@ const perfLogger = require('../commons/performance-logger');
6
6
  const MemoryFS = require('memory-fs');
7
7
  const mfs = new MemoryFS();
8
8
  const AbortController = require("abort-controller");
9
+ const logger = require('../commons/logger').getLogger('hybrid-step-playback');
9
10
 
10
11
  /**
11
12
  * @type {Map<string, import("abort-controller")>}
@@ -119,10 +120,12 @@ module.exports.execute = async function execute(step, context, driver, loginData
119
120
  }
120
121
 
121
122
  return { success: false, shouldRetry: false, reason: 'unknown hybrid format ' + hybridFunction.type };
123
+ } catch (err) {
124
+ logger.log('error running hybrid step', { err });
122
125
  } finally {
123
126
  runningStepsAbortControllersRegistry.delete(context.stepResultId);
124
127
  }
125
- }
128
+ };
126
129
 
127
130
  module.exports.abort = function abort(stepResultId) {
128
131
  const abortController = runningStepsAbortControllersRegistry.get(stepResultId);
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const _ = require('lodash');
3
4
  const sessionPlayerInit = require('../commons/getSessionPlayerRequire');
4
5
  const perfLogger = require('../commons/performance-logger');
5
6
  const { guid } = require('../utils');
package/testRunHandler.js CHANGED
@@ -15,13 +15,13 @@ const utils = require('./utils');
15
15
  const config = require('./commons/config');
16
16
  const analytics = require('./commons/testimAnalytics');
17
17
  const { SeleniumPerfStats } = require('./commons/SeleniumPerfStats');
18
- const featureFlags = require('./commons/featureFlags');
19
18
  const { preloadTests } = require('./commons/preloadTests');
20
19
 
21
20
  const RETRIES_ON_TIMEOUT = 3;
22
21
 
23
- const TestRun = function (executionId, test, options, branchToUse, testRunStatus) {
22
+ const TestRun = function (executionId, executionName, test, options, branchToUse, testRunStatus) {
24
23
  this._executionId = executionId;
24
+ this._executionName = executionName;
25
25
  this._testStatus = test.testStatus;
26
26
  this._testId = test.testId;
27
27
  this._testName = test.name;
@@ -81,6 +81,10 @@ TestRun.prototype.getExecutionId = function () {
81
81
  return this._executionId;
82
82
  };
83
83
 
84
+ TestRun.prototype.getExecutionName = function () {
85
+ return this._executionName;
86
+ };
87
+
84
88
  TestRun.prototype.getNativeAppData = function () {
85
89
  if (this._nativeApp && !this._options.baseUrl) {
86
90
  return this._nativeApp;
@@ -122,6 +126,7 @@ TestRun.prototype.getRunRequestParams = async function () {
122
126
  refreshToken: testimCustomToken.getRefreshToken(),
123
127
  projectId: this._options.project,
124
128
  executionId: this._executionId,
129
+ executionName: this._executionName,
125
130
  testId: this._testId,
126
131
  resultId: this._testResultId,
127
132
  baseUrl: this._baseUrl,
@@ -232,6 +237,12 @@ TestRun.prototype.clearTestResult = function () {
232
237
  if (this._options.mockNetworkRules) {
233
238
  runData.mockNetworkRules = this._options.mockNetworkRules;
234
239
  }
240
+ const mustClearPreviousStepResults = !this.isAllowReportTestResultRetries() && (this._timeoutRetryCount > 1 || this._retryCount > 1);
241
+
242
+ if (this._options.lightweightMode && this._options.lightweightMode.disableResults &&
243
+ !mustClearPreviousStepResults) {
244
+ return Promise.resolve();
245
+ }
235
246
 
236
247
  this.clearTestResultFinished = testimServicesApi.uploadRunDataArtifact(this._options.project, this._testId, this._testResultId, runData)
237
248
  .catch(err => {
@@ -252,9 +263,6 @@ TestRun.prototype.clearTestResult = function () {
252
263
  testRetryKey: this.getRetryKey(),
253
264
  });
254
265
  });
255
- if (this._testRunStatus.asyncReporting) {
256
- return Promise.resolve();
257
- }
258
266
  return this.clearTestResultFinished;
259
267
  };
260
268
 
@@ -321,10 +329,17 @@ TestRun.prototype.validateRunConfig = function () {
321
329
  const baseUrl = this.getBaseUrl();
322
330
  const { browserValue } = this.getRunConfig();
323
331
 
324
- if (baseUrl) {
325
- const { username, password } = new URL(baseUrl);
332
+ if (baseUrl && browserValue === 'safari') {
333
+ let parsedUrl;
334
+ try {
335
+ parsedUrl = new URL(baseUrl);
336
+ } catch (err) {
337
+ // ignore invalid URLs (missing http:// or https:// prefix)
338
+ return;
339
+ }
340
+ const { username, password } = parsedUrl;
326
341
 
327
- if (browserValue === 'safari' && (username || password)) {
342
+ if (username || password) {
328
343
  throw new Error('Basic authentication in URL is not supported in Safari');
329
344
  }
330
345
  }
@@ -524,7 +539,7 @@ TestRun.prototype.isAllowReportTestResultRetries = function () {
524
539
  TestRun.prototype.onRetry = async function () {
525
540
  this._previousTestResultId = this._testResultId;
526
541
 
527
- if (!featureFlags.flags.countRetries.isEnabled() || !this.isAllowReportTestResultRetries()) {
542
+ if (!this.isAllowReportTestResultRetries()) {
528
543
  return;
529
544
  }
530
545
 
package/testRunStatus.js CHANGED
@@ -43,7 +43,6 @@ const RunStatus = function (testInfoList, options, testPlanId, branchToUse) {
43
43
  this.exportsGlobal = {};
44
44
  this.testInfoList = testInfoList;
45
45
 
46
- this.asyncReporting = false; // whether or not we wait for result reporting
47
46
  this.executionStartedPromise = Promise.resolve();
48
47
 
49
48
  const browserNames = utils.getUniqBrowsers(options, testInfoList);
@@ -92,9 +91,6 @@ const RunStatus = function (testInfoList, options, testPlanId, branchToUse) {
92
91
  RunStatus.prototype.waitForExecutionStartedFinished = function () {
93
92
  return this.executionStartedPromise;
94
93
  };
95
- RunStatus.prototype.setAsyncReporting = function (isAsyncReportingEnabled = false) {
96
- this.asyncReporting = isAsyncReportingEnabled;
97
- };
98
94
  RunStatus.prototype.getTestResult = function (resultId) {
99
95
  return this.testRunStatus[resultId];
100
96
  };
@@ -157,7 +153,7 @@ RunStatus.prototype.updateTestStatusRunning = function (test, executionId, testR
157
153
  return this.executionStartedPromise;
158
154
  }
159
155
 
160
- const res = servicesApi.updateTestDataArtifact(projectId, test.testId, test.resultId, test.config.testData, projectData.defaults)
156
+ return servicesApi.updateTestDataArtifact(projectId, test.testId, test.resultId, test.config.testData, projectData.defaults)
161
157
  .catch(err => {
162
158
  logger.error('failed to upload test data artifact (runner)', { err });
163
159
  return '';
@@ -169,12 +165,6 @@ RunStatus.prototype.updateTestStatusRunning = function (test, executionId, testR
169
165
  await this.executionStartedPromise;
170
166
  return servicesApi.updateTestStatus(projectId, executionId, test.testId, test.resultId, 'RUNNING', { startTime: test.startTime, config: testConfig, remoteRunId, testRetryKey });
171
167
  });
172
- if (this.asyncReporting) {
173
- // silence is golden
174
- } else {
175
- return res;
176
- }
177
- return servicesApi.updateTestStatus(projectId, executionId, test.testId, test.resultId, 'RUNNING', { startTime: test.startTime, config: test.config, remoteRunId, testRetryKey });
178
168
  };
179
169
 
180
170
  RunStatus.prototype.testStartReport = function (test, executionId, testRetryKey) {
@@ -406,10 +396,7 @@ RunStatus.prototype.executionStart = function (executionId, projectId, startTime
406
396
  const ret = servicesApi.reportExecutionStarted(data);
407
397
  this.executionStartedPromise = ret;
408
398
  ret.catch(e => logger.error(e));
409
- if (!this.asyncReporting) {
410
- return ret;
411
- }
412
- return undefined;
399
+ return ret;
413
400
  });
414
401
  };
415
402
 
@@ -90,9 +90,7 @@ class BaseWorker {
90
90
  resultId: testRunHandler.getTestResultId(),
91
91
  seleniumSession: player.getSessionId(),
92
92
  });
93
- if (this.options.lightweightMode && this.options.lightweightMode.disableResults) {
94
- return undefined;
95
- }
93
+
96
94
  return await testRunHandler.clearTestResult();
97
95
  }
98
96
 
@@ -88,6 +88,9 @@ class WorkerSelenium extends BaseWorker {
88
88
 
89
89
  setupCliPerformanceMonitoring(sessionPlayer);
90
90
 
91
+ sessionPlayer.playbackManager.executionId = testRunHandler.getExecutionId();
92
+ sessionPlayer.playbackManager.executionName = testRunHandler.getExecutionName();
93
+
91
94
  sessionPlayer.setLightweightMode(this.options.lightweightMode);
92
95
  if (sessionPlayerInit.localAssetService) {
93
96
  sessionPlayerInit.localAssetService.initialize({ serverUrl: this.options.localRCASaver });