@testim/testim-cli 3.232.0 → 3.235.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 +4 -38
- package/cli.js +6 -0
- package/commons/httpRequest.js +4 -3
- package/commons/testimCloudflare.js +76 -0
- package/commons/testimServicesApi.js +34 -0
- package/commons/testimTunnel.js +18 -2
- package/npm-shrinkwrap.json +205 -172
- package/package.json +1 -2
- package/player/WebDriverHttpRequest.js +1 -1
- package/processHandler.js +9 -4
- package/runOptions.js +90 -70
- package/runner.js +1 -1
- package/runners/TestPlanRunner.js +7 -4
- package/services/gridService.js +1 -1
- package/services/lambdatestService.js +5 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const
|
|
4
|
+
const os = require('os');
|
|
5
5
|
const dataUriToBuffer = require('data-uri-to-buffer');
|
|
6
6
|
const { spawn: threadSpawn, config } = require('threads');
|
|
7
7
|
const Promise = require('bluebird');
|
|
@@ -20,8 +20,6 @@ config.set({
|
|
|
20
20
|
},
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
const transactions = {};
|
|
24
|
-
|
|
25
23
|
function convertWindowsBackslash(input) {
|
|
26
24
|
const isExtendedLengthPath = /^\\\\\?\\/.test(input);
|
|
27
25
|
const hasNonAscii = /[^\u0000-\u0080]+/.test(input); // eslint-disable-line no-control-regex
|
|
@@ -549,35 +547,13 @@ function removeFolder(installFolder) {
|
|
|
549
547
|
}));
|
|
550
548
|
}
|
|
551
549
|
|
|
552
|
-
function cleanPackages(transactionId) {
|
|
553
|
-
function cleanInstallFolder() {
|
|
554
|
-
const { installFolder } = transactions[transactionId];
|
|
555
|
-
if (!installFolder) {
|
|
556
|
-
return Promise.resolve();
|
|
557
|
-
}
|
|
558
|
-
return removeFolder(installFolder);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
if (!transactions[transactionId]) {
|
|
562
|
-
return Promise.resolve();
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
return cleanInstallFolder()
|
|
566
|
-
.finally(() => delete transactions[transactionId]);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
550
|
function getTransactionId(stepResultId, testResultId, stepId, retryIndex) {
|
|
570
551
|
return `${testResultId}_${stepId}_${stepResultId}_${retryIndex}`;
|
|
571
552
|
}
|
|
572
553
|
|
|
573
554
|
function installPackage(stepId, testResultId, retryIndex, packageData, stepResultId, timeout) {
|
|
574
555
|
const transactionId = getTransactionId(stepResultId, testResultId, stepId, retryIndex);
|
|
575
|
-
return runNpmInstall(transactionId, packageData, timeout)
|
|
576
|
-
.then(({ data, installFolder }) => {
|
|
577
|
-
transactions[transactionId] = transactions[transactionId] || {};
|
|
578
|
-
transactions[transactionId].installFolder = installFolder;
|
|
579
|
-
return data;
|
|
580
|
-
});
|
|
556
|
+
return runNpmInstall(transactionId, packageData, timeout).then(({ data }) => data);
|
|
581
557
|
}
|
|
582
558
|
|
|
583
559
|
function runCodeWithPackages(code, stepId, incomingParams, context, testResultId, retryIndex, stepResultId, timeout, fileDataUrl, s3filepath) {
|
|
@@ -608,8 +584,7 @@ function runCodeWithPackages(code, stepId, incomingParams, context, testResultId
|
|
|
608
584
|
return runCodeWithWorkerThread(transactionId, incomingParams, context, code, packageLocalLocations, timeout, fileDataUrl);
|
|
609
585
|
}
|
|
610
586
|
return runCode(transactionId, incomingParams, context, code, packageLocalLocations, timeout, fileDataUrl);
|
|
611
|
-
}).then(res => Object.assign({}, res, { nodeVersion: process.version }))
|
|
612
|
-
.finally(() => cleanPackages(transactionId));
|
|
587
|
+
}).then(res => Object.assign({}, res, { nodeVersion: process.version }));
|
|
613
588
|
}
|
|
614
589
|
|
|
615
590
|
function runNpmInstall(transactionId, packageData, timeout) {
|
|
@@ -653,16 +628,7 @@ function runNpmInstall(transactionId, packageData, timeout) {
|
|
|
653
628
|
}
|
|
654
629
|
|
|
655
630
|
function getLocalPackageInstallFolder() {
|
|
656
|
-
|
|
657
|
-
try {
|
|
658
|
-
return findRoot(process.cwd());
|
|
659
|
-
} catch (err) {
|
|
660
|
-
return process.cwd();
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
const root = getRoot();
|
|
665
|
-
return path.join(root, '/testim_local_packages');
|
|
631
|
+
return path.join(os.tmpdir(), '/testim_local_packages');
|
|
666
632
|
}
|
|
667
633
|
|
|
668
634
|
function cleanLocalPackageInstallFolder() {
|
package/cli.js
CHANGED
|
@@ -94,6 +94,12 @@ function main() {
|
|
|
94
94
|
return undefined;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
if (options.tunnelOnlyMode) {
|
|
98
|
+
await testRunner.init(options);
|
|
99
|
+
await require('./commons/testimTunnel').serveTunneling(options);
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
97
103
|
if (agentMode.shouldStartAgentMode(options)) {
|
|
98
104
|
return agentMode.runAgentMode(options);
|
|
99
105
|
}
|
package/commons/httpRequest.js
CHANGED
|
@@ -37,8 +37,8 @@ const logErrorAndRethrow = (logMsg, data) => err => {
|
|
|
37
37
|
throw err;
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
function deleteMethod(url, headers, timeout) {
|
|
41
|
-
return deleteFullRes(url, headers, timeout)
|
|
40
|
+
function deleteMethod(url, body, headers, timeout) {
|
|
41
|
+
return deleteFullRes(url, body, headers, timeout)
|
|
42
42
|
.then(res => {
|
|
43
43
|
if (res.type === 'text/plain') {
|
|
44
44
|
return res.text;
|
|
@@ -48,9 +48,10 @@ function deleteMethod(url, headers, timeout) {
|
|
|
48
48
|
.catch(logErrorAndRethrow('failed to delete request', { url }));
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function deleteFullRes(url, headers = {}, timeout = DEFAULT_REQUEST_TIMEOUT) {
|
|
51
|
+
function deleteFullRes(url, body = {}, headers = {}, timeout = DEFAULT_REQUEST_TIMEOUT) {
|
|
52
52
|
const request = superagent
|
|
53
53
|
.delete(url)
|
|
54
|
+
.send(body)
|
|
54
55
|
.timeout(timeout)
|
|
55
56
|
.set(headers);
|
|
56
57
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
const fse = require('fs-extra');
|
|
7
|
+
const { downloadAndSave, unzipFile } = require('../utils');
|
|
8
|
+
const servicesApi = require('./testimServicesApi.js');
|
|
9
|
+
|
|
10
|
+
const TUNNEL_BINARY_ORIGIN = 'https://github.com/cloudflare/cloudflared/releases/download/2022.4.1/';
|
|
11
|
+
const TUNNEL_BINARY_PATHNAME = {
|
|
12
|
+
win32ia32: { path: 'cloudflared-windows-386.exe' },
|
|
13
|
+
win32x64: { path: 'cloudflared-windows-amd64.exe' },
|
|
14
|
+
darwinx64: { path: 'cloudflared-darwin-amd64.tgz', extract: true },
|
|
15
|
+
linuxia32: { path: 'cloudflared-linux-386' },
|
|
16
|
+
linuxx64: { path: 'cloudflared-linux-amd64' },
|
|
17
|
+
};
|
|
18
|
+
const TUNNEL_BINARY_DIRECTORY = os.tmpdir();
|
|
19
|
+
const TUNNEL_BINARY_LOCATION = `${TUNNEL_BINARY_DIRECTORY}/cloudflared`;
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
let tunnelId = null;
|
|
23
|
+
let tunnelProcess = null;
|
|
24
|
+
|
|
25
|
+
async function prepareTunnel() {
|
|
26
|
+
const isBinaryExist = await fse.pathExists(TUNNEL_BINARY_LOCATION);
|
|
27
|
+
if (isBinaryExist) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const downloadUrl = TUNNEL_BINARY_PATHNAME[os.platform() + os.arch()];
|
|
32
|
+
if (!downloadUrl) {
|
|
33
|
+
throw new Error(`tunnel on ${os.platform() + os.arch()} platform is not supported.`);
|
|
34
|
+
}
|
|
35
|
+
const destination = downloadUrl.extract ? TUNNEL_BINARY_DIRECTORY + downloadUrl.path : TUNNEL_BINARY_LOCATION;
|
|
36
|
+
await downloadAndSave(`${TUNNEL_BINARY_ORIGIN}/${downloadUrl.path}`, destination);
|
|
37
|
+
if (downloadUrl.extract) {
|
|
38
|
+
await unzipFile(destination, TUNNEL_BINARY_DIRECTORY);
|
|
39
|
+
}
|
|
40
|
+
await fse.chmodSync(TUNNEL_BINARY_LOCATION, '755');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const connectTunnel = async (options) => {
|
|
44
|
+
const [result] = await Promise.all([
|
|
45
|
+
servicesApi.getCloudflareTunnel(options.company.companyId, options.tunnelRoutes),
|
|
46
|
+
prepareTunnel(),
|
|
47
|
+
]);
|
|
48
|
+
tunnelId = result._id;
|
|
49
|
+
tunnelProcess = spawn(TUNNEL_BINARY_LOCATION, ['tunnel', '--no-autoupdate', 'run', '--force', '--token', result.token], { stdio: 'inherit' });
|
|
50
|
+
await servicesApi.forceUpdateCloudflareTunnelRoutes(options.company.companyId, tunnelId);
|
|
51
|
+
await fse.writeFileSync(options.tunnelRoutesOutput, JSON.stringify(result.routesMapping, null, 2));
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const disconnectTunnel = async (options) => {
|
|
55
|
+
const promises = [];
|
|
56
|
+
if (tunnelId) {
|
|
57
|
+
promises.push(servicesApi.deleteCloudflareTunnel(options.company.companyId, tunnelId));
|
|
58
|
+
}
|
|
59
|
+
if (tunnelProcess) {
|
|
60
|
+
promises.push(new Promise((resolve, reject) => {
|
|
61
|
+
tunnelProcess.on('close', (code) => {
|
|
62
|
+
if (code) {
|
|
63
|
+
reject();
|
|
64
|
+
}
|
|
65
|
+
resolve();
|
|
66
|
+
});
|
|
67
|
+
tunnelProcess.kill();
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
return await Promise.all(promises);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
connectTunnel,
|
|
75
|
+
disconnectTunnel,
|
|
76
|
+
};
|
|
@@ -55,6 +55,11 @@ function putAuth(url, body) {
|
|
|
55
55
|
.then(headers => httpRequest.put(`${config.SERVICES_HOST}${url || ''}`, body, headers));
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
function deleteAuth(url, body) {
|
|
59
|
+
return getTokenHeader()
|
|
60
|
+
.then(headers => httpRequest.delete(`${config.SERVICES_HOST}${url || ''}`, body, headers));
|
|
61
|
+
}
|
|
62
|
+
|
|
58
63
|
function getWithAuth(url, query, options, timeout) {
|
|
59
64
|
return getTokenHeader()
|
|
60
65
|
.then(headers => httpRequest.get(`${config.SERVICES_HOST}${url || ''}`, query, headers, timeout, options));
|
|
@@ -444,6 +449,32 @@ function getApplitoolsIntegrationData(projectId) {
|
|
|
444
449
|
}
|
|
445
450
|
}
|
|
446
451
|
|
|
452
|
+
|
|
453
|
+
function getCloudflareTunnel(companyId, routes) {
|
|
454
|
+
try {
|
|
455
|
+
return putAuth('/tunnel', { companyId, routes });
|
|
456
|
+
} catch (err) {
|
|
457
|
+
logger.warn('could\'nt get tunnel.', { err });
|
|
458
|
+
return {};
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
function forceUpdateCloudflareTunnelRoutes(companyId, tunnelId) {
|
|
462
|
+
try {
|
|
463
|
+
return postAuth({ url: `/tunnel/${tunnelId}`, body: { companyId } });
|
|
464
|
+
} catch (err) {
|
|
465
|
+
logger.warn('could\'nt get tunnel.', { err });
|
|
466
|
+
return {};
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function deleteCloudflareTunnel(companyId, tunnelId) {
|
|
470
|
+
try {
|
|
471
|
+
return deleteAuth(`/tunnel/${tunnelId}`, { companyId });
|
|
472
|
+
} catch (err) {
|
|
473
|
+
logger.warn('could\'nt get tunnel.', { err });
|
|
474
|
+
return {};
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
447
478
|
module.exports = {
|
|
448
479
|
getS3Artifact,
|
|
449
480
|
getTestPlan,
|
|
@@ -476,4 +507,7 @@ module.exports = {
|
|
|
476
507
|
loadTest,
|
|
477
508
|
isTestResultCompleted,
|
|
478
509
|
getApplitoolsIntegrationData,
|
|
510
|
+
getCloudflareTunnel,
|
|
511
|
+
forceUpdateCloudflareTunnelRoutes,
|
|
512
|
+
deleteCloudflareTunnel,
|
|
479
513
|
};
|
package/commons/testimTunnel.js
CHANGED
|
@@ -3,22 +3,29 @@
|
|
|
3
3
|
const ora = require('ora');
|
|
4
4
|
|
|
5
5
|
const LambdatestService = require('../services/lambdatestService');
|
|
6
|
+
const { registerExitHook } = require('../processHandler');
|
|
7
|
+
const testimCustomToken = require('./testimCustomToken');
|
|
6
8
|
const { gridTypes } = require('./constants');
|
|
7
9
|
const testimNgrok = require('./testimNgrok');
|
|
10
|
+
const testimCloudflare = require('./testimCloudflare');
|
|
8
11
|
const logger = require('./logger').getLogger('tunnel');
|
|
9
12
|
|
|
10
|
-
const shouldUseLambdatestTunnel = options => [gridTypes.LAMBDATEST, gridTypes.HYBRID].includes(options.gridData.type) && options.gridData.tunnel !== 'ngrok';
|
|
13
|
+
const shouldUseLambdatestTunnel = options => [gridTypes.LAMBDATEST, gridTypes.HYBRID].includes(options.gridData && options.gridData.type) && options.gridData.tunnel !== 'ngrok';
|
|
11
14
|
|
|
12
|
-
const connect = async (options
|
|
15
|
+
const connect = async (options) => {
|
|
13
16
|
if (!options.tunnel) {
|
|
14
17
|
return;
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
const authData = testimCustomToken.getTokenV3UserData();
|
|
17
21
|
let spinner;
|
|
18
22
|
try {
|
|
19
23
|
if (shouldUseLambdatestTunnel(options)) {
|
|
20
24
|
spinner = ora('Starting testim lambdatest tunnel...').start();
|
|
21
25
|
await LambdatestService.connectTunnel(options);
|
|
26
|
+
} else if (options.tunnelRoutes) {
|
|
27
|
+
spinner = ora('Starting testim cloudflare tunnel...').start();
|
|
28
|
+
await testimCloudflare.connectTunnel(options);
|
|
22
29
|
} else {
|
|
23
30
|
spinner = ora('Starting testim ngrok tunnel...').start();
|
|
24
31
|
await testimNgrok.connectTunnel(options, authData);
|
|
@@ -40,6 +47,8 @@ const disconnect = async (options) => {
|
|
|
40
47
|
try {
|
|
41
48
|
if (shouldUseLambdatestTunnel(options)) {
|
|
42
49
|
await LambdatestService.disconnectTunnel(options);
|
|
50
|
+
} else if (options.tunnelRoutes) {
|
|
51
|
+
await testimCloudflare.disconnectTunnel(options);
|
|
43
52
|
} else {
|
|
44
53
|
await testimNgrok.disconnectTunnel(options);
|
|
45
54
|
}
|
|
@@ -50,7 +59,14 @@ const disconnect = async (options) => {
|
|
|
50
59
|
}
|
|
51
60
|
};
|
|
52
61
|
|
|
62
|
+
const serveTunneling = async (options) => {
|
|
63
|
+
await connect(options);
|
|
64
|
+
registerExitHook(() => disconnect(options));
|
|
65
|
+
return await new Promise(() => { /* avoid exiting process */ });
|
|
66
|
+
};
|
|
67
|
+
|
|
53
68
|
module.exports = {
|
|
54
69
|
connect,
|
|
55
70
|
disconnect,
|
|
71
|
+
serveTunneling,
|
|
56
72
|
};
|