@testim/testim-cli 3.254.0 → 3.255.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 (43) hide show
  1. package/agent/routers/codim/router.test.js +9 -12
  2. package/agent/routers/codim/service.js +16 -16
  3. package/agent/routers/playground/service.js +5 -7
  4. package/cli.js +4 -4
  5. package/cliAgentMode.js +4 -3
  6. package/codim/codim-cli.js +10 -8
  7. package/commons/featureFlags.js +8 -0
  8. package/commons/httpRequest.js +5 -1
  9. package/commons/httpRequestCounters.js +21 -10
  10. package/commons/lazyRequire.js +4 -3
  11. package/commons/preloadTests.js +2 -2
  12. package/commons/prepareRunner.js +4 -2
  13. package/commons/prepareRunnerAndTestimStartUtils.js +40 -41
  14. package/commons/runnerFileCache.js +1 -1
  15. package/commons/testimTunnel.test.js +2 -1
  16. package/coverage/SummaryToObjectReport.js +0 -1
  17. package/coverage/jsCoverage.js +12 -10
  18. package/inputFileUtils.js +11 -9
  19. package/npm-shrinkwrap.json +187 -444
  20. package/package.json +4 -3
  21. package/player/services/tabService.js +15 -1
  22. package/player/stepActions/locateStepAction.js +2 -0
  23. package/player/utils/imageCaptureUtils.js +81 -120
  24. package/player/webdriver.js +25 -22
  25. package/reports/junitReporter.js +6 -7
  26. package/reports/reporter.js +34 -39
  27. package/runOptions.d.ts +260 -0
  28. package/runOptions.js +53 -38
  29. package/runner.js +2 -1
  30. package/runners/ParallelWorkerManager.js +9 -10
  31. package/runners/TestPlanRunner.js +5 -9
  32. package/services/gridService.js +36 -40
  33. package/testRunStatus.js +8 -5
  34. package/utils/argsUtils.js +86 -0
  35. package/utils/argsUtils.test.js +32 -0
  36. package/utils/fsUtils.js +154 -0
  37. package/utils/index.js +10 -161
  38. package/utils/promiseUtils.js +13 -2
  39. package/utils/stringUtils.js +4 -2
  40. package/utils/stringUtils.test.js +22 -0
  41. package/utils/timeUtils.js +25 -0
  42. package/utils/utils.test.js +0 -41
  43. package/workers/WorkerExtension.js +6 -7
package/runner.js CHANGED
@@ -180,6 +180,7 @@ function setAuthData(options, authData) {
180
180
  function setProject(options, project) {
181
181
  const { id, name, type, defaults } = project;
182
182
  featureFlags.setProjectId(id);
183
+ featureFlags.setProjectType(type);
183
184
  options.projectData = {
184
185
  projectId: id,
185
186
  type,
@@ -203,7 +204,7 @@ async function setMockNetworkRules(options) {
203
204
  }
204
205
 
205
206
  /**
206
- * @param {Awaited<ReturnType<typeof import('./runOptions')['process']>>} options
207
+ * @param {import('./runOptions').RunnerOptions} options
207
208
  * @param {string=} customExtensionLocalLocation
208
209
  */
209
210
  async function runRunner(options, customExtensionLocalLocation) {
@@ -61,13 +61,13 @@ class ParallelWorkerManager {
61
61
  const combinedTestResults = {};
62
62
  const testCount = testList.length;
63
63
 
64
- const companyId = options.company && options.company.companyId;
65
- const companyName = options.company && options.company.name;
64
+ const companyId = options.company?.companyId;
65
+ const companyName = options.company?.name;
66
66
  const source = options.source || 'cli';
67
67
  const user = options.user;
68
- const companyPlan = options.company && options.company.planType;
69
- const isStartUp = options.company && options.company.isStartUp;
70
- const projectName = options.projectData && options.projectData.name;
68
+ const companyPlan = options.company?.planType;
69
+ const isStartUp = options.company?.isStartUp;
70
+ const projectName = options.projectData?.name;
71
71
  const lightweightMode = options.lightweightMode;
72
72
  const sessionType = utils.getSessionType(options);
73
73
 
@@ -95,7 +95,7 @@ class ParallelWorkerManager {
95
95
  const onTestCompleted = async (wid, testId, testResult, sessionId, isRerun) => {
96
96
  runningTests--;
97
97
  const update = {};
98
- if (lightweightMode && lightweightMode.onlyTestIdsNoSuite) {
98
+ if (lightweightMode?.onlyTestIdsNoSuite) {
99
99
  update.show = true;
100
100
  }
101
101
  if (testResult.seleniumStats) {
@@ -115,9 +115,9 @@ class ParallelWorkerManager {
115
115
  update.gridHost = options.host;
116
116
  }
117
117
  if (options.grid || options.gridId) {
118
- update.gridName = options.grid || (options.gridData && options.gridData.name);
119
- update.gridType = options.gridData && options.gridData.type;
120
- update.gridProvider = options.gridData && options.gridData.provider;
118
+ update.gridName = options.grid || options.gridData?.name;
119
+ update.gridType = options.gridData?.type;
120
+ update.gridProvider = options.gridData?.provider;
121
121
  } else if (options.useLocalChromeDriver) {
122
122
  update.gridName = 'local-chrome-driver-from-options';
123
123
  update.gridType = 'local-chrome';
@@ -214,7 +214,6 @@ class ParallelWorkerManager {
214
214
  }
215
215
  }
216
216
 
217
-
218
217
  function schedule(fn, index) {
219
218
  if (index === 0) {
220
219
  fn();
@@ -71,10 +71,6 @@ class TestPlanRunner {
71
71
  Object.assign(executionResults, afterTestsResults);
72
72
  };
73
73
 
74
- function catchBeforeTestsFailed() {
75
- return testStatus.markAllQueuedTests(executionId, constants.runnerTestStatus.ABORTED, 'aborted', false);
76
- }
77
-
78
74
  const sessionType = utils.getSessionType(tpOptions);
79
75
  analyticsService.analyticsExecsStart({ authData, executionId, projectId: tpOptions.project, sessionType });
80
76
  perf.log('right before runBeforeTests');
@@ -88,7 +84,7 @@ class TestPlanRunner {
88
84
  } catch (err) {
89
85
  logger.error('error running test plan', { err });
90
86
  if (err instanceof StopRunOnError) {
91
- return catchBeforeTestsFailed();
87
+ return testStatus.markAllQueuedTests(executionId, constants.runnerTestStatus.ABORTED, 'aborted', false);
92
88
  }
93
89
  throw err;
94
90
  } finally {
@@ -218,7 +214,7 @@ class TestPlanRunner {
218
214
  * @param {any[]} beforeTests
219
215
  * @param {any[]} tests
220
216
  * @param {any[]} afterTests
221
- * @param {Awaited<ReturnType<typeof import('../runOptions')['process']>>} tpOptions
217
+ * @param {import('../runOptions').RunnerOptions} tpOptions
222
218
  * @param {string} testPlanName
223
219
  * @param {string | null} testPlanId
224
220
  * @param {string} branch
@@ -276,7 +272,7 @@ class TestPlanRunner {
276
272
 
277
273
  /**
278
274
  * @private
279
- * @param {Awaited<ReturnType<typeof import('../runOptions')['process']>>} options
275
+ * @param {import('../runOptions').RunnerOptions} options
280
276
  * @param {string} branchToUse
281
277
  */
282
278
  async runTestPlans(options, branchToUse) {
@@ -347,7 +343,7 @@ class TestPlanRunner {
347
343
 
348
344
  /**
349
345
  * @private
350
- * @param {Awaited<ReturnType<typeof import('../runOptions')['process']>>} options
346
+ * @param {import('../runOptions').RunnerOptions} options
351
347
  * @param {string} branchToUse
352
348
  */
353
349
  async runAnonymousTestPlan(options, branchToUse) {
@@ -400,7 +396,7 @@ class TestPlanRunner {
400
396
  }
401
397
 
402
398
  /**
403
- * @param {Awaited<ReturnType<typeof import('../runOptions')['process']>>} options
399
+ * @param {import('../runOptions').RunnerOptions} options
404
400
  */
405
401
  async run(options) {
406
402
  const branchToUse = branchService.getCurrentBranch();
@@ -4,7 +4,7 @@ const _ = require('lodash');
4
4
  const Promise = require('bluebird');
5
5
 
6
6
  const { GridError, ArgError } = require('../errors');
7
- const { hasTestPlanFlag } = require('../utils');
7
+ const { hasTestPlanFlag, promiseMap } = require('../utils');
8
8
  const { gridMessages, gridTypes } = require('../commons/constants');
9
9
  const logger = require('../commons/logger').getLogger('grid-service');
10
10
  const servicesApi = require('../commons/testimServicesApi');
@@ -37,22 +37,22 @@ function extractHost(hostUrl) {
37
37
 
38
38
  function getSerializableObject(grid) {
39
39
  const host = grid && extractHost(grid.host);
40
- const port = grid && grid.port;
41
- const path = grid && grid.path;
40
+ const port = grid?.port;
41
+ const path = grid?.path;
42
42
  const protocol = grid && extractProtocol(grid);
43
- const accessToken = grid && grid.token;
44
- const slotId = grid && grid.slotId;
45
- const tunnel = grid && grid.hybrid && grid.hybrid.tunnel;
46
- const user = grid && grid.external && grid.external.user;
47
- const key = grid && grid.external && grid.external.key;
48
- const type = grid && grid.type;
43
+ const accessToken = grid?.token;
44
+ const slotId = grid?.slotId;
45
+ const tunnel = grid?.hybrid?.tunnel;
46
+ const user = grid?.external?.user;
47
+ const key = grid?.external?.key;
48
+ const type = grid?.type;
49
49
  const tunnelUser = type === gridTypes.HYBRID ?
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 name = grid && grid.name;
53
+ const name = grid?.name;
54
54
  const gridId = grid && (grid._id || grid.gridId);
55
- const provider = grid && grid.provider;
55
+ const provider = grid?.provider;
56
56
 
57
57
  return {
58
58
  host, port, path, protocol, accessToken, slotId, gridId, tunnel, user, key, type, name, provider, tunnelUser, tunnelKey,
@@ -97,14 +97,16 @@ function addItemToGridCache(workerId, companyId, gridId, slotId, browser) {
97
97
  gridCache[workerId] = { gridId, companyId, slotId, browser };
98
98
  }
99
99
 
100
- function getHostAndPortById(workerId, companyId, projectId, gridId, browser, executionId, options) {
101
- return handleGetGridResponse(projectId, companyId, workerId, browser, () => servicesApi.getGridById(companyId, projectId, gridId, browser, executionId));
100
+ function getHostAndPortById(workerId, companyId, projectId, gridId, browser, executionId) {
101
+ return handleGetGridResponse(projectId, companyId, workerId, browser, () =>
102
+ servicesApi.getGridById(companyId, projectId, gridId, browser, executionId),
103
+ );
102
104
  }
103
105
 
104
106
  function getHostAndPortByName(workerId, companyId, projectId, gridName, browser, executionId, options) {
105
107
  const get = () => {
106
- const grid = options.allGrids && options.allGrids.find(grid => (grid.name || '').toLowerCase() === gridName.toLowerCase());
107
- if (grid && grid._id) {
108
+ const grid = options.allGrids?.find(g => (g.name || '').toLowerCase() === gridName.toLowerCase());
109
+ if (grid?._id) {
108
110
  return servicesApi.getGridById(companyId, projectId, grid._id, browser, executionId);
109
111
  }
110
112
  return servicesApi.getGridByName(companyId, projectId, gridName, browser, executionId);
@@ -116,26 +118,22 @@ function getAllGrids(companyId) {
116
118
  return servicesApi.getAllGrids(companyId);
117
119
  }
118
120
 
119
- function getGridDataByGridId(companyId, gridId, allGrids) {
120
- return Promise.resolve(allGrids || getAllGrids(companyId))
121
- .then(grids => {
122
- const grid = grids.find(grid => grid._id === gridId);
123
- if (!grid) {
124
- throw new ArgError(`Failed to find grid id: ${gridId}`);
125
- }
126
- return getSerializableObject(grid);
127
- });
121
+ async function getGridDataByGridId(companyId, gridId, allGrids) {
122
+ const grids = await Promise.resolve(allGrids || getAllGrids(companyId));
123
+ const grid = grids.find(g => g._id === gridId);
124
+ if (!grid) {
125
+ throw new ArgError(`Failed to find grid id: ${gridId}`);
126
+ }
127
+ return getSerializableObject(grid);
128
128
  }
129
129
 
130
- function getGridDataByGridName(companyId, gridName, allGrids) {
131
- return Promise.resolve(allGrids || getAllGrids(companyId))
132
- .then(grids => {
133
- const grid = grids.find(grid => (grid.name || '').toLowerCase() === gridName.toLowerCase());
134
- if (!grid) {
135
- throw new ArgError(`Failed to find grid name: ${gridName}`);
136
- }
137
- return getSerializableObject(grid);
138
- });
130
+ async function getGridDataByGridName(companyId, gridName, allGrids) {
131
+ const grids = await Promise.resolve(allGrids || getAllGrids(companyId));
132
+ const grid = grids.find(g => (g.name || '').toLowerCase() === gridName.toLowerCase());
133
+ if (!grid) {
134
+ throw new ArgError(`Failed to find grid name: ${gridName}`);
135
+ }
136
+ return getSerializableObject(grid);
139
137
  }
140
138
 
141
139
  function releaseGridSlot(workerId, projectId) {
@@ -184,7 +182,7 @@ function releaseAllSlots(projectId) {
184
182
  }
185
183
 
186
184
  logger.warn('not all slots released before end runner flow', { projectId });
187
- return Promise.map(workerIds, workerId => releaseGridSlot(workerId, projectId))
185
+ return promiseMap(workerIds, workerId => releaseGridSlot(workerId, projectId))
188
186
  .catch(err => logger.error('failed to release all slots', { err, projectId }));
189
187
  }
190
188
 
@@ -266,20 +264,18 @@ async function getGridData(options) {
266
264
  throw new GridError('Missing host or grid configuration');
267
265
  }
268
266
 
269
- const getGridSlot = Promise.method(_getGridSlot);
270
-
271
- async function _getGridSlot(browser, executionId, testResultId, onGridSlot, options, workerId) {
272
- const getGridDataFromServer = () => {
267
+ async function getGridSlot(browser, executionId, testResultId, onGridSlot, options, workerId) {
268
+ const getGridDataFromServer = async () => {
273
269
  const { host, project, grid, gridId, useLocalChromeDriver, useChromeLauncher, company = {} } = options;
274
270
  const companyId = company.companyId;
275
271
  if (useLocalChromeDriver || useChromeLauncher) {
276
272
  return { mode: 'local' };
277
273
  }
278
274
  if (host) {
279
- return Promise.resolve(getOptionGrid(options));
275
+ return getOptionGrid(options);
280
276
  }
281
277
  if (gridId) {
282
- return getHostAndPortById(workerId, companyId, project, gridId, browser, executionId, options);
278
+ return getHostAndPortById(workerId, companyId, project, gridId, browser, executionId);
283
279
  }
284
280
  if (grid) {
285
281
  return getHostAndPortByName(workerId, companyId, project, grid, browser, executionId, options);
package/testRunStatus.js CHANGED
@@ -17,6 +17,7 @@ const { calculateCoverage } = require('./coverage/jsCoverage');
17
17
  const featureAvailabilityService = require('./commons/featureAvailabilityService');
18
18
  const featureFlags = require('./commons/featureFlags');
19
19
  const { mapFilesToLocalDrive } = require('./services/localRCASaver');
20
+ const { promiseMap } = require('./utils');
20
21
 
21
22
  const gitBranch = utils.getEnvironmentGitBranch();
22
23
  const gitCommit = process.env.GIT_COMMIT || process.env.CIRCLE_SHA1 || process.env.TRAVIS_COMMIT;
@@ -400,7 +401,7 @@ class RunStatus {
400
401
 
401
402
  const reportExecutionStarted = () => {
402
403
  const testResults = _.cloneDeep(this.testRunStatus);
403
- return Promise.map(Object.keys(testResults), testResultId => {
404
+ return promiseMap(Object.keys(testResults), testResultId => {
404
405
  const test = testResults[testResultId];
405
406
  const testData = test.config?.testData;
406
407
  const testId = test.testId;
@@ -498,10 +499,10 @@ class RunStatus {
498
499
  });
499
500
  }
500
501
 
501
- markAllQueuedTests(executionId, status, failureReason, success) {
502
+ async markAllQueuedTests(executionId, status, failureReason, success) {
502
503
  const queuedResultIds = Object.keys(this.testRunStatus).filter(resultId => this.getTestResult(resultId).status === 'QUEUED');
503
504
 
504
- return servicesApi.updateExecutionTests(
505
+ await servicesApi.updateExecutionTests(
505
506
  executionId,
506
507
  ['QUEUED'],
507
508
  status,
@@ -510,12 +511,14 @@ class RunStatus {
510
511
  this.startTime,
511
512
  null,
512
513
  this.options.project
513
- ).then(() => Promise.each(queuedResultIds, resultId => {
514
+ );
515
+ for (const resultId of queuedResultIds) {
514
516
  const test = this.getTestResult(resultId);
515
517
  test.status = status;
516
518
  test.failureReason = failureReason;
517
519
  test.success = success;
518
- })).then(() => this.testRunStatus);
520
+ }
521
+ return this.testRunStatus;
519
522
  }
520
523
  }
521
524
 
@@ -0,0 +1,86 @@
1
+ // @ts-check
2
+
3
+ 'use strict';
4
+
5
+ const { sessionType, testStatus: testStatusConst } = require('../commons/constants');
6
+
7
+
8
+ /**
9
+ * @typedef {import('../runOptions').Options} Options
10
+ * @typedef {import('../runOptions').InitModeOptions} InitModeOptions
11
+ * @typedef {import('../runOptions').LoginModeOptions} LoginModeOptions
12
+ * @typedef {import('../runOptions').TunnelOptions} TunnelOptions
13
+ * @typedef {import('../runOptions').NgrokTunnelOptions} NgrokTunnelOptions
14
+ * @typedef {import('../runOptions').TunnelDaemonOptions} TunnelDaemonOptions
15
+ * @typedef {import('../runOptions').RunnerOptions} RunnerOptions
16
+ */
17
+
18
+ /** @param {RunnerOptions} options */
19
+ function getSessionType(options) {
20
+ return options.files.length > 0 ? sessionType.CODEFUL : sessionType.CODELESS;
21
+ }
22
+
23
+ /**
24
+ * @param {RunnerOptions} options
25
+ * @param {{ runConfig: { browserValue: string } }[]} testList
26
+ */
27
+ function getUniqBrowsers(options, testList) {
28
+ if ((options.testConfigNames?.length || options.testConfigIds?.length || options.testPlan.length || options.testPlanIds.length) && !options.browser) {
29
+ return [...new Set(testList.map(t => t.runConfig.browserValue))];
30
+ }
31
+ return [options.browser?.toLowerCase()];
32
+ }
33
+
34
+ /** @param {RunnerOptions} options */
35
+ const hasTestPlanFlag = (options) => Boolean(options.testPlan?.length || options.testPlanIds?.length);
36
+
37
+ /** @param {RunnerOptions} options */
38
+ const isRemoteRun = (options) => options.resultId && options.source === 'remote-run';
39
+
40
+ /**
41
+ * @param {{ testStatus?: string }} test
42
+ * @param {RunnerOptions} options
43
+ */
44
+ const isQuarantineAndNotRemoteRun = (test, options) => test.testStatus === testStatusConst.QUARANTINE && !isRemoteRun(options) && !options.runQuarantinedTests;
45
+
46
+ function getArgsOnRemoteRunFailure() {
47
+ const { argv: args } = process;
48
+ if (!args.includes('--remoteRunId')) {
49
+ return undefined;
50
+ }
51
+ return {
52
+ remoteRunId: args[args.indexOf('--remoteRunId') + 1],
53
+ projectId: args[args.indexOf('--project') + 1],
54
+ token: args[args.indexOf('--token') + 1],
55
+ };
56
+ }
57
+
58
+
59
+ /** @type {(options: Options) => options is InitModeOptions} options */
60
+ // @ts-ignore should be `as any` etc
61
+ const isInitCodimMode = (options) => Boolean(options.initCodimMode);
62
+
63
+ /** @type {(options: Options) => options is LoginModeOptions} options */
64
+ // @ts-ignore should be `as any` etc
65
+ const isLoginMode = (options) => Boolean(options.loginMode);
66
+
67
+ /** @type {(options: Options) => options is TunnelOptions} options */
68
+ // @ts-ignore should be `as any` etc
69
+ const isTunnelOnlyMode = (options) => Boolean(options.tunnelOnlyMode);
70
+
71
+ /** @type {(options: Options) => options is RunnerOptions & { createPrefechedData: true; }} options */
72
+ // @ts-ignore should be `as any` etc
73
+ const isCreatePrefetchedDataMode = (options) => Boolean(options.createPrefechedData);
74
+
75
+ module.exports = {
76
+ getSessionType,
77
+ getUniqBrowsers,
78
+ hasTestPlanFlag,
79
+ isRemoteRun,
80
+ isQuarantineAndNotRemoteRun,
81
+ getArgsOnRemoteRunFailure,
82
+ isInitCodimMode,
83
+ isLoginMode,
84
+ isTunnelOnlyMode,
85
+ isCreatePrefetchedDataMode,
86
+ };
@@ -0,0 +1,32 @@
1
+ const chai = require('chai'); // eslint-disable-line import/no-extraneous-dependencies
2
+ const argsUtils = require('./argsUtils');
3
+
4
+ const expect = chai.expect;
5
+
6
+ describe('argsUtils', () => {
7
+ describe('getArgsOnRemoteRunFailure', () => {
8
+ let originalArgv;
9
+
10
+ beforeEach(() => {
11
+ originalArgv = process.argv;
12
+ });
13
+
14
+ afterEach(() => {
15
+ process.argv = originalArgv;
16
+ });
17
+
18
+ it('should return undefined if no remote run is current', () => {
19
+ process.argv = ['node', 'file.js', '--token', 'token', '--project', 'project-id'];
20
+ expect(argsUtils.getArgsOnRemoteRunFailure()).to.be.undefined;
21
+ });
22
+
23
+ it('should return details if remote run is current', () => {
24
+ process.argv = ['node', 'file.js', '--token', 'token', '--project', 'project-id', '--remoteRunId', 'remote-run-id'];
25
+ expect(argsUtils.getArgsOnRemoteRunFailure()).to.eql({
26
+ remoteRunId: 'remote-run-id',
27
+ projectId: 'project-id',
28
+ token: 'token',
29
+ });
30
+ });
31
+ });
32
+ });
@@ -0,0 +1,154 @@
1
+ // @ts-check
2
+
3
+ 'use strict';
4
+
5
+ const path = require('path');
6
+ const pRetry = require('p-retry');
7
+ const decompress = require('decompress');
8
+ const stringUtils = require('./stringUtils');
9
+ const httpRequest = require('../commons/httpRequest');
10
+ const { promises: fsPromises, createReadStream, createWriteStream, statSync } = require('fs');
11
+
12
+ const DOWNLOAD_RETRY = 3;
13
+
14
+ function getCliLocation() {
15
+ let cliLocation;
16
+ if (!require.main) { // we're in a REPL
17
+ return process.cwd(); // fall back on the current working directory
18
+ }
19
+ if (require.main.filename.includes('/src') || require.main.filename.includes('\\src') || process.env.IS_UNIT_TEST) {
20
+ cliLocation = path.resolve(__dirname, '../../');
21
+ } else {
22
+ cliLocation = path.resolve(__dirname, '../');
23
+ }
24
+
25
+ return cliLocation;
26
+ }
27
+
28
+ /**
29
+ * @param {string} url
30
+ */
31
+ const download = async (url) => pRetry(() => httpRequest.download(url), { retries: DOWNLOAD_RETRY });
32
+
33
+ /**
34
+ * @param {string} url
35
+ * @param {import('fs').PathLike} saveToLocation
36
+ */
37
+ const downloadAndSave = async (url, saveToLocation) => {
38
+ const res = await download(url);
39
+ return fsPromises.writeFile(saveToLocation, res.body);
40
+ };
41
+
42
+ /**
43
+ * @param {import('fs').PathLike} readFile
44
+ * @param {import('fs').PathLike} destFile
45
+ * @return {Promise<void>}
46
+ */
47
+ const copy = async (readFile, destFile) => new Promise((resolve, reject) => {
48
+ try {
49
+ // TODO: can this use fsPromises.copyFile?
50
+ const file = createWriteStream(destFile);
51
+ createReadStream(readFile).pipe(file);
52
+ file.on('finish', () => {
53
+ file.close(() => resolve());
54
+ });
55
+ } catch (err) {
56
+ reject(err);
57
+ }
58
+ });
59
+
60
+ /**
61
+ * @param {string} location
62
+ * @param {string=} fileName
63
+ */
64
+ function getSourcePath(location, fileName) {
65
+ if (stringUtils.isURL(location)) {
66
+ return fileName || path.join(process.cwd(), location.replace(/^.*[\\\/]/, ''));
67
+ }
68
+
69
+ return fileName || path.basename(location);
70
+ }
71
+
72
+ /**
73
+ * @param {string} location
74
+ * @param {string} fileName
75
+ */
76
+ const getSource = async (location, fileName) => {
77
+ const destFile = getSourcePath(location, fileName);
78
+ if (stringUtils.isURL(location)) {
79
+ return downloadAndSave(location, destFile);
80
+ }
81
+
82
+ return copy(location, destFile);
83
+ };
84
+
85
+ /**
86
+ * @param {string} location
87
+ */
88
+ const getSourceAsBuffer = async (location) => {
89
+ if (stringUtils.isURL(location)) {
90
+ return download(location);
91
+ }
92
+ return fsPromises.readFile(location);
93
+ };
94
+
95
+ /**
96
+ * @param {string} srcZipFile
97
+ * @param {string} destZipPath
98
+ */
99
+ const unzipFile = async (srcZipFile, destZipPath) => await decompress(srcZipFile, destZipPath);
100
+
101
+ /**
102
+ * @param {import('fs').PathLike} fileLocation
103
+ */
104
+ const getLocalFileSizeInMB = (fileLocation) => {
105
+ const stats = statSync(fileLocation);
106
+ const fileSizeInBytes = stats.size;
107
+ return fileSizeInBytes / 1000000;
108
+ };
109
+
110
+
111
+ function getRunnerVersion() {
112
+ try {
113
+ /** @type {import('../../package.json')} */
114
+ const pack = require(`${__dirname}/../package.json`); // eslint-disable-line import/no-dynamic-require
115
+ return pack.version;
116
+ } catch (err) {
117
+ return '';
118
+ }
119
+ }
120
+
121
+ function getEnginesVersion() {
122
+ try {
123
+ /** @type {import('../../package.json')} */
124
+ const pack = require(`${__dirname}/../package.json`); // eslint-disable-line import/no-dynamic-require
125
+ return pack.engines.node;
126
+ } catch (err) {
127
+ return '';
128
+ }
129
+ }
130
+
131
+ async function getEnginesVersionAsync() {
132
+ try {
133
+ /** @type {import('../../package.json')} */
134
+ const pack = JSON.parse(await fsPromises.readFile(`${__dirname}/../package.json`, 'utf8'));
135
+ return pack.engines.node;
136
+ } catch (err) {
137
+ return '';
138
+ }
139
+ }
140
+
141
+ module.exports = {
142
+ getCliLocation,
143
+ download,
144
+ downloadAndSave,
145
+ copy,
146
+ getSourcePath,
147
+ getSource,
148
+ getSourceAsBuffer,
149
+ unzipFile,
150
+ getLocalFileSizeInMB,
151
+ getRunnerVersion,
152
+ getEnginesVersion,
153
+ getEnginesVersionAsync,
154
+ };