@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.
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const path = require('path');
4
- const findRoot = require('find-root');
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
- function getRoot() {
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
  }
@@ -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
  };
@@ -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, authData) => {
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
  };