@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.
- package/agent/routers/cliJsCode/service.js +11 -8
- 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 +6 -6
- package/cliAgentMode.js +11 -10
- package/codim/codim-cli.js +14 -9
- package/commons/featureFlags.js +29 -7
- package/commons/httpRequest.js +5 -1
- package/commons/httpRequestCounters.js +21 -10
- package/commons/initializeUserWithAuth.js +7 -4
- package/commons/lazyRequire.js +4 -3
- package/commons/preloadTests.js +6 -3
- package/commons/prepareRunner.js +7 -5
- package/commons/prepareRunnerAndTestimStartUtils.js +51 -45
- package/commons/runnerFileCache.js +10 -2
- package/commons/testimServicesApi.js +36 -5
- 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 +214 -471
- package/package.json +4 -3
- package/player/services/tabService.js +15 -1
- package/player/stepActions/apiStepAction.js +49 -43
- package/player/stepActions/baseCliJsStepAction.js +19 -14
- package/player/stepActions/baseJsStepAction.js +9 -8
- package/player/stepActions/dropFileStepAction.js +1 -3
- package/player/stepActions/inputFileStepAction.js +10 -8
- package/player/stepActions/locateStepAction.js +2 -0
- package/player/stepActions/mouseStepAction.js +21 -22
- package/player/stepActions/nodePackageStepAction.js +34 -35
- package/player/stepActions/stepAction.js +1 -0
- package/player/utils/imageCaptureUtils.js +133 -172
- package/player/utils/screenshotUtils.js +16 -13
- package/player/utils/windowUtils.js +20 -8
- package/player/webdriver.js +25 -22
- package/processHandler.js +4 -0
- package/reports/junitReporter.js +6 -7
- package/reports/reporter.js +34 -39
- package/runOptions.d.ts +286 -0
- package/runOptions.js +60 -45
- package/runner.js +64 -24
- package/runners/ParallelWorkerManager.js +12 -12
- package/runners/TestPlanRunner.js +14 -15
- package/runners/buildCodeTests.js +1 -0
- package/runners/runnerUtils.js +11 -2
- package/services/branchService.js +11 -5
- package/services/gridService.js +36 -40
- package/services/localRCASaver.js +4 -0
- 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/commons/preloadTests.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
const Promise = require('bluebird');
|
|
2
1
|
const _ = require('lodash');
|
|
3
2
|
const localRunnerCache = require('./runnerFileCache');
|
|
4
|
-
const servicesApi = require('./testimServicesApi
|
|
3
|
+
const servicesApi = require('./testimServicesApi');
|
|
4
|
+
const { promiseMap } = require('../utils');
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
const TEN_HOURS = 1000 * 60 * 60 * 10;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @param {import('../runOptions').RunnerOptions} options
|
|
11
|
+
*/
|
|
9
12
|
async function preloadTests(options) {
|
|
10
13
|
if (!Array.isArray(options.testId) || !options.testId.length) {
|
|
11
14
|
return {};
|
|
@@ -15,7 +18,7 @@ async function preloadTests(options) {
|
|
|
15
18
|
projectId: options.project,
|
|
16
19
|
};
|
|
17
20
|
return await localRunnerCache.memoize(async () => {
|
|
18
|
-
const results = await
|
|
21
|
+
const results = await promiseMap(options.testId, testId => servicesApi.loadTest({ ...opts, testId }), { concurrency: 2 });
|
|
19
22
|
return _.keyBy(results, 'testData.id');
|
|
20
23
|
}, 'loadTests', TEN_HOURS, [opts, options.testId])();
|
|
21
24
|
}
|
package/commons/prepareRunner.js
CHANGED
|
@@ -22,9 +22,10 @@ Promise.resolve().then(() => {
|
|
|
22
22
|
global.xhr2 = require('./xhr2'); // this is inside a `then` to not block and let network requests start
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
+
/** @param {import('../runOptions').RunnerOptions} options */
|
|
25
26
|
async function prepare(options) {
|
|
26
27
|
/**
|
|
27
|
-
* @type {Promise}
|
|
28
|
+
* @type {globalThis.Promise<void>}
|
|
28
29
|
*/
|
|
29
30
|
let chromedriverPromise = Promise.resolve();
|
|
30
31
|
|
|
@@ -35,7 +36,7 @@ async function prepare(options) {
|
|
|
35
36
|
chromedriverPromise = prepareRunnerAndTestimStartUtils.prepareChromeDriver(
|
|
36
37
|
{ projectId: options.project, userId: options.user },
|
|
37
38
|
{ chromeBinaryLocation: options.chromeBinaryLocation },
|
|
38
|
-
Boolean(options.lightweightMode
|
|
39
|
+
Boolean(options.lightweightMode?.general)
|
|
39
40
|
);
|
|
40
41
|
options.useLocalChromeDriver = true;
|
|
41
42
|
}
|
|
@@ -61,14 +62,15 @@ async function prepare(options) {
|
|
|
61
62
|
|
|
62
63
|
async function prepareMockNetwork(location) {
|
|
63
64
|
logger.info('prepare MockNetwork', { location });
|
|
64
|
-
const
|
|
65
|
+
const _rulesJson = await utils.getSourceAsBuffer(location);
|
|
66
|
+
const rulesJsonBuf = Buffer.isBuffer(_rulesJson) ? _rulesJson : _rulesJson.body;
|
|
65
67
|
if (rulesJsonBuf.byteLength > MAX_RULE_FILE_SIZE_IN_MB * 1000000) {
|
|
66
68
|
throw new Error(`${PREPARE_MOCK_NETWORK_ERROR_PREFIX} exceeded ${MAX_RULE_FILE_SIZE_IN_MB}MB`);
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
let rulesJson;
|
|
70
72
|
try {
|
|
71
|
-
rulesJson = JSON.parse(rulesJsonBuf);
|
|
73
|
+
rulesJson = JSON.parse(rulesJsonBuf.toString('utf-8'));
|
|
72
74
|
} catch (error) {
|
|
73
75
|
throw new Error(`${PREPARE_MOCK_NETWORK_ERROR_PREFIX} cannot be parsed.${os.EOL}${error}`);
|
|
74
76
|
}
|
|
@@ -76,7 +78,7 @@ async function prepareMockNetwork(location) {
|
|
|
76
78
|
const ajv = new Ajv();
|
|
77
79
|
const valid = ajv.validate(mockNetworkRuleFileSchema, rulesJson);
|
|
78
80
|
if (!valid) {
|
|
79
|
-
const errors = ajv.errors
|
|
81
|
+
const errors = ajv.errors?.map((e, i) => `${++i}) ${e.dataPath} ${e.message}`).join(os.EOL);
|
|
80
82
|
throw new Error(`${PREPARE_MOCK_NETWORK_ERROR_PREFIX} is malformed.${os.EOL}${errors}`);
|
|
81
83
|
}
|
|
82
84
|
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
// @ts-ignore
|
|
4
|
-
const Promise = require('bluebird');
|
|
5
3
|
const path = require('path');
|
|
6
|
-
const fs =
|
|
4
|
+
const fs = require('fs');
|
|
7
5
|
const ms = require('ms');
|
|
8
6
|
const { serializeError } = require('serialize-error');
|
|
9
7
|
const { additionalLogDetails } = require('./logUtils');
|
|
@@ -11,7 +9,15 @@ const { additionalLogDetails } = require('./logUtils');
|
|
|
11
9
|
const config = require('./config');
|
|
12
10
|
const { ArgError, NpmPermissionsError } = require('../errors');
|
|
13
11
|
const {
|
|
14
|
-
getCliLocation,
|
|
12
|
+
getCliLocation,
|
|
13
|
+
isURL,
|
|
14
|
+
downloadAndSave,
|
|
15
|
+
getSource,
|
|
16
|
+
getLocalFileSizeInMB,
|
|
17
|
+
download,
|
|
18
|
+
unzipFile,
|
|
19
|
+
getSourcePath,
|
|
20
|
+
promiseMap,
|
|
15
21
|
} = require('../utils');
|
|
16
22
|
const localRunnerCache = require('./runnerFileCache');
|
|
17
23
|
const logger = require('./logger').getLogger('prepare runner and testim start');
|
|
@@ -31,63 +37,58 @@ module.exports = {
|
|
|
31
37
|
/**
|
|
32
38
|
* @param {string} location
|
|
33
39
|
*/
|
|
34
|
-
function prepareCustomExtension(location, unlimitedSize = false) {
|
|
40
|
+
async function prepareCustomExtension(location, unlimitedSize = false) {
|
|
35
41
|
if (!location) {
|
|
36
|
-
return
|
|
42
|
+
return undefined;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
if (isURL(location)) {
|
|
40
46
|
const destFile = path.join(process.cwd(), location.replace(/^.*[\\\/]/, ''));
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
})
|
|
48
|
-
.then(() => destFile);
|
|
47
|
+
const contentLength = await getRemoteFileSizeInMB(location);
|
|
48
|
+
if (contentLength > MAX_CUSTOM_EXT_SIZE_MB && !unlimitedSize) {
|
|
49
|
+
throw new ArgError(MAX_CUSTOM_SIZE_ERROR_MSG);
|
|
50
|
+
}
|
|
51
|
+
await downloadAndSave(location, destFile);
|
|
52
|
+
return destFile;
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
const destFile = path.resolve(location);
|
|
52
56
|
if (!fs.existsSync(destFile)) {
|
|
53
|
-
|
|
57
|
+
throw new ArgError(`Failed to find custom extension in location: ${destFile}`);
|
|
54
58
|
}
|
|
55
59
|
const fileSize = getLocalFileSizeInMB(destFile);
|
|
56
60
|
if (fileSize > MAX_CUSTOM_EXT_SIZE_MB && !unlimitedSize) {
|
|
57
|
-
|
|
61
|
+
throw new ArgError(MAX_CUSTOM_SIZE_ERROR_MSG);
|
|
58
62
|
}
|
|
59
|
-
return
|
|
63
|
+
return destFile;
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
|
|
63
67
|
/**
|
|
64
68
|
* @param {string} url
|
|
65
69
|
*/
|
|
66
|
-
function getRemoteFileSizeInMB(url) {
|
|
70
|
+
async function getRemoteFileSizeInMB(url) {
|
|
67
71
|
const httpRequest = require('./httpRequest');
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
});
|
|
72
|
+
try {
|
|
73
|
+
const res = await httpRequest.head(url);
|
|
74
|
+
const contentLengthHeader = res.headers['content-length'];
|
|
75
|
+
const contentLengthBytes = contentLengthHeader ? parseInt(contentLengthHeader, 10) : 0;
|
|
76
|
+
return contentLengthBytes / 1000000;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
logger.warn('failed to download custom extension', { err });
|
|
79
|
+
throw new ArgError(`Failed to download custom extension from location: ${url}`);
|
|
80
|
+
}
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
/**
|
|
81
|
-
*
|
|
82
84
|
* @param {string[]} locations
|
|
83
|
-
*
|
|
84
85
|
*/
|
|
85
86
|
function prepareExtension(locations) {
|
|
86
87
|
logger.info('prepare extension', { locations });
|
|
87
88
|
|
|
88
89
|
const fullLocations = locations.map(location => ({ location, path: getSourcePath(location) }));
|
|
89
90
|
return localRunnerCache.memoize(
|
|
90
|
-
() =>
|
|
91
|
+
() => promiseMap(fullLocations, ({ location, path }) => getSource(location, path)),
|
|
91
92
|
'prepareExtension',
|
|
92
93
|
MSEC_IN_HALF_DAY,
|
|
93
94
|
fullLocations
|
|
@@ -131,25 +132,23 @@ async function prepareChromeDriver(userDetails = {}, driverOptions = {}, skipIsR
|
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
function getPlayerVersion() {
|
|
135
|
+
async function getPlayerVersion() {
|
|
135
136
|
const url = `${config.BLOB_URL}/extension/sessionPlayer_LATEST_RELEASE`;
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
const res = await download(url);
|
|
138
|
+
return res.body.toString('utf8');
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
/**
|
|
141
142
|
* @param {string} location
|
|
142
|
-
* @param {
|
|
143
|
-
*
|
|
144
|
-
* @returns {Promise<string>}
|
|
143
|
+
* @param {boolean=} canary
|
|
145
144
|
*/
|
|
146
|
-
function getPlayerLocation(location, canary) {
|
|
145
|
+
async function getPlayerLocation(location, canary) {
|
|
147
146
|
if (!isURL(location) || (isURL(location) && canary) || config.IS_ON_PREM) {
|
|
148
|
-
return
|
|
147
|
+
return location;
|
|
149
148
|
}
|
|
150
149
|
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
const ver = await getPlayerVersion();
|
|
151
|
+
return `${location}-${ver}`;
|
|
153
152
|
}
|
|
154
153
|
|
|
155
154
|
function getSessionPlayerFolder() {
|
|
@@ -178,13 +177,20 @@ async function downloadAndUnzip(loc, playerFileName, isRetry = false) {
|
|
|
178
177
|
}
|
|
179
178
|
}
|
|
180
179
|
|
|
181
|
-
|
|
180
|
+
/**
|
|
181
|
+
* @param {string} location
|
|
182
|
+
* @param {boolean=} canary
|
|
183
|
+
*/
|
|
184
|
+
async function preparePlayer(location, canary) {
|
|
182
185
|
logger.info('prepare player', { location, canary });
|
|
183
186
|
const playerFileName = getPlayerDestination();
|
|
184
187
|
return localRunnerCache.memoize(
|
|
185
|
-
() =>
|
|
186
|
-
|
|
187
|
-
|
|
188
|
+
async () => {
|
|
189
|
+
const loc = await getPlayerLocation(location, canary);
|
|
190
|
+
await downloadAndUnzip(loc, playerFileName);
|
|
191
|
+
// return a truthy value, so the caching has a value, and doesn't ignore
|
|
192
|
+
return {};
|
|
193
|
+
},
|
|
188
194
|
'preparePlayer',
|
|
189
195
|
MSEC_IN_HALF_DAY,
|
|
190
196
|
[location, canary, playerFileName]
|
|
@@ -79,7 +79,15 @@ function decrypt(key, buffer) {
|
|
|
79
79
|
const decrypted = decipher.update(encryptedText);
|
|
80
80
|
return JSON.parse(Buffer.concat([decrypted, decipher.final()]));
|
|
81
81
|
}
|
|
82
|
-
|
|
82
|
+
/**
|
|
83
|
+
* argument-less memoize for functions with a global cache name
|
|
84
|
+
* @template T
|
|
85
|
+
* @param {() => PromiseLike<T>} fn
|
|
86
|
+
* @param {string} fnName
|
|
87
|
+
* @param {number=} duration
|
|
88
|
+
* @param {*} parameters
|
|
89
|
+
* @returns {() => Promise<Awaited<T>>}
|
|
90
|
+
*/
|
|
83
91
|
function memoize(fn, fnName, duration = THREE_HOURS, parameters = undefined) {
|
|
84
92
|
return Promise.method(async () => {
|
|
85
93
|
if (!cacheEnabled) {
|
|
@@ -96,7 +104,7 @@ function memoize(fn, fnName, duration = THREE_HOURS, parameters = undefined) {
|
|
|
96
104
|
}
|
|
97
105
|
logger.debug('cache miss:', { fnName });
|
|
98
106
|
if (!cacheMissAllowed) {
|
|
99
|
-
throw new Error(`
|
|
107
|
+
throw new Error(`Attempted to rebuild cache for ${originalFnName}. cache miss is not allowed with current run configuration`);
|
|
100
108
|
}
|
|
101
109
|
const value = await fn();
|
|
102
110
|
if (value) {
|
|
@@ -192,6 +192,27 @@ async function getTestPlanTestList(projectId, names, planIds, branch, intersecti
|
|
|
192
192
|
}), { retries: DEFAULT_REQUEST_RETRY });
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
/**
|
|
196
|
+
* @param {{
|
|
197
|
+
* projectId: string;
|
|
198
|
+
* labels: string[];
|
|
199
|
+
* testIds: string[];
|
|
200
|
+
* testNames: string[];
|
|
201
|
+
* testConfigNames?: string[];
|
|
202
|
+
* suiteNames: string[];
|
|
203
|
+
* suiteIds: string[];
|
|
204
|
+
* branch: string;
|
|
205
|
+
* rerunFailedByRunId?: string;
|
|
206
|
+
* testConfigIds?: string[];
|
|
207
|
+
* intersections: import('../runOptions').RunnerOptions['intersections'];
|
|
208
|
+
* }} param0
|
|
209
|
+
* @returns {globalThis.Promise<{
|
|
210
|
+
* tests: any[][];
|
|
211
|
+
* branch?: string;
|
|
212
|
+
* runName?: string;
|
|
213
|
+
* runExists?: boolean;
|
|
214
|
+
* }>}
|
|
215
|
+
*/
|
|
195
216
|
function getSuiteTestList({
|
|
196
217
|
projectId, labels, testIds, testNames, testConfigNames, suiteNames, suiteIds, branch, rerunFailedByRunId, testConfigIds, intersections,
|
|
197
218
|
}) {
|
|
@@ -263,7 +284,17 @@ function getGridById(companyId, projectId, gridId, browser, executionId) {
|
|
|
263
284
|
return pRetry(() => getWithAuth(`/grid/${gridId}`, { companyId, projectId, browser, executionId, reqId: utils.guid() }), { retries: DEFAULT_REQUEST_RETRY });
|
|
264
285
|
}
|
|
265
286
|
|
|
266
|
-
|
|
287
|
+
/**
|
|
288
|
+
* @param {{ projectId: string; token: string; branchName: string; lightweightMode?: import('../runOptions').LightweightSettings; localGrid: boolean; }} param0
|
|
289
|
+
* @returns {globalThis.Promise<{
|
|
290
|
+
* authData: { token: string; refreshToken: string; uid: string; ngrokToken?: string; isNgrokWhitelisted?: boolean; };
|
|
291
|
+
* editorConfig: { editorUrl: string };
|
|
292
|
+
* companyByProjectId: Awaited<ReturnType<import('services/src/company/companyService')['getCompanyByProjectIdUsingCache']>>;
|
|
293
|
+
* projectById: NonNullable<Awaited<ReturnType<import('services/src/commons/testimCache')['project']['getById']>>>;
|
|
294
|
+
* allGrids: import('services/src/commons/mongo/models/grid').RawGrid[];
|
|
295
|
+
* branchName: string | import('services/src/commons/mongo/models/branchesData').RawBranchesData | null;
|
|
296
|
+
* }>}
|
|
297
|
+
*/
|
|
267
298
|
async function initializeUserWithAuth({ projectId, token, branchName, lightweightMode, localGrid }) {
|
|
268
299
|
try {
|
|
269
300
|
return await pRetry(() => httpRequest.post({
|
|
@@ -278,14 +309,14 @@ async function initializeUserWithAuth({ projectId, token, branchName, lightweigh
|
|
|
278
309
|
}), { retries: DEFAULT_REQUEST_RETRY });
|
|
279
310
|
} catch (e) {
|
|
280
311
|
logger.error('error initializing info from server', e);
|
|
281
|
-
if (e
|
|
312
|
+
if (e?.message?.includes('Bad Request')) {
|
|
282
313
|
throw new ArgError(
|
|
283
314
|
'Error trying to retrieve CLI token. ' +
|
|
284
315
|
'Your CLI token and project might not match. ' +
|
|
285
316
|
'Please make sure to pass `--project` and `--token` that' +
|
|
286
317
|
' match to each other or make sure they match in your ~/.testim file.');
|
|
287
318
|
}
|
|
288
|
-
if (e
|
|
319
|
+
if (e?.code?.includes('ENOTFOUND')) {
|
|
289
320
|
throw new ArgError('Due to network connectivity issues, Testim CLI has been unable to connect to the Testim backend.');
|
|
290
321
|
}
|
|
291
322
|
throw e;
|
|
@@ -415,7 +446,7 @@ const updateTestDataArtifact = _.memoize(async (projectId, testId, testResultId,
|
|
|
415
446
|
}
|
|
416
447
|
const removeHiddenParamsInTestData = () => {
|
|
417
448
|
const testDataValueClone = _.clone(testData);
|
|
418
|
-
if (projectDefaults
|
|
449
|
+
if (projectDefaults?.hiddenParams) {
|
|
419
450
|
const { hiddenParams } = projectDefaults;
|
|
420
451
|
(hiddenParams || []).forEach((param) => {
|
|
421
452
|
if (testDataValueClone[param]) {
|
|
@@ -521,7 +552,7 @@ module.exports = {
|
|
|
521
552
|
getLabFeaturesByProjectId,
|
|
522
553
|
uploadRunDataArtifact: Promise.method(uploadRunDataArtifact),
|
|
523
554
|
updateTestDataArtifact: Promise.method(updateTestDataArtifact),
|
|
524
|
-
initializeUserWithAuth
|
|
555
|
+
initializeUserWithAuth,
|
|
525
556
|
addTestRetry,
|
|
526
557
|
getHybridGridProvider,
|
|
527
558
|
loadSfdcCredential,
|
|
@@ -66,7 +66,8 @@ describe('testimTunnel', () => {
|
|
|
66
66
|
|
|
67
67
|
it('should handle connect errors', async () => {
|
|
68
68
|
ltConnectStub.rejects('error');
|
|
69
|
-
|
|
69
|
+
const connectPromise = testimTunnel.connect({ tunnel: true, gridData: { type: 'testimLambdaTest', tunnel: 'lambdatest' } });
|
|
70
|
+
await expect(connectPromise).to.be.rejectedWith('Failed to start tunnel. Please contact support@testim.io');
|
|
70
71
|
});
|
|
71
72
|
});
|
|
72
73
|
|
package/coverage/jsCoverage.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
3
|
+
const servicesApi = require('../commons/testimServicesApi');
|
|
4
|
+
const Promise = require('bluebird');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const fsPromises = require('fs/promises');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const mkdirp = require('mkdirp');
|
|
9
9
|
const lazyRequire = require('../commons/lazyRequire');
|
|
10
10
|
const libReport = require('istanbul-lib-report');
|
|
11
11
|
const reports = require('istanbul-reports');
|
|
12
12
|
const SummaryToObjectReport = require('./SummaryToObjectReport');
|
|
13
|
-
const { ArgError } = require('../errors');
|
|
14
13
|
const ora = require('ora');
|
|
15
14
|
const moment = require('moment');
|
|
16
15
|
const TestExclude = require('test-exclude');
|
|
16
|
+
const { ArgError } = require('../errors');
|
|
17
|
+
const { promiseMap } = require('../utils');
|
|
17
18
|
|
|
18
19
|
const logger = require('../commons/logger').getLogger('test-run-status');
|
|
19
20
|
|
|
@@ -77,7 +78,7 @@ module.exports.remapCoverage = async (options, storagePath, sourceMaps) => {
|
|
|
77
78
|
}
|
|
78
79
|
const parsedPath = rewritePath(storagePath, sourcePath);
|
|
79
80
|
await mkdirp(path.parse(parsedPath).dir);
|
|
80
|
-
await
|
|
81
|
+
await fsPromises.writeFile(parsedPath, sourceMapObject.sourcesContent[index]);
|
|
81
82
|
}));
|
|
82
83
|
}));
|
|
83
84
|
};
|
|
@@ -173,11 +174,11 @@ const collectAndMergeJsCoverageData = async (projectId, branch, runId) => {
|
|
|
173
174
|
const realDataRes = await servicesApi.getRealData(projectId, 'testResult', `runId=${runId}`);
|
|
174
175
|
const testResults = realDataRes.data.docs;
|
|
175
176
|
|
|
176
|
-
await
|
|
177
|
-
|
|
177
|
+
await promiseMap(
|
|
178
|
+
testResults.flatMap((testResult) => testResult.JSCoverageURLS || []),
|
|
178
179
|
async (coverageURL) => {
|
|
179
180
|
const data = await servicesApi.getS3Artifact(coverageURL, 90000);
|
|
180
|
-
await
|
|
181
|
+
await promiseMap(data, async (cov) => {
|
|
181
182
|
if (!covUrlMap.has(cov.url)) {
|
|
182
183
|
let text = cov.text;
|
|
183
184
|
if (cov.sourceUrl) {
|
|
@@ -195,7 +196,8 @@ const collectAndMergeJsCoverageData = async (projectId, branch, runId) => {
|
|
|
195
196
|
delete cov.text;
|
|
196
197
|
mergedCoverages = mergeProcessCovs([mergedCoverages, { result: [cov] }]);
|
|
197
198
|
});
|
|
198
|
-
},
|
|
199
|
+
},
|
|
200
|
+
{ concurrency: DOWNLOAD_COVERAGE_DATA_CONCURRENCY }
|
|
199
201
|
);
|
|
200
202
|
|
|
201
203
|
return { covUrlMap, mergedCoverages };
|
package/inputFileUtils.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const httpRequest = require('./commons/httpRequest');
|
|
4
|
-
const
|
|
5
|
-
const fs = Promise.promisifyAll(require('fs'));
|
|
4
|
+
const fsPromises = require('fs/promises');
|
|
6
5
|
const os = require('os');
|
|
7
|
-
const
|
|
6
|
+
const { promiseMap } = require('./utils');
|
|
7
|
+
const { getLogger } = require('./commons/logger');
|
|
8
|
+
|
|
9
|
+
const logger = getLogger('input-file-utils');
|
|
8
10
|
|
|
9
11
|
function getVisibleElementScript(positionAndSize = {
|
|
10
12
|
width: '2px', height: '2px', left: '0px', top: '400px',
|
|
@@ -77,22 +79,22 @@ async function downloadFile(fileUrl, fileName) {
|
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
const localFileLocation = `${os.tmpdir()}/${fileName}`;
|
|
80
|
-
await
|
|
82
|
+
await fsPromises.writeFile(localFileLocation, body);
|
|
81
83
|
return localFileLocation;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
function downloadFiles(fileUrls) {
|
|
85
|
-
return
|
|
87
|
+
return promiseMap(fileUrls, file => downloadFile(file.url, file.name));
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
function uploadFileToGrid(localFileLocation, uploadFileFn) {
|
|
89
91
|
return uploadFileFn(localFileLocation);
|
|
90
92
|
}
|
|
91
93
|
|
|
92
|
-
function downloadFilesAndUploadToGrid(fileUrls, uploadFileFn) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
async function downloadFilesAndUploadToGrid(fileUrls, uploadFileFn) {
|
|
95
|
+
const filesOnDisk = await downloadFiles(fileUrls);
|
|
96
|
+
const gridLocalFiles = await promiseMap(filesOnDisk, localFileLocation => uploadFileToGrid(localFileLocation, uploadFileFn));
|
|
97
|
+
return Array.isArray(gridLocalFiles) && gridLocalFiles.map(gridLocalFile => gridLocalFile?.value);
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
module.exports = {
|