@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.
- package/agent/routers/codim/router.test.js +9 -12
- package/agent/routers/codim/service.js +16 -16
- package/agent/routers/playground/service.js +5 -7
- package/cli.js +4 -4
- package/cliAgentMode.js +4 -3
- package/codim/codim-cli.js +10 -8
- package/commons/featureFlags.js +8 -0
- package/commons/httpRequest.js +5 -1
- package/commons/httpRequestCounters.js +21 -10
- package/commons/lazyRequire.js +4 -3
- package/commons/preloadTests.js +2 -2
- package/commons/prepareRunner.js +4 -2
- package/commons/prepareRunnerAndTestimStartUtils.js +40 -41
- package/commons/runnerFileCache.js +1 -1
- package/commons/testimTunnel.test.js +2 -1
- package/coverage/SummaryToObjectReport.js +0 -1
- package/coverage/jsCoverage.js +12 -10
- package/inputFileUtils.js +11 -9
- package/npm-shrinkwrap.json +187 -444
- package/package.json +4 -3
- package/player/services/tabService.js +15 -1
- package/player/stepActions/locateStepAction.js +2 -0
- package/player/utils/imageCaptureUtils.js +81 -120
- package/player/webdriver.js +25 -22
- package/reports/junitReporter.js +6 -7
- package/reports/reporter.js +34 -39
- package/runOptions.d.ts +260 -0
- package/runOptions.js +53 -38
- package/runner.js +2 -1
- package/runners/ParallelWorkerManager.js +9 -10
- package/runners/TestPlanRunner.js +5 -9
- package/services/gridService.js +36 -40
- package/testRunStatus.js +8 -5
- package/utils/argsUtils.js +86 -0
- package/utils/argsUtils.test.js +32 -0
- package/utils/fsUtils.js +154 -0
- package/utils/index.js +10 -161
- package/utils/promiseUtils.js +13 -2
- package/utils/stringUtils.js +4 -2
- package/utils/stringUtils.test.js +22 -0
- package/utils/timeUtils.js +25 -0
- package/utils/utils.test.js +0 -41
- 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 {
|
|
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
|
|
65
|
-
const companyName = options.company
|
|
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
|
|
69
|
-
const isStartUp = options.company
|
|
70
|
-
const projectName = options.projectData
|
|
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
|
|
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 ||
|
|
119
|
-
update.gridType = options.gridData
|
|
120
|
-
update.gridProvider = options.gridData
|
|
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
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
399
|
+
* @param {import('../runOptions').RunnerOptions} options
|
|
404
400
|
*/
|
|
405
401
|
async run(options) {
|
|
406
402
|
const branchToUse = branchService.getCurrentBranch();
|
package/services/gridService.js
CHANGED
|
@@ -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
|
|
41
|
-
const path = grid
|
|
40
|
+
const port = grid?.port;
|
|
41
|
+
const path = grid?.path;
|
|
42
42
|
const protocol = grid && extractProtocol(grid);
|
|
43
|
-
const accessToken = grid
|
|
44
|
-
const slotId = grid
|
|
45
|
-
const tunnel = grid
|
|
46
|
-
const user = grid
|
|
47
|
-
const key = grid
|
|
48
|
-
const type = grid
|
|
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
|
|
53
|
+
const name = grid?.name;
|
|
54
54
|
const gridId = grid && (grid._id || grid.gridId);
|
|
55
|
-
const provider = grid
|
|
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
|
|
101
|
-
return handleGetGridResponse(projectId, companyId, workerId, browser, () =>
|
|
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
|
|
107
|
-
if (grid
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
275
|
+
return getOptionGrid(options);
|
|
280
276
|
}
|
|
281
277
|
if (gridId) {
|
|
282
|
-
return getHostAndPortById(workerId, companyId, project, gridId, browser, executionId
|
|
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
|
|
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
|
-
|
|
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
|
-
)
|
|
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
|
-
}
|
|
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
|
+
});
|
package/utils/fsUtils.js
ADDED
|
@@ -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
|
+
};
|