@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testim/testim-cli",
3
- "version": "3.232.0",
3
+ "version": "3.235.0",
4
4
  "description": "Command line interface for running Testing on your CI",
5
5
  "author": "Oren Rubin",
6
6
  "contributors": [{
@@ -69,7 +69,6 @@
69
69
  "data-uri-to-buffer": "2.0.2",
70
70
  "decompress": "4.2.1",
71
71
  "express": "4.17.3",
72
- "find-root": "1.1.0",
73
72
  "fkill": "7.2.1",
74
73
  "form-data": "3.0.0",
75
74
  "fs-extra": "10.0.1",
@@ -154,7 +154,7 @@ class WebDriverHttpRequest {
154
154
  httpDeleteRequest(path) {
155
155
  const requestId = utils.guid();
156
156
  logger.info("DELETE REQUEST", {requestId, path, testResultId: this.testResultId});
157
- return httpRequest.deleteFullRes(`${this.gridUrl}${path}`, this.headers, this.connectionRetryTimeout)
157
+ return httpRequest.deleteFullRes(`${this.gridUrl}${path}`, undefined, this.headers, this.connectionRetryTimeout)
158
158
  .tap((response) => logger.info("DELETE RESPONSE", {
159
159
  requestId,
160
160
  path,
package/processHandler.js CHANGED
@@ -1,3 +1,5 @@
1
+ /* eslint-disable no-console */
2
+
1
3
  'use strict';
2
4
 
3
5
  const logger = require('./commons/logger').getLogger('process-handler');
@@ -6,9 +8,12 @@ const exitHooks = [];
6
8
  const Promise = require('bluebird');
7
9
 
8
10
  module.exports = function (onExit) {
9
- process.on('uncaughtException', async (err) => {
11
+ async function cleanup(err) {
10
12
  // give cleanup and socket reports a chance to run
11
13
  await Promise.all(exitHooks.map(x => x())).timeout(10000).catch(() => {});
14
+ onExit(err);
15
+ }
16
+ process.on('uncaughtException', async (err) => {
12
17
  logger.error('Caught exception', { err });
13
18
  console.log('Uncaught exception');
14
19
  if (err.message) {
@@ -17,7 +22,7 @@ module.exports = function (onExit) {
17
22
  if (err.reason) {
18
23
  console.log('Reason =', err.reason);
19
24
  }
20
- onExit(err);
25
+ await cleanup(err);
21
26
  });
22
27
 
23
28
  process.on('unhandledRejection', (reason) => {
@@ -40,7 +45,7 @@ module.exports = function (onExit) {
40
45
  const msg = 'Runner aborted - SIGTERM event';
41
46
  const err = new Error(msg);
42
47
  logger.error(msg);
43
- onExit(err);
48
+ cleanup(err);
44
49
  throw err;
45
50
  });
46
51
 
@@ -48,7 +53,7 @@ module.exports = function (onExit) {
48
53
  const msg = 'Runner aborted - SIGINT event';
49
54
  const err = new Error(msg);
50
55
  logger.error(msg);
51
- onExit(err);
56
+ cleanup(err);
52
57
  throw err;
53
58
  });
54
59
 
package/runOptions.js CHANGED
@@ -295,7 +295,9 @@ program
295
295
  .option('--require-credentials', 'Log in to Testim if not already logged in')
296
296
 
297
297
  // Tunnel
298
+ .option('tunneld', 'run a tunnel daemon only')
298
299
  .option('--tunnel [tunnel]', 'enable tunnel')
300
+ .option('--tunnel-routes [routes]', 'tunnel routes for cloudflare tunnels')
299
301
  .option('--tunnel-port [tunnel-port]', 'tunnel port address')
300
302
  .option('--tunnel-host-header [tunnel-host-header]', 'tunnel host header')
301
303
  .option('--tunnel-region [tunnel-region]', 'ngrok tunnel region')
@@ -304,6 +306,7 @@ program
304
306
  .option('--external-lambdatest-tunnel-id [tunnel-id]', 'use existing lambdatest tunnel ID')
305
307
  .option('--external-lambdatest-use-wss', 'use wss instead of ssh for LT', false)
306
308
  .option('--external-lambdatest-disable-automation-tunneling', 'don\'t tunnel Testim calls in LT tunnel', true)
309
+ .option('--external-lambdatest-mitm', 'Turn on LT Man in the middle', false)
307
310
 
308
311
  .option('--w3c-capabilities [enable-w3c-caps-mode]', 'enable/disable w3c capabilities format (default enable)', JSON.parse, true)
309
312
  .option('--old-capabilities [enable-old-caps-mode]', 'enable/disable old capabilities format (default enable)', JSON.parse, true)
@@ -492,7 +495,7 @@ module.exports = {
492
495
  }
493
496
 
494
497
  if (program.proxyForGrid && !program.proxy) {
495
- return Promise.reject(new ArgError('missing --proxy option'));
498
+ throw new ArgError('missing --proxy option');
496
499
  }
497
500
 
498
501
  if (runOptionsAgentFlow.isAgentFlow(program)) {
@@ -522,6 +525,23 @@ module.exports = {
522
525
  throw err;
523
526
  }
524
527
 
528
+ if (program.tunneld) {
529
+ return {
530
+ tunnel: true,
531
+ tunnelPort: program.tunnelPort,
532
+ tunnelRoutes: program.tunnelRoutes,
533
+ tunnelRoutesOutput: program.tunnelRoutesOutput,
534
+ tunnelHostHeader: program.tunnelHostHeader,
535
+ tunnelRegion: program.tunnelRegion,
536
+ tunnelDiagnostics: program.tunnelDiagnostics,
537
+ tunnelUseHttpAddress: program.tunnelUseHttpAddress,
538
+ tunnelOnlyMode: true,
539
+ token: program.token,
540
+ project: program.project,
541
+ };
542
+ }
543
+
544
+
525
545
  const isTestConfigSpecified = (program.testConfig && program.testConfig.length) || (program.testConfigId && program.testConfigId.length);
526
546
  const isTestPlanSpecified = (program.testPlan && program.testPlan.length) || (program.testPlanId && program.testPlanId.length);
527
547
  const isSuiteSpecified = (program.suite && program.suite.length) || (program.suiteId && program.suiteId.length);
@@ -534,20 +554,20 @@ module.exports = {
534
554
  }
535
555
  }
536
556
 
537
- if (program.reporters && program.reporters.indexOf('junit') > -1 && !program.reportFile) {
557
+ if (program.reporters && program.reporters.includes('junit') && !program.reportFile) {
538
558
  console.log('Warning: please define --report-file option for JUnit reporter');
539
559
  }
540
560
 
541
561
  if (!program.tunnel && program.externalLambdatestTunnelId) {
542
- return Promise.reject(new ArgError('missing --tunnel parameter'));
562
+ throw new ArgError('missing --tunnel parameter');
543
563
  }
544
564
 
545
565
  if (!program.tunnel && program.externalLambdatestUseWss) {
546
- return Promise.reject(new ArgError('missing --tunnel parameter'));
566
+ throw new ArgError('missing --tunnel parameter');
547
567
  }
548
568
 
549
569
  if (!program.tunnel && [program.tunnelPort, program.tunnelHostHeader, program.tunnelRegion, program.tunnelDiagnostics].some(Boolean)) {
550
- return Promise.reject(new ArgError('missing --tunnel parameter'));
570
+ throw new ArgError('missing --tunnel parameter');
551
571
  }
552
572
  if (program.chromeExtraPrefs) {
553
573
  try {
@@ -590,7 +610,7 @@ module.exports = {
590
610
 
591
611
  // SauceLabs Options
592
612
  if ((program.sauceUser && !program.sauceKey) || (!program.sauceUser && program.sauceKey)) {
593
- return Promise.reject(new ArgError('missing --sauce-user <sauce-user> or --sauce-key <sauce-key>'));
613
+ throw new ArgError('missing --sauce-user <sauce-user> or --sauce-key <sauce-key>');
594
614
  }
595
615
 
596
616
  if (program.sauceUser && program.sauceKey) {
@@ -636,7 +656,7 @@ module.exports = {
636
656
 
637
657
  // BrowserStack options
638
658
  if ((program.browserstackUser && !program.browserstackKey) || (!program.browserstackUser && program.browserstackKey)) {
639
- return Promise.reject(new ArgError('missing --browserstack-user <browserstack-user> or --browserstack-key <browserstack-key>'));
659
+ throw new ArgError('missing --browserstack-user <browserstack-user> or --browserstack-key <browserstack-key>');
640
660
  }
641
661
  if (program.browserstackUser && program.browserstackKey) {
642
662
  setHostAndPortForBrowserStack();
@@ -707,7 +727,7 @@ module.exports = {
707
727
  if (projectId) {
708
728
  program.project = projectId;
709
729
  } else {
710
- return Promise.reject(new ArgError('missing project-id info, either --login to provide new credentials or use --project <project-id>'));
730
+ throw new ArgError('missing project-id info, either --login to provide new credentials or use --project <project-id>');
711
731
  }
712
732
  }
713
733
 
@@ -718,12 +738,12 @@ module.exports = {
718
738
 
719
739
  if (program.testConfig) {
720
740
  // convert single test config inputs to array (e.g. from configFile)
721
- program.testConfig = [].concat(program.testConfig);
741
+ program.testConfig = [program.testConfig].flat();
722
742
  }
723
743
 
724
744
  if (program.testConfigId) {
725
745
  // convert single test config inputs to array (e.g. from configFile)
726
- program.testConfigId = [].concat(program.testConfigId);
746
+ program.testConfigId = [program.testConfigId].flat();
727
747
  }
728
748
 
729
749
  program.retries = !program.retries || typeof program.retries === 'boolean' ? 1 : Number(program.retries) + 1;
@@ -745,21 +765,19 @@ module.exports = {
745
765
 
746
766
 
747
767
  if (program.parallel > 1 && program.run && !program.gridId && !program.grid &&
748
- ((!program.testPlan || program.testPlan.length === 0) && (!program.testPlanId || !program.testPlanId.length))) {
749
- if (process.stdout.isTTY && !program.headless && !process.env.TERM) {
750
- const prompts = require('prompts');
751
- const response = await prompts({
752
- type: 'toggle',
753
- name: 'isSure',
754
- message: 'Running in parallel without --headless flag will open several browsers on your computer. Are you sure?',
755
- initial: false,
756
- active: 'yes',
757
- inactive: 'no',
758
- });
759
-
760
- if (!response.isSure) {
761
- process.exit(0);
762
- }
768
+ ((!program.testPlan || program.testPlan.length === 0) && (!program.testPlanId || !program.testPlanId.length)) && process.stdout.isTTY && !program.headless && !process.env.TERM) {
769
+ const prompts = require('prompts');
770
+ const response = await prompts({
771
+ type: 'toggle',
772
+ name: 'isSure',
773
+ message: 'Running in parallel without --headless flag will open several browsers on your computer. Are you sure?',
774
+ initial: false,
775
+ active: 'yes',
776
+ inactive: 'no',
777
+ });
778
+
779
+ if (!response.isSure) {
780
+ process.exit(0);
763
781
  }
764
782
  }
765
783
 
@@ -768,11 +786,11 @@ module.exports = {
768
786
  program.port = program.port && Number(program.port);
769
787
 
770
788
  if (program.retries <= 0 || _.isNaN(program.retries)) {
771
- return Promise.reject(new ArgError('test failure retry count could not be a negative number or string, --retries <max_num_of_retries>'));
789
+ throw new ArgError('test failure retry count could not be a negative number or string, --retries <max_num_of_retries>');
772
790
  }
773
791
 
774
792
  if (program.retries > 21) {
775
- return Promise.reject(new ArgError('Max number of retries exceeded. Number cannot be greater than 20, --retries <max_num_of_retries>'));
793
+ throw new ArgError('Max number of retries exceeded. Number cannot be greater than 20, --retries <max_num_of_retries>');
776
794
  }
777
795
 
778
796
  if (!program.token) {
@@ -782,33 +800,32 @@ module.exports = {
782
800
  if (credentialToken) {
783
801
  program.token = credentialToken;
784
802
  } else {
785
- return Promise.reject(
786
- new ArgError('missing Testim Access Token, either --login to provide new credentials or use --token <testim-access-token>, contact info@testim.io if you need a new one.'));
803
+ throw new ArgError('missing Testim Access Token, either --login to provide new credentials or use --token <testim-access-token>, contact info@testim.io if you need a new one.');
787
804
  }
788
805
  }
789
806
 
790
807
  if (program.browserTimeout <= 0 || _.isNaN(program.browserTimeout)) {
791
- return Promise.reject(new ArgError('get browser timeout could not be a negative number, --browser-timeout <get-browser-timeout>'));
808
+ throw new ArgError('get browser timeout could not be a negative number, --browser-timeout <get-browser-timeout>');
792
809
  }
793
810
 
794
811
  if (program.newBrowserWaitTimeout <= 0 || _.isNaN(program.newBrowserWaitTimeout)) {
795
- return Promise.reject(new ArgError('max new browser wait timeout could not be a negative number, --new-browser-wait-timeout <max-wait-to-browser>'));
812
+ throw new ArgError('max new browser wait timeout could not be a negative number, --new-browser-wait-timeout <max-wait-to-browser>');
796
813
  }
797
814
 
798
815
  if (program.timeout <= 0 || _.isNaN(program.timeout)) {
799
- return Promise.reject(new ArgError('test run timeout could not be a negative number, --timeout <run-timeout>'));
816
+ throw new ArgError('test run timeout could not be a negative number, --timeout <run-timeout>');
800
817
  }
801
818
 
802
819
  if (program.parallel <= 0 || _.isNaN(program.parallel)) {
803
- return Promise.reject(new ArgError('parallel could not be a negative number or not number, --parallel <number-of-tests>'));
820
+ throw new ArgError('parallel could not be a negative number or not number, --parallel <number-of-tests>');
804
821
  }
805
822
 
806
- if ([CLI_MODE.EXTENSION, CLI_MODE.SELENIUM].indexOf(program.mode) === -1) {
807
- return Promise.reject(new ArgError(`runner mode <${program.mode}> is not supported`));
823
+ if (![CLI_MODE.EXTENSION, CLI_MODE.SELENIUM].includes(program.mode)) {
824
+ throw new ArgError(`runner mode <${program.mode}> is not supported`);
808
825
  }
809
826
 
810
827
  if ((program.mode !== CLI_MODE.SELENIUM) && program.disableNativeEvents) {
811
- return Promise.reject(new ArgError('disable-native-events is only applicable in selenium mode'));
828
+ throw new ArgError('disable-native-events is only applicable in selenium mode');
812
829
  }
813
830
 
814
831
  if (
@@ -829,9 +846,9 @@ module.exports = {
829
846
  !program.useChromeLauncher &&
830
847
  !program.createPrefechedData
831
848
  ) {
832
- return Promise.reject(new ArgError(
849
+ throw new ArgError(
833
850
  'missing remote grid address parameter, specify --host <host-name-or-ip> or --grid <grid-name> or --grid-id <grid-id>'
834
- ));
851
+ );
835
852
  }
836
853
  } else if (
837
854
  program.testId.length ||
@@ -843,9 +860,9 @@ module.exports = {
843
860
  program.useLocalChromeDriver ||
844
861
  program.useChromeLauncher
845
862
  ) {
846
- return Promise.reject(new ArgError(
863
+ throw new ArgError(
847
864
  'cannot set --testId, --label, --name, --browser, --test-config, --test-config-id, --use-local-chrome-driver --use-chrome-launcher or --suite with --test-plan option'
848
- ));
865
+ );
849
866
  }
850
867
 
851
868
  if (
@@ -856,17 +873,17 @@ module.exports = {
856
873
  isSuiteSpecified) &&
857
874
  program.file
858
875
  ) {
859
- return Promise.reject(new ArgError(
876
+ throw new ArgError(
860
877
  'Cannot pass codeful automation tests with --testId --label --name or --suite'
861
- ));
878
+ );
862
879
  }
863
880
 
864
881
  const numberOfDefinedHosts = [program.host, program.grid, program.gridId].filter(Boolean).length;
865
882
  if (numberOfDefinedHosts > 1) {
866
- return Promise.reject(new ArgError('please define exactly one of --grid or --grid-id or --host'));
883
+ throw new ArgError('please define exactly one of --grid or --grid-id or --host');
867
884
  }
868
885
 
869
- if (program.host && program.host.indexOf('/') !== -1) {
886
+ if (program.host && program.host.includes('/')) {
870
887
  if (!/^(f|ht)tps?:\/\//i.test(program.host)) {
871
888
  program.host = `http://${program.host}`;
872
889
  }
@@ -877,7 +894,7 @@ module.exports = {
877
894
  program.resultLabel = program.resultLabel.map(label => label.trim()).filter(Boolean);
878
895
  const invalidLabels = program.resultLabel.filter(label => label.length >= 250).filter(Boolean);
879
896
  if (invalidLabels.length) {
880
- return Promise.reject(new ArgError('A result label cannot exceed 250 characters'));
897
+ throw new ArgError('A result label cannot exceed 250 characters');
881
898
  }
882
899
  }
883
900
 
@@ -885,66 +902,66 @@ module.exports = {
885
902
  const playerUrl = runOptionsUtils.getPlayerUrl(program);
886
903
 
887
904
  if (!program.w3cCapabilities && !program.oldCapabilities) {
888
- return Promise.reject(new ArgError('cannot set --w3c-capabilities and --old-capabilities options as false'));
905
+ throw new ArgError('cannot set --w3c-capabilities and --old-capabilities options as false');
889
906
  }
890
907
  program.protocol = program.protocol || (program.port === 443 ? 'https' : 'http');
891
908
  if (!['http', 'https'].includes(program.protocol)) {
892
- return Promise.reject(new ArgError('invalid --protocol value, allow --protocol http or https'));
909
+ throw new ArgError('invalid --protocol value, allow --protocol http or https');
893
910
  }
894
911
 
895
912
  if (program.rerunFailedByRunId && program.branch) {
896
- return Promise.reject(new ArgError('It is not possible to use --branch with --rerun-failed-by-run-id. Tests will automatically run on the same branch that was used in the original run'));
913
+ throw new ArgError('It is not possible to use --branch with --rerun-failed-by-run-id. Tests will automatically run on the same branch that was used in the original run');
897
914
  }
898
915
 
899
916
  if (program.rerunFailedByRunId &&
900
917
  (isSuiteSpecified || program.name.length ||
901
918
  program.testId.length || program.label.length || isTestPlanSpecified)) {
902
- return Promise.reject(new ArgError('Re-running failed tests is not possible when suite (--suite),' +
903
- ' label (--label), plan (--test-plan), or other test flags (--test) are provided. Please remove these flags and try again'));
919
+ throw new ArgError('Re-running failed tests is not possible when suite (--suite),' +
920
+ ' label (--label), plan (--test-plan), or other test flags (--test) are provided. Please remove these flags and try again');
904
921
  }
905
922
 
906
923
  if (program.run.length) {
907
924
  const glob = require('glob');
908
925
  program.files = _.flatMap(program.run, files => glob.sync(files));
909
926
  if (program.files.length === 0) {
910
- return Promise.reject(new ArgError(`No files found at path '${program.run}'.`));
927
+ throw new ArgError(`No files found at path '${program.run}'.`);
911
928
  }
912
929
  } else {
913
930
  program.files = [];
914
931
  }
915
932
 
916
933
  if (program.setRetention && !_.inRange(_.parseInt(program.setRetention), 1, 11)) {
917
- return Promise.reject(new ArgError('Please provide the number of days that the test results will be retained for (--set-retention must be a whole number between 1 to 10)'));
934
+ throw new ArgError('Please provide the number of days that the test results will be retained for (--set-retention must be a whole number between 1 to 10)');
918
935
  }
919
936
  program.setRetention = program.setRetention && Number(program.setRetention);
920
937
 
921
938
  const mockNetworkDeprecationMsg = 'is no longer supported, please use --override-mapping-file';
922
939
  if (program.mockNetworkHar) {
923
- return Promise.reject(new ArgError(`--mock-network-har ${mockNetworkDeprecationMsg}`));
940
+ throw new ArgError(`--mock-network-har ${mockNetworkDeprecationMsg}`);
924
941
  }
925
942
  if (program.mockNetworkPattern) {
926
- return Promise.reject(new ArgError(`--mock-network-pattern ${mockNetworkDeprecationMsg}`));
943
+ throw new ArgError(`--mock-network-pattern ${mockNetworkDeprecationMsg}`);
927
944
  }
928
945
 
929
946
  if (program.disableMockNetwork && program.overrideMappingFile) {
930
- return Promise.reject(new ArgError('You can either use --disable-mock-network or --override-mapping-file'));
947
+ throw new ArgError('You can either use --disable-mock-network or --override-mapping-file');
931
948
  }
932
949
 
933
950
  if (!program.collectCodeCoverage && program.codeCoverageSourceMapPath) {
934
- return Promise.reject(new ArgError('cannot set --code-coverage-source-map-path without passing --collect-code-coverage'));
951
+ throw new ArgError('cannot set --code-coverage-source-map-path without passing --collect-code-coverage');
935
952
  }
936
953
 
937
954
  if (!program.collectCodeCoverage && program.codeCoverageReporter.length) {
938
- return Promise.reject(new ArgError('cannot set --code-coverage-reporter without passing --collect-code-coverage'));
955
+ throw new ArgError('cannot set --code-coverage-reporter without passing --collect-code-coverage');
939
956
  }
940
957
 
941
958
  if (!program.collectCodeCoverage && program.codeCoverageInclude.length) {
942
- return Promise.reject(new ArgError('cannot set --code-coverage-include without passing --collect-code-coverage'));
959
+ throw new ArgError('cannot set --code-coverage-include without passing --collect-code-coverage');
943
960
  }
944
961
 
945
962
  if (program.collectCodeCoverage && program.codeCoverageReporter && _.difference(program.codeCoverageReporter, CODE_COVERAGE_REPORTER_OPTIONS).length) {
946
963
  const diff = _.difference(program.codeCoverageReporter, CODE_COVERAGE_REPORTER_OPTIONS);
947
- return Promise.reject(new ArgError(`invalid --code-coverage-reporter parameters ${diff.join('/')}`));
964
+ throw new ArgError(`invalid --code-coverage-reporter parameters ${diff.join('/')}`);
948
965
  }
949
966
 
950
967
  program.codeCoverageReporter = program.codeCoverageReporter.length === 0 ? ['html', 'text'] : program.codeCoverageReporter;
@@ -963,7 +980,7 @@ module.exports = {
963
980
 
964
981
  if (program.mode !== CLI_MODE.EXTENSION && usedExtensionOptions.length) {
965
982
  const multi = usedExtensionOptions.length > 1 ? 'are' : 'is';
966
- return Promise.reject(new ArgError(`${usedExtensionOptions.map(key => extensionOnlyOptions[key]).join(' and ')} ${multi} only applicable in extension mode`));
983
+ throw new ArgError(`${usedExtensionOptions.map(key => extensionOnlyOptions[key]).join(' and ')} ${multi} only applicable in extension mode`);
967
984
  }
968
985
 
969
986
  if (program.tmsFieldFile) {
@@ -1035,18 +1052,18 @@ module.exports = {
1035
1052
  }
1036
1053
 
1037
1054
  if (typeof program.baseUrl === 'boolean') {
1038
- return Promise.reject(new ArgError('base url cannot be used as a flag, and must contain a string value'));
1055
+ throw new ArgError('base url cannot be used as a flag, and must contain a string value');
1039
1056
  }
1040
1057
 
1041
1058
  return ({
1042
- testId: [].concat(program.testId),
1043
- name: [].concat(program.name),
1044
- label: [].concat(program.label),
1045
- suites: [].concat(program.suite),
1046
- suiteIds: [].concat(program.suiteId),
1047
- testPlan: [].concat(program.testPlan),
1048
- testPlanIds: [].concat(program.testPlanId),
1049
- files: [].concat(program.files),
1059
+ testId: [program.testId].flat(),
1060
+ name: [program.name].flat(),
1061
+ label: [program.label].flat(),
1062
+ suites: [program.suite].flat(),
1063
+ suiteIds: [program.suiteId].flat(),
1064
+ testPlan: [program.testPlan].flat(),
1065
+ testPlanIds: [program.testPlanId].flat(),
1066
+ files: [program.files].flat(),
1050
1067
  webpackConfig: program.webpackConfig,
1051
1068
  reportFile: program.reportFile,
1052
1069
  reportFileClassname: program.overrideReportFileClassname,
@@ -1097,7 +1114,7 @@ module.exports = {
1097
1114
 
1098
1115
  // Extension
1099
1116
  ext: program.ext,
1100
- extensionLocation: [].concat(program.extensionPath || extHeadlessUrl),
1117
+ extensionLocation: [program.extensionPath || extHeadlessUrl].flat(),
1101
1118
  extensionPath: program.extensionPath,
1102
1119
 
1103
1120
  // Player
@@ -1108,6 +1125,8 @@ module.exports = {
1108
1125
  // Tunnel
1109
1126
  tunnel: program.tunnel,
1110
1127
  tunnelPort: program.tunnelPort,
1128
+ tunnelRoutes: program.tunnelRoutes,
1129
+ tunnelRoutesOutput: program.tunnelRoutesOutput,
1111
1130
  tunnelHostHeader: program.tunnelHostHeader,
1112
1131
  tunnelRegion: program.tunnelRegion,
1113
1132
  tunnelDiagnostics: program.tunnelDiagnostics,
@@ -1115,6 +1134,7 @@ module.exports = {
1115
1134
  externalLambdatestTunnelId: program.externalLambdatestTunnelId,
1116
1135
  externalLambdatestUseWss: program.externalLambdatestUseWss || false,
1117
1136
  externalLambdatestDisableAutomationTunneling: Boolean(program.externalLambdatestDisableAutomationTunneling),
1137
+ externalLambdatestMitm: Boolean(program.externalLambdatestMitm),
1118
1138
 
1119
1139
  // Hooks
1120
1140
  beforeTest: program.beforeTest,
package/runner.js CHANGED
@@ -213,7 +213,7 @@ function runRunner(options, customExtensionLocalLocation) {
213
213
 
214
214
  return validateCliAccount(options)
215
215
  .log('in runRunner before tunnel.connect')
216
- .then(() => tunnel.connect(options, testimCustomToken.getTokenV3UserData()))
216
+ .then(() => tunnel.connect(options))
217
217
  .log('in runRunner after tunnel.connect')
218
218
  .then(() => new TestPlanRunner(customExtensionLocalLocation).run(options))
219
219
  .log('before tunnel.disconnect')
@@ -88,22 +88,25 @@ class TestPlanRunner {
88
88
  await EyeSdkBuilder.closeBatch(executionId);
89
89
  return;
90
90
  }
91
+ /** @type {Awaited<ReturnType<typeof testimServicesApi['getApplitoolsIntegrationData']>>} */
92
+ let applitoolsIntegrationData;
91
93
  try {
92
94
  if (!tpOptions.company || !tpOptions.company.activePlan || !tpOptions.company.activePlan.premiumFeatures || !tpOptions.company.activePlan.premiumFeatures.applitools) {
93
95
  return;
94
96
  }
95
- const applitoolsIntegrationData = await testimServicesApi.getApplitoolsIntegrationData(tpOptions.project);
96
- if (_.isEmpty(applitoolsIntegrationData)) {
97
+ applitoolsIntegrationData = await testimServicesApi.getApplitoolsIntegrationData(tpOptions.project);
98
+ if (_.isEmpty(applitoolsIntegrationData) || !executionId) {
97
99
  return;
98
100
  }
99
101
  const { runKey: apiKey, url: serverUrl } = applitoolsIntegrationData;
100
102
  const tmpSDK = require('@applitools/eyes-sdk-core').makeSDK({ name: 'Testim.io', version: '4.0.0', spec: {} });
101
103
  await tmpSDK.closeBatches({ batchIds: [executionId], serverUrl, apiKey });
102
104
  } catch (err) {
103
- if (err.message && err.message.startsWith('Request failed with status code 404')) { // If a batch with this name did not exist, do not log an error.
105
+ // If a batch with this name did not exist, do not log an error.
106
+ if (err.message && (err.message.startsWith('Request failed with status code 404') || err.message.startsWith('no batchIds were set'))) {
104
107
  return;
105
108
  }
106
- logger.error('Failed closing batch in extension mode', { err, projectId: tpOptions.project });
109
+ logger.error('Failed closing batch in extension mode', { err, projectId: tpOptions.project, applitoolsIntegrationData, batchIds: [executionId] });
107
110
  }
108
111
  });
109
112
  }
@@ -258,7 +258,7 @@ async function getGridData(options) {
258
258
  if (grid) {
259
259
  return getGridDataByGridName(companyId, grid, allGrids);
260
260
  }
261
- if (hasTestPlanFlag(options)) {
261
+ if (hasTestPlanFlag(options) || options.tunnelOnlyMode) {
262
262
  logger.info('skipping getting grid, as it is set on test plan', { companyId });
263
263
  return undefined;
264
264
  }
@@ -136,6 +136,10 @@ class LambdatestService {
136
136
  }
137
137
  }
138
138
 
139
+ if (runnerOptions.externalLambdatestMitm) {
140
+ tunnelArgs = [...tunnelArgs, '--mitm'];
141
+ }
142
+
139
143
  LambdatestService.tunnel = spawn('./LT', tunnelArgs, { cwd: LT_TUNNEL_BINARY_DIRECTORY });
140
144
 
141
145
  let stdoutResult = '';
@@ -207,6 +211,7 @@ class LambdatestService {
207
211
  build: executionId,
208
212
  name: `${testResultId} - ${testName}`,
209
213
  platform: LambdatestService.lambdatestConfig.PLATFORM,
214
+ // eslint-disable-next-line camelcase
210
215
  selenium_version: LambdatestService.lambdatestConfig.SELENIUM_VERSION,
211
216
  resolution: LambdatestService.lambdatestConfig.RESOLUTION,
212
217
  timezone: LambdatestService.lambdatestConfig.TIMEZONE,