@testim/testim-cli 3.254.0 → 3.256.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.
Files changed (61) hide show
  1. package/agent/routers/cliJsCode/service.js +11 -8
  2. package/agent/routers/codim/router.test.js +9 -12
  3. package/agent/routers/codim/service.js +16 -16
  4. package/agent/routers/playground/service.js +5 -7
  5. package/cli.js +6 -6
  6. package/cliAgentMode.js +11 -10
  7. package/codim/codim-cli.js +14 -9
  8. package/commons/featureFlags.js +29 -7
  9. package/commons/httpRequest.js +5 -1
  10. package/commons/httpRequestCounters.js +21 -10
  11. package/commons/initializeUserWithAuth.js +7 -4
  12. package/commons/lazyRequire.js +4 -3
  13. package/commons/preloadTests.js +6 -3
  14. package/commons/prepareRunner.js +7 -5
  15. package/commons/prepareRunnerAndTestimStartUtils.js +51 -45
  16. package/commons/runnerFileCache.js +10 -2
  17. package/commons/testimServicesApi.js +36 -5
  18. package/commons/testimTunnel.test.js +2 -1
  19. package/coverage/SummaryToObjectReport.js +0 -1
  20. package/coverage/jsCoverage.js +12 -10
  21. package/inputFileUtils.js +11 -9
  22. package/npm-shrinkwrap.json +214 -471
  23. package/package.json +4 -3
  24. package/player/services/tabService.js +15 -1
  25. package/player/stepActions/apiStepAction.js +49 -43
  26. package/player/stepActions/baseCliJsStepAction.js +19 -14
  27. package/player/stepActions/baseJsStepAction.js +9 -8
  28. package/player/stepActions/dropFileStepAction.js +1 -3
  29. package/player/stepActions/inputFileStepAction.js +10 -8
  30. package/player/stepActions/locateStepAction.js +2 -0
  31. package/player/stepActions/mouseStepAction.js +21 -22
  32. package/player/stepActions/nodePackageStepAction.js +34 -35
  33. package/player/stepActions/stepAction.js +1 -0
  34. package/player/utils/imageCaptureUtils.js +133 -172
  35. package/player/utils/screenshotUtils.js +16 -13
  36. package/player/utils/windowUtils.js +20 -8
  37. package/player/webdriver.js +25 -22
  38. package/processHandler.js +4 -0
  39. package/reports/junitReporter.js +6 -7
  40. package/reports/reporter.js +34 -39
  41. package/runOptions.d.ts +286 -0
  42. package/runOptions.js +60 -45
  43. package/runner.js +64 -24
  44. package/runners/ParallelWorkerManager.js +12 -12
  45. package/runners/TestPlanRunner.js +14 -15
  46. package/runners/buildCodeTests.js +1 -0
  47. package/runners/runnerUtils.js +11 -2
  48. package/services/branchService.js +11 -5
  49. package/services/gridService.js +36 -40
  50. package/services/localRCASaver.js +4 -0
  51. package/testRunStatus.js +8 -5
  52. package/utils/argsUtils.js +86 -0
  53. package/utils/argsUtils.test.js +32 -0
  54. package/utils/fsUtils.js +154 -0
  55. package/utils/index.js +10 -161
  56. package/utils/promiseUtils.js +13 -2
  57. package/utils/stringUtils.js +4 -2
  58. package/utils/stringUtils.test.js +22 -0
  59. package/utils/timeUtils.js +25 -0
  60. package/utils/utils.test.js +0 -41
  61. package/workers/WorkerExtension.js +6 -7
package/utils/index.js CHANGED
@@ -1,18 +1,17 @@
1
+ // @ts-check
2
+
1
3
  'use strict';
2
4
 
3
5
  const os = require('os');
4
6
  const _ = require('lodash');
5
7
  const path = require('path');
6
- const moment = require('moment');
7
- const pRetry = require('p-retry');
8
- const Promise = require('bluebird');
9
- const decompress = require('decompress');
10
- const fs = Promise.promisifyAll(require('fs'));
8
+ const fsUtils = require('./fsUtils');
9
+ const argsUtils = require('./argsUtils');
10
+ const timeUtils = require('./timeUtils');
11
11
  const stringUtils = require('./stringUtils');
12
12
  const promiseUtils = require('./promiseUtils');
13
13
  const httpRequest = require('../commons/httpRequest');
14
14
  const { W3C_ELEMENT_ID } = require('../player/constants');
15
- const { sessionType, testStatus: testStatusConst } = require('../commons/constants');
16
15
 
17
16
  const HOMEDIR = os.homedir();
18
17
  const TESTIM_BROWSER_DIR = path.join(HOMEDIR, '.testim-browser-profile');
@@ -78,56 +77,10 @@ function getRunConfigByBrowserName(browser, saucelabs, browserstack) {
78
77
  return _.merge(selectedBrowser, selectedOS);
79
78
  }
80
79
 
81
- function getDuration(ms) {
82
- const duration = moment.duration(ms);
83
- return `${duration.hours()}:${duration.minutes()}:${duration.seconds()}.${duration.milliseconds()}`;
84
- }
85
-
86
- function getDurationSec(ms) {
87
- return moment.duration(ms).asSeconds();
88
- }
89
-
90
- function getRunnerVersion() {
91
- try {
92
- /** @type {import('../../package.json')} */
93
- const pack = require(`${__dirname}/../package.json`); // eslint-disable-line import/no-dynamic-require
94
- return pack.version;
95
- } catch (err) {
96
- return '';
97
- }
98
- }
99
-
100
- function getEnginesVersion() {
101
- try {
102
- /** @type {import('../../package.json')} */
103
- const pack = require(`${__dirname}/../package.json`); // eslint-disable-line import/no-dynamic-require
104
- return pack.engines.node;
105
- } catch (err) {
106
- return '';
107
- }
108
- }
109
-
110
- async function getEnginesVersionAsync() {
111
- try {
112
- /** @type {import('../../package.json')} */
113
- const pack = JSON.parse(await fs.readFileAsync(`${__dirname}/../package.json`));
114
- return pack.engines.node;
115
- } catch (err) {
116
- return '';
117
- }
118
- }
119
-
120
80
  function getEnvironmentGitBranch() {
121
81
  return process.env.GIT_BRANCH || process.env.CIRCLE_BRANCH || process.env.TRAVIS_BRANCH || process.env.CI_BRANCH;
122
82
  }
123
83
 
124
- function getUniqBrowsers(options, testList) {
125
- if ((options.testConfigNames.length || options.testConfigIds.length || options.testPlan.length || options.testPlanIds.length) && !options.browser) {
126
- return _.uniq(testList.map(t => t.runConfig.browserValue));
127
- }
128
- return [options.browser.toLowerCase()];
129
- }
130
-
131
84
  function removePropertyFromObject(obj, propName, cmpFunction) {
132
85
  for (const prop in obj) {
133
86
  if (obj.hasOwnProperty(prop)) {
@@ -140,79 +93,10 @@ function removePropertyFromObject(obj, propName, cmpFunction) {
140
93
  }
141
94
  }
142
95
 
143
- function getCliLocation() {
144
- let cliLocation;
145
- if (!require.main) { // we're in a REPL
146
- return process.cwd(); // fall back on the current working directory
147
- }
148
- if (require.main.filename.includes('/src') || require.main.filename.includes('\\src') || process.env.IS_UNIT_TEST) {
149
- cliLocation = path.resolve(__dirname, '../../');
150
- } else {
151
- cliLocation = path.resolve(__dirname, '../');
152
- }
153
-
154
- return cliLocation;
155
- }
156
-
157
96
  function extractElementId(element) {
158
97
  return element.ELEMENT || element[W3C_ELEMENT_ID];
159
98
  }
160
99
 
161
- const DOWNLOAD_RETRY = 3;
162
- const download = async (url) => pRetry(() => httpRequest.download(url), { retries: DOWNLOAD_RETRY });
163
-
164
- const downloadAndSave = async (url, saveToLocation) => {
165
- const res = await download(url);
166
- return fs.writeFileAsync(saveToLocation, res.body);
167
- };
168
-
169
- const copy = async (readFile, destFile) => new Promise((resolve, reject) => {
170
- try {
171
- const file = fs.createWriteStream(destFile);
172
- fs.createReadStream(readFile).pipe(file);
173
- file.on('finish', () => {
174
- file.close(resolve);
175
- });
176
- } catch (err) {
177
- reject(err);
178
- }
179
- });
180
- function getSourcePath(location, fileName) {
181
- if (stringUtils.isURL(location)) {
182
- return fileName || path.join(process.cwd(), location.replace(/^.*[\\\/]/, ''));
183
- }
184
-
185
- return fileName || path.basename(location);
186
- }
187
-
188
- const getSource = async (location, fileName) => {
189
- const destFile = getSourcePath(location, fileName);
190
- if (stringUtils.isURL(location)) {
191
- return downloadAndSave(location, destFile);
192
- }
193
-
194
- return copy(location, destFile);
195
- };
196
-
197
- const getSourceAsBuffer = async (location) => {
198
- if (stringUtils.isURL(location)) {
199
- return download(location);
200
- }
201
- return fs.readFileAsync(location);
202
- };
203
-
204
- const unzipFile = async (srcZipFile, destZipPath) => await decompress(srcZipFile, destZipPath);
205
-
206
- const getLocalFileSizeInMB = (fileLocation) => {
207
- const stats = fs.statSync(fileLocation);
208
- const fileSizeInBytes = stats.size;
209
- return fileSizeInBytes / 1000000;
210
- };
211
-
212
- function getSessionType(options) {
213
- return options.files.length > 0 ? sessionType.CODEFUL : sessionType.CODELESS;
214
- }
215
-
216
100
  function getPlanType(plan) {
217
101
  plan = plan || {};
218
102
  const now = Date.now();
@@ -240,17 +124,11 @@ const calcPercentile = (arr, percentile) => {
240
124
  return arr[index];
241
125
  };
242
126
 
243
- const hasTestPlanFlag = (options) => Boolean(options.testPlan?.length || options.testPlanIds?.length);
244
-
245
- const isRemoteRun = (options) => options.resultId && options.source === 'remote-run';
246
-
247
- const isQuarantineAndNotRemoteRun = (test, options) => test.testStatus === testStatusConst.QUARANTINE && !isRemoteRun(options) && !options.runQuarantinedTests;
248
-
249
127
  function groupTestsByRetries(testResults = []) { // NOTE: This duplicates a function in services (stream-data/result/resultService.js) since we can't share code between packages.
250
128
  return _.chain(testResults)
251
129
  .groupBy((tr) => tr.originalTestResultId || tr.resultId)
252
130
  .values()
253
- .reduce((all, current) => {
131
+ .reduce((/** @type {any[]} */ all, current) => {
254
132
  if (!current) {
255
133
  return all;
256
134
  }
@@ -269,7 +147,7 @@ function groupTestsByRetries(testResults = []) { // NOTE: This duplicates a func
269
147
  all.push(last);
270
148
  return all;
271
149
  }, [])
272
- .filter(Boolean)
150
+ .compact()
273
151
  .value();
274
152
  }
275
153
 
@@ -293,49 +171,20 @@ async function getCdpAddressForHost(browserInstanceHost, timeout) {
293
171
  }
294
172
  }
295
173
 
296
- function getArgsOnRemoteRunFailure() {
297
- const { argv: args } = process;
298
- if (!args.includes('--remoteRunId')) {
299
- return undefined;
300
- }
301
- return {
302
- remoteRunId: args[args.indexOf('--remoteRunId') + 1],
303
- projectId: args[args.indexOf('--project') + 1],
304
- token: args[args.indexOf('--token') + 1],
305
- };
306
- }
307
-
308
174
 
309
175
  module.exports = {
310
176
  TESTIM_BROWSER_DIR,
311
177
  removePropertyFromObject,
312
- getDuration,
313
- getDurationSec,
314
- getRunnerVersion,
315
- getEnginesVersion,
316
- getEnginesVersionAsync,
317
178
  getEnvironmentGitBranch,
318
- getUniqBrowsers,
319
179
  getRunConfigByBrowserName,
320
180
  extractElementId,
321
- getCliLocation,
322
- download,
323
- downloadAndSave,
324
- copy,
325
- unzipFile,
326
- getLocalFileSizeInMB,
327
- getSource,
328
- getSourceAsBuffer,
329
- getSessionType,
330
- getSourcePath,
331
181
  calcPercentile,
332
- hasTestPlanFlag,
333
- isRemoteRun,
334
- isQuarantineAndNotRemoteRun,
335
182
  groupTestsByRetries,
336
183
  getPlanType,
337
184
  getCdpAddressForHost,
338
- getArgsOnRemoteRunFailure,
185
+ ...fsUtils,
186
+ ...argsUtils,
187
+ ...timeUtils,
339
188
  ...promiseUtils,
340
189
  ...stringUtils,
341
190
  };
@@ -1,5 +1,7 @@
1
1
  // @ts-check
2
2
 
3
+ 'use strict';
4
+
3
5
  const Bluebird = require('bluebird');
4
6
  const { TimeoutError } = require('../errors');
5
7
 
@@ -43,13 +45,22 @@ function promiseTimeout(promise, timeout, errMsg = 'Timeout Error') {
43
45
  ]);
44
46
  }
45
47
 
48
+ /** @type {import('p-limit').default} */
49
+ let pLimit;
50
+
46
51
  /**
47
52
  * @template T, U
48
53
  * @param {ArrayLike<T> | Iterable<T>} arr
49
54
  * @param {(item: T, index: number) => PromiseLike<U>} handler
55
+ * @param {{ concurrency?: number }} options
50
56
  */
51
- function promiseMap(arr, handler) {
52
- return Promise.all(Array.from(arr, handler));
57
+ async function promiseMap(arr, handler, { concurrency } = {}) {
58
+ pLimit = pLimit || (await import('p-limit')).default;
59
+ if (concurrency) {
60
+ const limit = pLimit(concurrency);
61
+ return await Promise.all(Array.from(arr, (item, index) => limit(() => handler(item, index))));
62
+ }
63
+ return await Promise.all(Array.from(arr, handler));
53
64
  }
54
65
 
55
66
  /**
@@ -1,5 +1,7 @@
1
1
  // @ts-check
2
2
 
3
+ 'use strict';
4
+
3
5
  /**
4
6
  * @param {number} length
5
7
  * @returns {string}
@@ -14,8 +16,8 @@ function randomString(length) {
14
16
  * @param {string} editorUrl
15
17
  * @param {string} projectId
16
18
  * @param {string} testId
17
- * @param {string | undefined} resultId
18
- * @param {string} branch
19
+ * @param {string=} resultId
20
+ * @param {string=} branch
19
21
  */
20
22
  function getTestUrl(editorUrl, projectId, testId, resultId, branch) {
21
23
  let testUrl = '';
@@ -0,0 +1,22 @@
1
+ const chai = require('chai'); // eslint-disable-line import/no-extraneous-dependencies
2
+ const stringUtils = require('./stringUtils');
3
+
4
+ const expect = chai.expect;
5
+
6
+ describe('stringUtils', () => {
7
+ describe('getTestUrl', () => {
8
+ it('should create properly escaped test URL', () => {
9
+ expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test')).to.equal('http://localhost:8080/#/project/project/branch/master/test/test');
10
+ expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test', 'result')).to.equal('http://localhost:8080/#/project/project/branch/master/test/test?result-id=result');
11
+ expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', null)).to.equal('http://localhost:8080/#/project/project/branch/master/test/test?result-id=result');
12
+ expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', 'normal-branch-name'))
13
+ .to.equal('http://localhost:8080/#/project/project/branch/normal-branch-name/test/test?result-id=result');
14
+ expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', 'branch/with/slashes'))
15
+ .to.equal('http://localhost:8080/#/project/project/branch/branch%2Fwith%2Fslashes/test/test?result-id=result');
16
+ expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', 'branch with spaces'))
17
+ .to.equal('http://localhost:8080/#/project/project/branch/branch%20with%20spaces/test/test?result-id=result');
18
+ expect(stringUtils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', 'encoded%20branch'))
19
+ .to.equal('http://localhost:8080/#/project/project/branch/encoded%2520branch/test/test?result-id=result');
20
+ });
21
+ });
22
+ });
@@ -0,0 +1,25 @@
1
+ //@ts-check
2
+
3
+ 'use strict';
4
+
5
+ const moment = require('moment');
6
+
7
+ /**
8
+ * @param {moment.DurationInputArg1} ms
9
+ */
10
+ function getDuration(ms) {
11
+ const duration = moment.duration(ms);
12
+ return `${duration.hours()}:${duration.minutes()}:${duration.seconds()}.${duration.milliseconds()}`;
13
+ }
14
+
15
+ /**
16
+ * @param {moment.DurationInputArg1} ms
17
+ */
18
+ function getDurationSec(ms) {
19
+ return moment.duration(ms).asSeconds();
20
+ }
21
+
22
+ module.exports = {
23
+ getDuration,
24
+ getDurationSec,
25
+ };
@@ -24,45 +24,4 @@ describe('utils', () => {
24
24
  expect(p100).to.eql(10);
25
25
  });
26
26
  });
27
- describe('getTestUrl', () => {
28
- it('should create properly escaped test URL', () => {
29
- expect(utils.getTestUrl('http://localhost:8080', 'project', 'test')).to.equal('http://localhost:8080/#/project/project/branch/master/test/test');
30
- expect(utils.getTestUrl('http://localhost:8080', 'project', 'test', 'result')).to.equal('http://localhost:8080/#/project/project/branch/master/test/test?result-id=result');
31
- expect(utils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', null)).to.equal('http://localhost:8080/#/project/project/branch/master/test/test?result-id=result');
32
- expect(utils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', 'normal-branch-name'))
33
- .to.equal('http://localhost:8080/#/project/project/branch/normal-branch-name/test/test?result-id=result');
34
- expect(utils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', 'branch/with/slashes'))
35
- .to.equal('http://localhost:8080/#/project/project/branch/branch%2Fwith%2Fslashes/test/test?result-id=result');
36
- expect(utils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', 'branch with spaces'))
37
- .to.equal('http://localhost:8080/#/project/project/branch/branch%20with%20spaces/test/test?result-id=result');
38
- expect(utils.getTestUrl('http://localhost:8080', 'project', 'test', 'result', 'encoded%20branch'))
39
- .to.equal('http://localhost:8080/#/project/project/branch/encoded%2520branch/test/test?result-id=result');
40
- });
41
- });
42
-
43
- describe('getArgsOnRemoteRunFailure', () => {
44
- let originalArgv;
45
-
46
- beforeEach(() => {
47
- originalArgv = process.argv;
48
- });
49
-
50
- afterEach(() => {
51
- process.argv = originalArgv;
52
- });
53
-
54
- it('should return undefined if no remote run is current', () => {
55
- process.argv = ['node', 'file.js', '--token', 'token', '--project', 'project-id'];
56
- expect(utils.getArgsOnRemoteRunFailure()).to.be.undefined;
57
- });
58
-
59
- it('should return details if remote run is current', () => {
60
- process.argv = ['node', 'file.js', '--token', 'token', '--project', 'project-id', '--remoteRunId', 'remote-run-id'];
61
- expect(utils.getArgsOnRemoteRunFailure()).to.eql({
62
- remoteRunId: 'remote-run-id',
63
- projectId: 'project-id',
64
- token: 'token',
65
- });
66
- });
67
- });
68
27
  });
@@ -10,7 +10,6 @@ const ExtensionTestPlayer = require('../player/extensionTestPlayer');
10
10
  const ChromeLauncherTestPlayer = require('../player/chromeLauncherTestPlayer');
11
11
  const reporter = require('../reports/reporter');
12
12
 
13
- const TEST_START_TIMEOUT_MS = parseInt(process.env.TESTIM_TEST_START_TIMEOUT, 10) || (2 * 60 * 1000);
14
13
 
15
14
  class WorkerExtension extends BaseWorker {
16
15
  initPlayer() {
@@ -65,7 +64,7 @@ class WorkerExtension extends BaseWorker {
65
64
  if (this.options.useChromeLauncher) {
66
65
  const testTimeout = this.options.timeoutWasGiven ?
67
66
  Math.max(10000, this.options.timeout) :
68
- TEST_START_TIMEOUT_MS;
67
+ this.options.testStartTimeout;
69
68
 
70
69
  reporter.onWaitToTestStart(this.id);
71
70
  reporter.onWaitToTestComplete(this.id, this.isCodeMode);
@@ -82,20 +81,20 @@ class WorkerExtension extends BaseWorker {
82
81
  });
83
82
  }
84
83
 
85
- const startStausDetails = { driverUrlFinished: false, testRunHandlerStartedFinished: false }; //for logging / debugging purposes
84
+ const startStatusDetails = { driverUrlFinished: false, testRunHandlerOnStartedHadFinished: false }; //for logging / debugging purposes
86
85
  return new Promise((resolve, reject) => testRunHandler.getRunTestUrl()
87
86
  .then(url => {
88
87
  reporter.onWaitToTestStart(this.id);
89
88
  return Promise.all([
90
- driver.url(url).tap(() => { startStausDetails.driverUrlFinished = true; }).catch(err => {
89
+ driver.url(url).tap(() => { startStatusDetails.driverUrlFinished = true; }).catch(err => {
91
90
  logger.error('error from driver.url', { err, testResultId, executionId, testId, url, urlLength: url.length });
92
91
  throw err;
93
92
  }),
94
- testRunHandler.onStarted(TEST_START_TIMEOUT_MS).tap(() => { startStausDetails.testRunHandlerStartedFinished = true; }),
93
+ testRunHandler.onStarted(this.options.testStartTimeout).tap(() => { startStatusDetails.testRunHandlerOnStartedHadFinished = true; }),
95
94
  ])
96
- .timeout(TEST_START_TIMEOUT_MS, timeoutMessages.TEST_START_TIMEOUT_MSG)
95
+ .timeout(this.options.testStartTimeout, timeoutMessages.TEST_START_TIMEOUT_MSG)
97
96
  .catch(Promise.TimeoutError, () => {
98
- logger.warn('timeout occurred (see log\'s payload). Running checkViaRestAPIIfTestStarted', { testResultId, executionId, testId, ...startStausDetails });
97
+ logger.warn('timeout occurred (see log\'s payload). Running checkViaRestAPIIfTestStarted', { testResultId, executionId, testId, ...startStatusDetails });
99
98
  return testRunHandler.checkViaRestAPIIfTestStarted();
100
99
  });
101
100
  })