@testim/testim-cli 3.231.0 → 3.234.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/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
  }
@@ -53,10 +53,10 @@ class FeatureFlagsService {
53
53
  useSameBrowserForMultiTests: new LabFeatureFlag('labs'),
54
54
  highSpeedMode: new LabFeatureFlag(),
55
55
  usePortedHtml5DragDrop: new Rox.Flag(),
56
- applitoolsNewIntegration: new Rox.Flag(),
57
56
  testNamesToBeforeSuiteHook: new Rox.Flag(),
58
57
  addCustomCapabilities: new Rox.Variant('{}'),
59
58
  enableWorkerThreadsCliCodeExecution: new Rox.Flag(true),
59
+ LTNetworkCapabilities: new Rox.Flag(),
60
60
  };
61
61
  Rox.register('default', this.flags);
62
62
  }
@@ -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
  };