@testim/testim-cli 3.212.0 → 3.216.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/runner.js CHANGED
@@ -25,6 +25,7 @@ const FREE_PLAN_MINIMUM_BROWSER_TIMEOUT = 30 * 60 * 1000;
25
25
  const TestPlanRunner = require('./runners/TestPlanRunner');
26
26
  const labFeaturesService = require('./services/labFeaturesService');
27
27
  const featureAvailabilityService = require('./commons/featureAvailabilityService');
28
+ const featureFlagService = require('./commons/featureFlags');
28
29
 
29
30
  const logger = require('./commons/logger').getLogger('runner');
30
31
 
@@ -268,12 +269,12 @@ async function init(options) {
268
269
  await labFeaturesService.loadLabFeatures(projectById.id, companyByProjectId.activePlan);
269
270
  }
270
271
 
271
- if (options.lightweightMode && options.lightweightMode.type === 'highSpeed' && (!labFeaturesService.isFeatureAvailableForProject('highSpeedMode') || options.company.planType === 'free')) {
272
+ if (options.lightweightMode && options.lightweightMode.type === 'turboMode' && (featureFlagService.flags.highSpeedMode.getValue() === 'disabled' || options.company.planType === 'free')) {
272
273
  delete options.lightweightMode;
273
274
  }
274
275
 
275
- if (options.lightweightMode && options.lightweightMode.type === 'highSpeed') {
276
- console.log('High-speed mode will ignore step delays. Test artifacts like screenshots and logs will only be saved for failed runs. For more information see our docs: https://help.testim.io/docs/high-speed-mode');
276
+ if (options.lightweightMode && options.lightweightMode.type === 'turboMode') {
277
+ console.log('\nTurbo mode will ignore step delays. Test artifacts like screenshots and logs will only be saved for failed runs. For more information see our docs: https://help.testim.io/docs/turbo-mode');
277
278
  }
278
279
 
279
280
  gridService.keepAlive.start(project);
@@ -180,6 +180,7 @@ class LambdatestService {
180
180
 
181
181
  const defaultBrowserCaps = LambdatestService.lambdatestConfig.CAPABILITIES[browser] || {};
182
182
 
183
+
183
184
  const tunnelCaps = {};
184
185
  if (LambdatestService.tunnelName) {
185
186
  tunnelCaps.tunnel = true;
package/testRunHandler.js CHANGED
@@ -18,6 +18,8 @@ const { SeleniumPerfStats } = require('./commons/SeleniumPerfStats');
18
18
  const { preloadTests } = require('./commons/preloadTests');
19
19
 
20
20
  const RETRIES_ON_TIMEOUT = 3;
21
+ 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.
22
+ const canSendRunDataOverUrl = (runData) => JSON.stringify(runData).length < MAX_LIGHTWEIGHT_MODE_RUN_DATA_SIZE;
21
23
 
22
24
  const TestRun = function (executionId, executionName, test, options, branchToUse, testRunStatus) {
23
25
  this._executionId = executionId;
@@ -175,7 +177,14 @@ TestRun.prototype.getRunRequestParams = async function () {
175
177
 
176
178
  if (this._options.lightweightMode && this._options.lightweightMode.general) {
177
179
  runRequestParams.company = this._options.company;
178
- runRequestParams.runData = this.getRunData();
180
+ const runData = this.getRunData();
181
+ runRequestParams.lightweightMode.isRunDataSentInUrl = canSendRunDataOverUrl(runData);
182
+ if (runRequestParams.lightweightMode.isRunDataSentInUrl) {
183
+ runRequestParams.runData = runData;
184
+ logger.info(`Run data sent as URL param, test id: ${this.getTestId()} run data length: ${JSON.stringify(runData).length}`);
185
+ } else {
186
+ logger.warn(`Run data is too big to be sent as a URL param. Test id: ${this.getTestId()}, run data size: ${JSON.stringify(runData).length} (limit: ${MAX_LIGHTWEIGHT_MODE_RUN_DATA_SIZE} characters)`);
187
+ }
179
188
  runRequestParams.isLocalRun = Boolean(this._options.useLocalChromeDriver || this._options.useChromeLauncher);
180
189
  }
181
190
 
@@ -195,9 +204,11 @@ TestRun.prototype.getRunRequestParams = async function () {
195
204
  return runRequestParams;
196
205
  };
197
206
 
198
- TestRun.prototype.getRunTestUrl = function () {
199
- return this.getRunRequestParams()
200
- .then(runRequestParams => `https://run.testim.io/?params=${encodeURIComponent(JSON.stringify(runRequestParams))}`);
207
+ TestRun.prototype.getRunTestUrl = async function () {
208
+ const runRequestParams = await this.getRunRequestParams();
209
+ const url = `https://run.testim.io/?params=${encodeURIComponent(JSON.stringify(runRequestParams))}`;
210
+ logger.info(`Test (${this.getTestId()}) run URL length: ${url.length}`);
211
+ return url;
201
212
  };
202
213
 
203
214
  TestRun.prototype.setSessionId = function (sessionId) {
@@ -240,7 +251,7 @@ TestRun.prototype.clearTestResult = function () {
240
251
  const mustClearPreviousStepResults = !this.isAllowReportTestResultRetries() && (this._timeoutRetryCount > 1 || this._retryCount > 1);
241
252
 
242
253
  if (this._options.lightweightMode && this._options.lightweightMode.disableResults &&
243
- !mustClearPreviousStepResults) {
254
+ !mustClearPreviousStepResults && canSendRunDataOverUrl(runData)) {
244
255
  return Promise.resolve();
245
256
  }
246
257
 
@@ -8,7 +8,7 @@ const { timeoutMessages, testRunStatus, stepResult, runnerTestStatus } = require
8
8
  const logger = require('../commons/logger').getLogger('base-worker');
9
9
  const testResultService = require('../commons/socket/testResultService');
10
10
  const remoteStepService = require('../commons/socket/remoteStepService');
11
- const { isNetworkHealthy } = require('../commons/httpRequest');
11
+ const { isNetworkHealthy, didNetworkConnectivityTestFail } = require('../commons/httpRequest');
12
12
  const testimServicesApi = require('../commons/testimServicesApi');
13
13
  const gridService = require('../services/gridService');
14
14
  const LambdatestService = require('../services/lambdatestService');
@@ -264,13 +264,14 @@ class BaseWorker {
264
264
  return undefined;
265
265
  }
266
266
  };
267
+ const getNetworkErrorMessage = () => 'Due to network connectivity issues, Testim CLI has been unable to connect to the grid.\n' +
268
+ `Please make sure the CLI has stable access to the internet. ${didNetworkConnectivityTestFail() ? '(Internal: network connectivity test failed)' : ''}`;
267
269
 
268
- const buildError = (err) => {
269
- if (!isNetworkHealthy() && featureFlags.flags.errorMessageOnBadNetwork.isEnabled()) {
270
+ const buildError = (err, wasNetworkHealthy) => {
271
+ if (!wasNetworkHealthy && featureFlags.flags.errorMessageOnBadNetwork.isEnabled()) {
270
272
  return {
271
273
  errorType: NETWORK_ERROR,
272
- reason: 'Due to network connectivity issues, Testim CLI has been unable to connect to the grid.' +
273
- 'Please make sure the CLI has stable access to the internet.',
274
+ reason: getNetworkErrorMessage(),
274
275
  };
275
276
  }
276
277
 
@@ -324,17 +325,16 @@ class BaseWorker {
324
325
  };
325
326
 
326
327
  const onRunError = async (err, testRunHandler) => {
327
- if (!isNetworkHealthy() && featureFlags.flags.warnOnBadNetwork.isEnabled()) {
328
+ const wasNetworkHealthy = await isNetworkHealthy();
329
+ if (!wasNetworkHealthy && featureFlags.flags.warnOnBadNetwork.isEnabled()) {
328
330
  // intentional, we want to log to stderr:
329
331
  // eslint-disable-next-line no-console
330
- console.warn('Due to network connectivity issues, Testim CLI has been unable to connect to the grid.');
331
- // eslint-disable-next-line no-console
332
- console.warn('Please make sure the CLI has stable access to the internet.');
332
+ console.warn(getNetworkErrorMessage());
333
333
  }
334
334
  logger.warn('error on run', { err });
335
335
 
336
336
  const projectId = this.userData && this.userData.projectId;
337
- const { errorType, reason } = buildError(err);
337
+ const { errorType, reason } = buildError(err, wasNetworkHealthy);
338
338
  testimServicesApi.updateTestResult(projectId, this.testResultId, this.testId, {
339
339
  status: testRunStatus.COMPLETED,
340
340
  success: false,
@@ -87,7 +87,10 @@ class WorkerExtension extends BaseWorker {
87
87
  .then(url => {
88
88
  reporter.onWaitToTestStart(this.id);
89
89
  return Promise.all([
90
- driver.url(url).tap(() => { startStausDetails.driverUrlFinished = true; }).tapCatch(err => logger.error('error from driver.url', { err, testResultId, executionId, testId })),
90
+ driver.url(url).tap(() => { startStausDetails.driverUrlFinished = true; }).catch(err => {
91
+ logger.error('error from driver.url', { err, testResultId, executionId, testId, url, urlLength: url.length });
92
+ throw err;
93
+ }),
91
94
  testRunHandler.onStarted(TEST_START_TIMEOUT_MS).tap(() => { startStausDetails.testRunHandlerStartedFinished = true; }),
92
95
  ])
93
96
  .timeout(TEST_START_TIMEOUT_MS, timeoutMessages.TEST_START_TIMEOUT_MSG)
@@ -107,14 +110,11 @@ class WorkerExtension extends BaseWorker {
107
110
  };
108
111
  driver.registerToClosedBrowser(onBrowserClosed);
109
112
  return testRunHandler.onCompleted().timeout(this.testRunTimeout, timeoutMessages.TEST_COMPLETE_TIMEOUT_MSG)
110
- .tap(testResult => {
113
+ .then(async testResult => {
111
114
  driver.unregisterToClosedBrowser(onBrowserClosed);
112
115
  if (this.lambdatestService.isLambdatestRun()) {
113
- return driver.executeJS(`lambda-status=${!testResult.success ? 'failed' : 'passed'}`).catch(() => {});
116
+ await driver.executeJS(`lambda-status=${!testResult.success ? 'failed' : 'passed'}`).catch(() => { });
114
117
  }
115
- return undefined;
116
- })
117
- .then(testResult => {
118
118
  if (!driver.isAlive()) {
119
119
  logger.warn(`possible grid unresponsive for test ${this.testId}, result ${this.testResultId} (execution: ${this.executionId})`);
120
120
  testResult.gridIssues = 'could not validate grid is alive';
@@ -168,13 +168,10 @@ class WorkerSelenium extends BaseWorker {
168
168
  }
169
169
  throw err;
170
170
  })
171
- .tap(() => {
171
+ .then(async testResult => {
172
172
  if (sessionPlayerInit.localAssetService) {
173
- return sessionPlayerInit.localAssetService.drain();
173
+ await sessionPlayerInit.localAssetService.drain();
174
174
  }
175
- return undefined;
176
- })
177
- .then(testResult => {
178
175
  testResult.stepsResults = null;
179
176
  testResult.resultId = this.testResultId;
180
177
  if (!driver.isAlive()) {
@@ -187,13 +184,11 @@ class WorkerSelenium extends BaseWorker {
187
184
  logger.warn(`possible browser keep alive issue ${this.testId}, result ${this.testResultId} (execution: ${this.executionId})`);
188
185
  testResult.keepAliveIssue = maxKeepAliveGap;
189
186
  }
190
- return { ...testResult, ...testRunHandler.seleniumPerfStats.getStats() };
191
- })
192
- .tap(testResult => {
187
+ const resultWithStats = { ...testResult, ...testRunHandler.seleniumPerfStats.getStats() };
193
188
  if (this.lambdatestService.isLambdatestRun()) {
194
- return driver.executeJS(`lambda-status=${!testResult.success ? 'failed' : 'passed'}`).catch(() => { });
189
+ await driver.executeJS(`lambda-status=${!resultWithStats.success ? 'failed' : 'passed'}`).catch(() => { });
195
190
  }
196
- return Promise.resolve();
191
+ return resultWithStats;
197
192
  });
198
193
  }
199
194
 
@@ -1,19 +1,6 @@
1
1
  const Promise = require('bluebird');
2
- const utils = require('../utils');
3
2
  const gridService = require('../services/gridService');
4
3
  const logger = require('../commons/logger').getLogger('worker-utils');
5
- const { timeoutMessages } = require('../commons/constants');
6
- const { GetBrowserError, PageNotAvailableError } = require('../errors');
7
-
8
- const waitUntilBrowserTimeout = (err, startTime, interval, projectId, workerId, player, releaseSlotOnTestFinished) => {
9
- logger.warn('failed getting browser from grid', { err });
10
- return releasePlayer(workerId, releaseSlotOnTestFinished, projectId, player)
11
- .then(() => {
12
- const requestTime = Date.now() - startTime;
13
- const timeDiff = interval - requestTime;
14
- return Promise.delay(timeDiff).then(() => Promise.reject(err));
15
- });
16
- };
17
4
 
18
5
  const releaseGridSlot = (workerId, releaseSlotOnTestFinished, projectId) => {
19
6
  if (!releaseSlotOnTestFinished) {
@@ -29,26 +16,3 @@ const releasePlayer = (workerId, releaseSlotOnTestFinished, projectId, player) =
29
16
  };
30
17
 
31
18
  module.exports.releasePlayer = releasePlayer;
32
-
33
- module.exports.getBrowserWithRetries = ({ getBrowserOnce, testPlayerFactory, releaseSlotOnTestFinished }, { totalTimeoutDuration, singleGetBrowserDuration, projectId, workerId, reporter }) => {
34
- const maxGetBrowserAttempts = totalTimeoutDuration / singleGetBrowserDuration;
35
- let failedAttempts = 0;
36
-
37
- return utils.runWithRetries(() => {
38
- const startTime = Date.now();
39
- const player = testPlayerFactory();
40
- return getBrowserOnce(player)
41
- .then((getBrowserRes) => player || getBrowserRes)
42
- .timeout(singleGetBrowserDuration, timeoutMessages.GET_BROWSER_TIMEOUT_MSG)
43
- .tapCatch(() => reporter.onGetBrowserFailure(workerId, projectId, ++failedAttempts))
44
- .tap(() => reporter.onGetBrowserSuccess(workerId, projectId))
45
- .catch(err => waitUntilBrowserTimeout(err, startTime, singleGetBrowserDuration, projectId, workerId, player, releaseSlotOnTestFinished));
46
- }, maxGetBrowserAttempts).catch(err => {
47
- if (err instanceof PageNotAvailableError) {
48
- throw err;
49
- }
50
- throw new GetBrowserError(err);
51
- });
52
- };
53
-
54
- module.exports.isGetBrowserError = err => err && err.constructor && err.constructor.name === 'GetBrowserError';
@@ -1,39 +0,0 @@
1
- module.exports = function (input) {
2
- const {transactionId, packages, command, localPackageInstallFolder, proxyUri} = input;
3
- return installLocalPackages(transactionId, packages, command, localPackageInstallFolder, proxyUri);
4
- };
5
-
6
- const path = require('path');
7
- const npm = require("npm");
8
- const {NpmPackageError} = require('../../../errors');
9
-
10
- function installLocalPackages(transactionId, packages, command, localPackageInstallFolder, proxyUri) {
11
- if (!packages || packages.length === 0) {
12
- return Promise.resolve();
13
- }
14
-
15
- const installFolder = path.join(localPackageInstallFolder, `/${transactionId}`);
16
-
17
- return new Promise((resolve, reject) => {
18
- const npmLoadConfig = {
19
- prefix: installFolder,
20
- loglevel: 'silent',
21
- };
22
- if(proxyUri) {
23
- npmLoadConfig.proxy = proxyUri;
24
- npmLoadConfig["https-proxy"] = proxyUri;
25
- }
26
- npm.load(npmLoadConfig, (err) => {
27
- if (err) {
28
- return reject(err);
29
- }
30
-
31
- npm.commands[command](packages, (installErr, data) => {
32
- if (installErr) {
33
- return reject(new NpmPackageError(installErr.message));
34
- }
35
- resolve(Object.assign({data, installFolder}));
36
- });
37
- });
38
- });
39
- }