@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/runOptions.js
CHANGED
|
@@ -2,22 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
-
const { CLI_MODE } = require('./commons/constants');
|
|
6
|
-
const { EDGE_CHROMIUM_MIN_VERSION } = require('./player/constants');
|
|
7
|
-
const program = require('commander');
|
|
8
5
|
const fs = require('fs');
|
|
9
6
|
const ms = require('ms');
|
|
10
|
-
const Promise = require('bluebird');
|
|
11
|
-
const NoArgsError = require('./errors.js').NoArgsError;
|
|
12
|
-
const ArgError = require('./errors.js').ArgError;
|
|
13
7
|
const url = require('url');
|
|
14
8
|
const _ = require('lodash');
|
|
15
9
|
const path = require('path');
|
|
10
|
+
const chalk = require('chalk');
|
|
11
|
+
const program = require('commander');
|
|
16
12
|
const utils = require('./utils');
|
|
17
|
-
const runOptionsAgentFlow = require('./runOptionsAgentFlow');
|
|
18
13
|
const runOptionsUtils = require('./runOptionsUtils');
|
|
14
|
+
const runOptionsAgentFlow = require('./runOptionsAgentFlow');
|
|
19
15
|
const localRunnerCache = require('./commons/runnerFileCache');
|
|
20
|
-
const
|
|
16
|
+
const { CLI_MODE } = require('./commons/constants');
|
|
17
|
+
const { NoArgsError, ArgError } = require('./errors');
|
|
18
|
+
const { EDGE_CHROMIUM_MIN_VERSION } = require('./player/constants');
|
|
21
19
|
|
|
22
20
|
const camelizeHyphenValues = (prop) => prop.replace(/-([a-z])/g, (m, w) => w.toUpperCase());
|
|
23
21
|
|
|
@@ -114,6 +112,10 @@ const printUsage = () => {
|
|
|
114
112
|
return line.includes('--high-speed'); // high speed mode was renamed to turbo mode
|
|
115
113
|
}
|
|
116
114
|
|
|
115
|
+
function isTestStartTimeout(line) {
|
|
116
|
+
return line.includes('--test-start-timeout');
|
|
117
|
+
}
|
|
118
|
+
|
|
117
119
|
program.help((txt) => {
|
|
118
120
|
const lines = txt.split('\n');
|
|
119
121
|
return lines
|
|
@@ -129,7 +131,8 @@ const printUsage = () => {
|
|
|
129
131
|
!isWebdriverTimeout(ln) &&
|
|
130
132
|
!isSaveRCALocally(ln) &&
|
|
131
133
|
!isExitCodeIgnoreFailingTests(ln) &&
|
|
132
|
-
!isDeprecatedHighSpeed(ln)
|
|
134
|
+
!isDeprecatedHighSpeed(ln) &&
|
|
135
|
+
!isTestStartTimeout(ln)
|
|
133
136
|
)
|
|
134
137
|
.join('\n');
|
|
135
138
|
});
|
|
@@ -217,17 +220,18 @@ program
|
|
|
217
220
|
.option('--file-cache-location [directory]', ' internal CLI file caching location')
|
|
218
221
|
|
|
219
222
|
// Timeout
|
|
220
|
-
.option('--timeout [test-timeout]', 'test
|
|
221
|
-
.option('--
|
|
222
|
-
.option('--
|
|
223
|
+
.option('--test-start-timeout [test-start-timeout]', 'The time to wait for a test to start after getting a browser session', Number, Number(process.env.TESTIM_TEST_START_TIMEOUT) || (2 * 60 * 1000))
|
|
224
|
+
.option('--timeout [test-timeout]', 'Test run timeout in milliseconds', Number)
|
|
225
|
+
.option('--browser-timeout [open-browser-timeout]', 'Get browser from grid timeout in milliseconds', Number)
|
|
226
|
+
.option('--new-browser-wait-timeout [max-wait-to-browser]', 'Maximum get browser wait in minutes', Number)
|
|
223
227
|
|
|
224
228
|
// New Timeouts
|
|
225
|
-
.option('--get-browser-timeout [get-browser-timeout]', 'Timeout for a single attempt to get browser from the grid configured in the project\'s plan') // getBrowserTimeout
|
|
226
|
-
.option('--get-browser-retries [get-browser-retries]', 'Number of attempts to get browser from the grid configured in the project\'s plan') // getBrowserRetries
|
|
227
|
-
.option('--get-session-timeout [get-session-timeout]', 'Timeout for "/session" request to the selenium server', ms('90s')) // getSessionTimeout
|
|
228
|
-
.option('--get-session-retries [get-session-retries]', 'Retries for "/session" request to the selenium server', 3) // getSessionRetries
|
|
229
|
-
.option('--driver-request-timeout [driver-request-timeout]', 'Timeout for any WebDriver request to the grid server', ms('90s')) // driverRequestTimeout
|
|
230
|
-
.option('--driver-request-retries [driver-request-retries]', 'Retries for any WebDriver request to the grid server', 3) // driverRequestRetries
|
|
229
|
+
.option('--get-browser-timeout [get-browser-timeout]', 'Timeout for a single attempt to get browser from the grid configured in the project\'s plan', Number) // getBrowserTimeout
|
|
230
|
+
.option('--get-browser-retries [get-browser-retries]', 'Number of attempts to get browser from the grid configured in the project\'s plan', Number) // getBrowserRetries
|
|
231
|
+
.option('--get-session-timeout [get-session-timeout]', 'Timeout for "/session" request to the selenium server', Number, ms('90s')) // getSessionTimeout
|
|
232
|
+
.option('--get-session-retries [get-session-retries]', 'Retries for "/session" request to the selenium server', Number, 3) // getSessionRetries
|
|
233
|
+
.option('--driver-request-timeout [driver-request-timeout]', 'Timeout for any WebDriver request to the grid server', Number, ms('90s')) // driverRequestTimeout
|
|
234
|
+
.option('--driver-request-retries [driver-request-retries]', 'Retries for any WebDriver request to the grid server', Number, 3) // driverRequestRetries
|
|
231
235
|
|
|
232
236
|
// Run chrome ext mode locally
|
|
233
237
|
.option('--use-local-chrome-driver [use-local-chrome-driver]', 'use a local ChromeDriver instance instead of a selenium grid')
|
|
@@ -510,8 +514,10 @@ module.exports = {
|
|
|
510
514
|
try {
|
|
511
515
|
let options = {};
|
|
512
516
|
if (program.configFile) {
|
|
517
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
513
518
|
options = require(path.join(process.cwd(), program.configFile)).config;
|
|
514
519
|
} else if (program.optionsFile) {
|
|
520
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
515
521
|
options = require(path.join(process.cwd(), program.optionsFile));
|
|
516
522
|
}
|
|
517
523
|
|
|
@@ -546,19 +552,20 @@ module.exports = {
|
|
|
546
552
|
}
|
|
547
553
|
|
|
548
554
|
|
|
549
|
-
const isTestConfigSpecified =
|
|
550
|
-
const isTestPlanSpecified =
|
|
551
|
-
const isSuiteSpecified =
|
|
555
|
+
const isTestConfigSpecified = program.testConfig?.length || program.testConfigId?.length;
|
|
556
|
+
const isTestPlanSpecified = program.testPlan?.length || program.testPlanId?.length;
|
|
557
|
+
const isSuiteSpecified = program.suite?.length || program.suiteId?.length;
|
|
552
558
|
|
|
553
559
|
if (program.seleniumCapsFile) {
|
|
554
560
|
try {
|
|
561
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
555
562
|
seleniumCapsFileContent = require(path.join(process.cwd(), program.seleniumCapsFile));
|
|
556
563
|
} catch (err) {
|
|
557
|
-
|
|
564
|
+
throw new ArgError(`Failed to parse selenium caps file file error: ${err.message}`);
|
|
558
565
|
}
|
|
559
566
|
}
|
|
560
567
|
|
|
561
|
-
if (program.reporters
|
|
568
|
+
if (program.reporters?.includes('junit') && !program.reportFile) {
|
|
562
569
|
console.log('Warning: please define --report-file option for JUnit reporter');
|
|
563
570
|
}
|
|
564
571
|
|
|
@@ -575,9 +582,10 @@ module.exports = {
|
|
|
575
582
|
}
|
|
576
583
|
if (program.chromeExtraPrefs) {
|
|
577
584
|
try {
|
|
585
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
578
586
|
chromeExtraPrefs = require(path.join(process.cwd(), program.chromeExtraPrefs));
|
|
579
587
|
} catch (err) {
|
|
580
|
-
|
|
588
|
+
throw new ArgError(`Failed to read/open chrome extra prefs file error: ${err.message}`);
|
|
581
589
|
}
|
|
582
590
|
}
|
|
583
591
|
|
|
@@ -598,9 +606,10 @@ module.exports = {
|
|
|
598
606
|
|
|
599
607
|
if (program.paramsFile) {
|
|
600
608
|
try {
|
|
609
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
601
610
|
userParamsData = Object.assign({}, userParamsData, require(path.join(process.cwd(), program.paramsFile)));
|
|
602
611
|
} catch (err) {
|
|
603
|
-
|
|
612
|
+
throw new ArgError(`Failed to read/open params file error: ${err.message}`);
|
|
604
613
|
}
|
|
605
614
|
}
|
|
606
615
|
|
|
@@ -608,7 +617,7 @@ module.exports = {
|
|
|
608
617
|
try {
|
|
609
618
|
userParamsData = Object.assign({}, userParamsData, JSON.parse(program.params));
|
|
610
619
|
} catch (err) {
|
|
611
|
-
|
|
620
|
+
throw new ArgError(`Failed to parse params string error: ${err.message}`);
|
|
612
621
|
}
|
|
613
622
|
}
|
|
614
623
|
|
|
@@ -627,6 +636,7 @@ module.exports = {
|
|
|
627
636
|
|
|
628
637
|
if (program.sauceOptions) {
|
|
629
638
|
try {
|
|
639
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
630
640
|
const sOptions = require(path.join(process.cwd(), program.sauceOptions));
|
|
631
641
|
const isMobile = sOptions.platformName && ['ios', 'android'].includes(sOptions.platformName.toLowerCase());
|
|
632
642
|
if (sOptions.browserName) {
|
|
@@ -649,12 +659,12 @@ module.exports = {
|
|
|
649
659
|
|
|
650
660
|
const isBadVersion = parseFloat(sOptions.version) < 50 && !['dev', 'beta'].includes(sOptions.version);
|
|
651
661
|
if (!isMobile && program.browser === 'chrome' && isBadVersion) {
|
|
652
|
-
|
|
662
|
+
throw new ArgError('The minimum chrome supported version is 50.0');
|
|
653
663
|
}
|
|
654
664
|
|
|
655
665
|
program.saucelabs = Object.assign({}, program.saucelabs, sOptions);
|
|
656
666
|
} catch (err) {
|
|
657
|
-
|
|
667
|
+
throw new ArgError(`Failed to parse saucelabs options file error: ${err.message}`);
|
|
658
668
|
}
|
|
659
669
|
}
|
|
660
670
|
|
|
@@ -672,6 +682,7 @@ module.exports = {
|
|
|
672
682
|
|
|
673
683
|
if (program.browserstackOptions) {
|
|
674
684
|
try {
|
|
685
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
675
686
|
const bsOptions = require(path.join(process.cwd(), program.browserstackOptions));
|
|
676
687
|
const isMobile = bsOptions.platform && ['mac', 'android'].includes(bsOptions.platform.toLowerCase());
|
|
677
688
|
if (bsOptions.browserName) {
|
|
@@ -683,12 +694,12 @@ module.exports = {
|
|
|
683
694
|
}
|
|
684
695
|
|
|
685
696
|
if (!isMobile && parseFloat(bsOptions.browser_version) < 50 && program.browser === 'chrome') {
|
|
686
|
-
|
|
697
|
+
throw new ArgError('The minimum chrome supported version is 50.0');
|
|
687
698
|
}
|
|
688
699
|
|
|
689
700
|
program.browserstack = Object.assign({}, program.browserstack, bsOptions);
|
|
690
701
|
} catch (err) {
|
|
691
|
-
|
|
702
|
+
throw new ArgError(`Failed to parse browserstack options file error: ${err.message}`);
|
|
692
703
|
}
|
|
693
704
|
}
|
|
694
705
|
|
|
@@ -700,11 +711,12 @@ module.exports = {
|
|
|
700
711
|
|
|
701
712
|
if (program.perfectoOptions) {
|
|
702
713
|
try {
|
|
714
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
703
715
|
const perfectoOptions = require(path.join(process.cwd(), program.perfectoOptions));
|
|
704
716
|
const DEFAULTS = { location: 'US East', securityToken: program.perfectoToken };
|
|
705
717
|
program.perfecto = Object.assign({}, DEFAULTS, perfectoOptions);
|
|
706
718
|
} catch (err) {
|
|
707
|
-
|
|
719
|
+
throw new ArgError(`Failed to parse perfecto options file error: ${err.message}`);
|
|
708
720
|
}
|
|
709
721
|
}
|
|
710
722
|
|
|
@@ -716,11 +728,12 @@ module.exports = {
|
|
|
716
728
|
|
|
717
729
|
if (program.testobjectOptions) {
|
|
718
730
|
try {
|
|
731
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
719
732
|
const testobjectOptions = require(path.join(process.cwd(), program.testobjectOptions));
|
|
720
733
|
const DEFAULTS = { testobjectApiKey: program.testobjectKey };
|
|
721
734
|
program.testobjectSauce = Object.assign({}, DEFAULTS, testobjectOptions);
|
|
722
735
|
} catch (err) {
|
|
723
|
-
|
|
736
|
+
throw new ArgError(`Failed to parse test object options file error: ${err.message}`);
|
|
724
737
|
}
|
|
725
738
|
}
|
|
726
739
|
|
|
@@ -751,8 +764,8 @@ module.exports = {
|
|
|
751
764
|
}
|
|
752
765
|
|
|
753
766
|
program.retries = !program.retries || typeof program.retries === 'boolean' ? 1 : Number(program.retries) + 1;
|
|
754
|
-
program.browserTimeout = !program.browserTimeout || typeof program.browserTimeout === 'boolean' ? 60 * 1000 :
|
|
755
|
-
program.newBrowserWaitTimeout = !program.newBrowserWaitTimeout || typeof program.newBrowserWaitTimeout === 'boolean' ? 10 * 60 * 1000 :
|
|
767
|
+
program.browserTimeout = !program.browserTimeout || typeof program.browserTimeout === 'boolean' ? 60 * 1000 : program.browserTimeout;
|
|
768
|
+
program.newBrowserWaitTimeout = !program.newBrowserWaitTimeout || typeof program.newBrowserWaitTimeout === 'boolean' ? 10 * 60 * 1000 : program.newBrowserWaitTimeout * 60 * 1000;
|
|
756
769
|
|
|
757
770
|
if (!program.getBrowserTimeout) {
|
|
758
771
|
program.getBrowserTimeout = program.browserTimeout;
|
|
@@ -764,7 +777,7 @@ module.exports = {
|
|
|
764
777
|
program.driverRequestTimeout = program.browserTimeout < program.driverRequestTimeout ? program.driverRequestTimeout : program.browserTimeout;
|
|
765
778
|
|
|
766
779
|
const timeoutWasGiven = Boolean(program.timeout);
|
|
767
|
-
program.timeout = !program.timeout || typeof program.timeout === 'boolean' ? 10 * 60 * 1000 :
|
|
780
|
+
program.timeout = !program.timeout || typeof program.timeout === 'boolean' ? 10 * 60 * 1000 : program.timeout;
|
|
768
781
|
program.beforeParallel = !program.beforeParallel || typeof program.beforeParallel === 'boolean' ? 1 : Number(program.beforeParallel);
|
|
769
782
|
program.parallel = !program.parallel || typeof program.parallel === 'boolean' ? 1 : Number(program.parallel);
|
|
770
783
|
program.afterParallel = !program.afterParallel || typeof program.afterParallel === 'boolean' ? 1 : Number(program.afterParallel);
|
|
@@ -791,7 +804,7 @@ module.exports = {
|
|
|
791
804
|
|
|
792
805
|
program.port = program.port && Number(program.port);
|
|
793
806
|
|
|
794
|
-
if (program.retries <= 0 ||
|
|
807
|
+
if (program.retries <= 0 || Number.isNaN(program.retries)) {
|
|
795
808
|
throw new ArgError('test failure retry count could not be a negative number or string, --retries <max_num_of_retries>');
|
|
796
809
|
}
|
|
797
810
|
|
|
@@ -810,27 +823,27 @@ module.exports = {
|
|
|
810
823
|
}
|
|
811
824
|
}
|
|
812
825
|
|
|
813
|
-
if (program.browserTimeout <= 0 ||
|
|
826
|
+
if (program.browserTimeout <= 0 || Number.isNaN(program.browserTimeout)) {
|
|
814
827
|
throw new ArgError('get browser timeout could not be a negative number, --browser-timeout <get-browser-timeout>');
|
|
815
828
|
}
|
|
816
829
|
|
|
817
|
-
if (program.newBrowserWaitTimeout <= 0 ||
|
|
830
|
+
if (program.newBrowserWaitTimeout <= 0 || Number.isNaN(program.newBrowserWaitTimeout)) {
|
|
818
831
|
throw new ArgError('max new browser wait timeout could not be a negative number, --new-browser-wait-timeout <max-wait-to-browser>');
|
|
819
832
|
}
|
|
820
833
|
|
|
821
|
-
if (program.timeout <= 0 ||
|
|
834
|
+
if (program.timeout <= 0 || Number.isNaN(program.timeout)) {
|
|
822
835
|
throw new ArgError('test run timeout could not be a negative number, --timeout <run-timeout>');
|
|
823
836
|
}
|
|
824
837
|
|
|
825
|
-
if (program.beforeParallel <= 0 ||
|
|
838
|
+
if (program.beforeParallel <= 0 || Number.isNaN(program.beforeParallel)) {
|
|
826
839
|
throw new ArgError('before-parallel could not be a negative number or not number, --before-parallel <number-of-tests>');
|
|
827
840
|
}
|
|
828
841
|
|
|
829
|
-
if (program.parallel <= 0 ||
|
|
842
|
+
if (program.parallel <= 0 || Number.isNaN(program.parallel)) {
|
|
830
843
|
throw new ArgError('parallel could not be a negative number or not number, --parallel <number-of-tests>');
|
|
831
844
|
}
|
|
832
845
|
|
|
833
|
-
if (program.afterParallel <= 0 ||
|
|
846
|
+
if (program.afterParallel <= 0 || Number.isNaN(program.afterParallel)) {
|
|
834
847
|
throw new ArgError('after-parallel could not be a negative number or not number, --after-parallel <number-of-tests>');
|
|
835
848
|
}
|
|
836
849
|
|
|
@@ -901,7 +914,7 @@ module.exports = {
|
|
|
901
914
|
throw new ArgError('please define exactly one of --grid or --grid-id or --host');
|
|
902
915
|
}
|
|
903
916
|
|
|
904
|
-
if (program.host
|
|
917
|
+
if (program.host?.includes('/')) {
|
|
905
918
|
if (!/^(f|ht)tps?:\/\//i.test(program.host)) {
|
|
906
919
|
program.host = `http://${program.host}`;
|
|
907
920
|
}
|
|
@@ -1009,7 +1022,7 @@ module.exports = {
|
|
|
1009
1022
|
const fileContent = fs.readFileSync(program.tmsFieldFile);
|
|
1010
1023
|
program.tmsCustomFields = JSON.parse(fileContent);
|
|
1011
1024
|
} catch (err) {
|
|
1012
|
-
|
|
1025
|
+
throw new ArgError(`failed to parse field file error: ${err.message}`);
|
|
1013
1026
|
}
|
|
1014
1027
|
}
|
|
1015
1028
|
|
|
@@ -1046,7 +1059,7 @@ module.exports = {
|
|
|
1046
1059
|
const lightweightModeOptions = typeof program.lightweightMode === 'string' ? JSON.parse(program.lightweightMode) : {};
|
|
1047
1060
|
program.lightweightMode = Object.assign({}, DEFAULTS, lightweightModeOptions);
|
|
1048
1061
|
} catch (err) {
|
|
1049
|
-
|
|
1062
|
+
throw new ArgError(`failed to parse lightweightMode settings error: ${err.message}`);
|
|
1050
1063
|
}
|
|
1051
1064
|
} else if (program.turboMode && program.mode === CLI_MODE.EXTENSION) {
|
|
1052
1065
|
program.lightweightMode = {
|
|
@@ -1179,6 +1192,8 @@ module.exports = {
|
|
|
1179
1192
|
driverRequestTimeout: program.driverRequestTimeout,
|
|
1180
1193
|
driverRequestRetries: program.driverRequestRetries,
|
|
1181
1194
|
|
|
1195
|
+
testStartTimeout: program.testStartTimeout,
|
|
1196
|
+
|
|
1182
1197
|
testConfigNames: program.testConfig,
|
|
1183
1198
|
testConfigIds: program.testConfigId,
|
|
1184
1199
|
|
package/runner.js
CHANGED
|
@@ -1,32 +1,34 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/* eslint-disable no-console */
|
|
4
|
-
const { CLI_MODE } = require('./commons/constants');
|
|
5
4
|
const _ = require('lodash');
|
|
6
|
-
const { EDITOR_URL } = require('./commons/config');
|
|
7
|
-
const tunnel = require('./commons/testimTunnel');
|
|
8
5
|
const utils = require('./utils');
|
|
9
6
|
const reporter = require('./reports/reporter');
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
7
|
+
const npmDriver = require('./testimNpmDriver');
|
|
8
|
+
const tunnel = require('./commons/testimTunnel');
|
|
9
|
+
const perf = require('./commons/performance-logger');
|
|
10
|
+
const gridService = require('./services/gridService');
|
|
14
11
|
const analytics = require('./commons/testimAnalytics');
|
|
12
|
+
const featureFlags = require('./commons/featureFlags');
|
|
15
13
|
const branchService = require('./services/branchService');
|
|
16
|
-
const
|
|
14
|
+
const servicesApi = require('./commons/testimServicesApi');
|
|
15
|
+
const TestPlanRunner = require('./runners/TestPlanRunner');
|
|
16
|
+
const socketService = require('./commons/socket/socketService');
|
|
17
|
+
const testimCustomToken = require('./commons/testimCustomToken');
|
|
18
|
+
const labFeaturesService = require('./services/labFeaturesService');
|
|
19
|
+
const featureAvailabilityService = require('./commons/featureAvailabilityService');
|
|
20
|
+
const { getLogger } = require('./commons/logger');
|
|
21
|
+
const { EDITOR_URL } = require('./commons/config');
|
|
22
|
+
const { CLI_MODE } = require('./commons/constants');
|
|
17
23
|
const { ArgError, QuotaDepletedError } = require('./errors');
|
|
18
|
-
const featureFlags = require('./commons/featureFlags');
|
|
19
|
-
const perf = require('./commons/performance-logger');
|
|
20
24
|
const { prepareMockNetwork, initializeUserWithAuth } = require('./commons/prepareRunner');
|
|
21
25
|
|
|
22
26
|
const FREE_PLAN_MINIMUM_BROWSER_TIMEOUT = 30 * 60 * 1000;
|
|
23
27
|
|
|
24
|
-
const TestPlanRunner = require('./runners/TestPlanRunner');
|
|
25
|
-
const labFeaturesService = require('./services/labFeaturesService');
|
|
26
|
-
const featureAvailabilityService = require('./commons/featureAvailabilityService');
|
|
27
28
|
|
|
28
|
-
const logger =
|
|
29
|
+
const logger = getLogger('runner');
|
|
29
30
|
|
|
31
|
+
/** @param {import('./runOptions').RunnerOptions} options */
|
|
30
32
|
function validateCLIRunsAreAllowed(options) {
|
|
31
33
|
const hasCliAccess = _.get(options, 'company.activePlan.premiumFeatures.allowCLI');
|
|
32
34
|
|
|
@@ -34,14 +36,16 @@ function validateCLIRunsAreAllowed(options) {
|
|
|
34
36
|
const projectId = options.project;
|
|
35
37
|
analytics.track(options.authData.uid, 'cli-not-supported', { projectId });
|
|
36
38
|
console.warn('Testim CLI is not supported in this plan');
|
|
39
|
+
// TODO: shouldn't this throw an error? 🤔
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
|
|
43
|
+
/** @param {import('./runOptions').RunnerOptions} options */
|
|
40
44
|
async function validateProjectQuotaNotDepleted(options) {
|
|
41
45
|
const projectId = options.project;
|
|
42
46
|
|
|
43
47
|
const usage = await servicesApi.getUsageForCurrentBillingPeriod(projectId);
|
|
44
|
-
const isExecutionBlocked = usage
|
|
48
|
+
const isExecutionBlocked = usage?.isExecutionBlocked;
|
|
45
49
|
if (!isExecutionBlocked) {
|
|
46
50
|
return;
|
|
47
51
|
}
|
|
@@ -51,6 +55,10 @@ async function validateProjectQuotaNotDepleted(options) {
|
|
|
51
55
|
throw new QuotaDepletedError();
|
|
52
56
|
}
|
|
53
57
|
|
|
58
|
+
/**
|
|
59
|
+
* @param {import('./runOptions').RunnerOptions} options
|
|
60
|
+
* @param {Awaited<ReturnType<initializeUserWithAuth>>['companyByProjectId']} company
|
|
61
|
+
*/
|
|
54
62
|
function validateOptionsForCompany(options, company) {
|
|
55
63
|
const optionsRetention = options.retentionDays;
|
|
56
64
|
if (!optionsRetention) {
|
|
@@ -63,8 +71,9 @@ function validateOptionsForCompany(options, company) {
|
|
|
63
71
|
}
|
|
64
72
|
}
|
|
65
73
|
|
|
74
|
+
/** @param {import('./runOptions').RunnerOptions} options */
|
|
66
75
|
async function validateCliAccount(options) {
|
|
67
|
-
if (options.lightweightMode
|
|
76
|
+
if (options.lightweightMode?.disableQuotaBlocking) {
|
|
68
77
|
return;
|
|
69
78
|
}
|
|
70
79
|
try {
|
|
@@ -73,13 +82,14 @@ async function validateCliAccount(options) {
|
|
|
73
82
|
validateCLIRunsAreAllowed(options),
|
|
74
83
|
]);
|
|
75
84
|
} catch (err) {
|
|
76
|
-
if (
|
|
85
|
+
if ([ArgError, QuotaDepletedError].some(errType => err instanceof errType)) {
|
|
77
86
|
throw err;
|
|
78
87
|
}
|
|
79
88
|
logger.error('could not validate cli account', { err });
|
|
80
89
|
}
|
|
81
90
|
}
|
|
82
91
|
|
|
92
|
+
/** @param {string} projectId */
|
|
83
93
|
function analyticsIdentify(projectId) {
|
|
84
94
|
const authData = testimCustomToken.getTokenV3UserData();
|
|
85
95
|
return analytics.identify({
|
|
@@ -95,6 +105,7 @@ function analyticsIdentify(projectId) {
|
|
|
95
105
|
});
|
|
96
106
|
}
|
|
97
107
|
|
|
108
|
+
/** @param {string} projectId */
|
|
98
109
|
function initSocketServices(projectId, { disableResults = false, disableRemoteStep = false }) {
|
|
99
110
|
if (featureFlags.flags.useNewWSCLI.isEnabled() && !disableResults && !disableRemoteStep) {
|
|
100
111
|
return socketService.connect(projectId);
|
|
@@ -110,6 +121,10 @@ function initSocketServices(projectId, { disableResults = false, disableRemoteSt
|
|
|
110
121
|
return undefined;
|
|
111
122
|
}
|
|
112
123
|
|
|
124
|
+
/**
|
|
125
|
+
* @param {import('./runOptions').RunnerOptions} options
|
|
126
|
+
* @param {Awaited<ReturnType<initializeUserWithAuth>>['branchName']} branchInfoFromServer
|
|
127
|
+
*/
|
|
113
128
|
function setBranch(options, branchInfoFromServer) {
|
|
114
129
|
const { branch, autoDetect } = options;
|
|
115
130
|
branchService.setCurrentBranch(branchInfoFromServer, autoDetect);
|
|
@@ -118,14 +133,19 @@ function setBranch(options, branchInfoFromServer) {
|
|
|
118
133
|
}
|
|
119
134
|
}
|
|
120
135
|
|
|
136
|
+
/** @param {import('./runOptions').RunnerOptions} options */
|
|
121
137
|
async function setSfdcCredential(options) {
|
|
122
|
-
const { projectData: { projectId } } = options;
|
|
138
|
+
const { projectData: { projectId } = {} } = options;
|
|
123
139
|
const branch = branchService.getCurrentBranch();
|
|
124
140
|
if (_.get(options, 'company.activePlan.premiumFeatures.ttaForSalesforce')) {
|
|
125
141
|
options.sfdcCredential = await servicesApi.loadSfdcCredential({ projectId, branch });
|
|
126
142
|
}
|
|
127
143
|
}
|
|
128
144
|
|
|
145
|
+
/**
|
|
146
|
+
* @param {import('./runOptions').RunnerOptions} options
|
|
147
|
+
* @param {Awaited<ReturnType<initializeUserWithAuth>>['companyByProjectId']} company
|
|
148
|
+
*/
|
|
129
149
|
function setCompany(options, company) {
|
|
130
150
|
const { onprem, id, storageBaseUrl, storageType, name, activePlan = {} } = company;
|
|
131
151
|
if (onprem) {
|
|
@@ -161,6 +181,10 @@ function setCompany(options, company) {
|
|
|
161
181
|
};
|
|
162
182
|
}
|
|
163
183
|
|
|
184
|
+
/**
|
|
185
|
+
* @param {import('./runOptions').RunnerOptions} options
|
|
186
|
+
* @param {Awaited<ReturnType<initializeUserWithAuth>>['editorConfig']} editorConfig
|
|
187
|
+
*/
|
|
164
188
|
function setSystemInfo(options, editorConfig) {
|
|
165
189
|
if (EDITOR_URL) {
|
|
166
190
|
options.editorUrl = EDITOR_URL;
|
|
@@ -169,17 +193,30 @@ function setSystemInfo(options, editorConfig) {
|
|
|
169
193
|
options.editorUrl = editorConfig.editorUrl;
|
|
170
194
|
}
|
|
171
195
|
|
|
196
|
+
/**
|
|
197
|
+
* @param {import('./runOptions').RunnerOptions} options
|
|
198
|
+
* @param {Awaited<ReturnType<initializeUserWithAuth>>['allGrids']} allGrids
|
|
199
|
+
*/
|
|
172
200
|
function setAllGrids(options, allGrids) {
|
|
173
201
|
options.allGrids = allGrids;
|
|
174
202
|
}
|
|
175
203
|
|
|
204
|
+
/**
|
|
205
|
+
* @param {import('./runOptions').RunnerOptions} options
|
|
206
|
+
* @param {Awaited<ReturnType<initializeUserWithAuth>>['authData']} authData
|
|
207
|
+
*/
|
|
176
208
|
function setAuthData(options, authData) {
|
|
177
209
|
options.authData = authData;
|
|
178
210
|
}
|
|
179
211
|
|
|
212
|
+
/**
|
|
213
|
+
* @param {import('./runOptions').RunnerOptions} options
|
|
214
|
+
* @param {Awaited<ReturnType<initializeUserWithAuth>>['projectById']} project
|
|
215
|
+
*/
|
|
180
216
|
function setProject(options, project) {
|
|
181
217
|
const { id, name, type, defaults } = project;
|
|
182
218
|
featureFlags.setProjectId(id);
|
|
219
|
+
featureFlags.setProjectType(type);
|
|
183
220
|
options.projectData = {
|
|
184
221
|
projectId: id,
|
|
185
222
|
type,
|
|
@@ -188,10 +225,12 @@ function setProject(options, project) {
|
|
|
188
225
|
};
|
|
189
226
|
}
|
|
190
227
|
|
|
228
|
+
/** @param {import('./runOptions').RunnerOptions} options */
|
|
191
229
|
async function setGrid(options) {
|
|
192
230
|
options.gridData = await gridService.getGridData(options);
|
|
193
231
|
}
|
|
194
232
|
|
|
233
|
+
/** @param {import('./runOptions').RunnerOptions} options */
|
|
195
234
|
async function setMockNetworkRules(options) {
|
|
196
235
|
const { project } = options;
|
|
197
236
|
const props = { projectId: project };
|
|
@@ -203,7 +242,7 @@ async function setMockNetworkRules(options) {
|
|
|
203
242
|
}
|
|
204
243
|
|
|
205
244
|
/**
|
|
206
|
-
* @param {
|
|
245
|
+
* @param {import('./runOptions').RunnerOptions} options
|
|
207
246
|
* @param {string=} customExtensionLocalLocation
|
|
208
247
|
*/
|
|
209
248
|
async function runRunner(options, customExtensionLocalLocation) {
|
|
@@ -235,8 +274,9 @@ async function runRunner(options, customExtensionLocalLocation) {
|
|
|
235
274
|
return results;
|
|
236
275
|
}
|
|
237
276
|
|
|
277
|
+
/** @param {import('./runOptions').RunnerOptions} options */
|
|
238
278
|
function showFreeGridRunWarningIfNeeded(options) {
|
|
239
|
-
if (featureAvailabilityService.shouldShowFreeGridRunWarning(options.gridData
|
|
279
|
+
if (featureAvailabilityService.shouldShowFreeGridRunWarning(options.gridData?.type)) {
|
|
240
280
|
const CYAN = '\x1b[36m';
|
|
241
281
|
const UNDERSCORE = '\x1b[4m';
|
|
242
282
|
const RESET = '\x1b[0m';
|
|
@@ -251,15 +291,15 @@ function showFreeGridRunWarningIfNeeded(options) {
|
|
|
251
291
|
* - Reporting the user to analytics
|
|
252
292
|
* - Authenticating the user and exchanging their token for a jwt
|
|
253
293
|
* - Sets the grids for the company and validates the user has permission to run the CLI
|
|
254
|
-
* @param {
|
|
294
|
+
* @param {import('./runOptions').RunnerOptions} options - the run options passed to the CLI, namely the project and token
|
|
255
295
|
*/
|
|
256
296
|
async function init(options) {
|
|
257
297
|
perf.log('start runner init');
|
|
258
298
|
const { project, lightweightMode, useChromeLauncher, mode, disableSockets } = options;
|
|
259
299
|
const featureFlagsReady = featureFlags.fetch();
|
|
260
300
|
const socketConnected = initSocketServices(project, {
|
|
261
|
-
disableResults: disableSockets || Boolean(lightweightMode
|
|
262
|
-
disableRemoteStep: disableSockets || Boolean(lightweightMode
|
|
301
|
+
disableResults: disableSockets || Boolean(lightweightMode?.disableResults && (useChromeLauncher || mode !== 'extension')),
|
|
302
|
+
disableRemoteStep: disableSockets || Boolean(lightweightMode?.disableRemoteStep),
|
|
263
303
|
});
|
|
264
304
|
|
|
265
305
|
featureFlagsReady.catch(() => {}); // suppress unhandled rejection
|
|
@@ -279,7 +319,7 @@ async function init(options) {
|
|
|
279
319
|
setAuthData(options, authData);
|
|
280
320
|
await setSfdcCredential(options);
|
|
281
321
|
|
|
282
|
-
if (!
|
|
322
|
+
if (!options.lightweightMode?.disableLabs) {
|
|
283
323
|
await labFeaturesService.loadLabFeatures(projectById.id, companyByProjectId.activePlan);
|
|
284
324
|
}
|
|
285
325
|
|
|
@@ -54,20 +54,21 @@ class ParallelWorkerManager {
|
|
|
54
54
|
|
|
55
55
|
let stoppedOnError = false;
|
|
56
56
|
let runningTests = 0;
|
|
57
|
-
const runAndWaitToComplete = token => new Promise((resolve
|
|
57
|
+
const runAndWaitToComplete = token => new Promise((resolve) => {
|
|
58
58
|
const projectId = options.project;
|
|
59
59
|
const executionQueue = new ExecutionQueue(executionId, executionName, testList, options, branchToUse, testStatus);
|
|
60
60
|
|
|
61
|
+
/** @type {{ [testResultId: string]: any; }} */
|
|
61
62
|
const combinedTestResults = {};
|
|
62
63
|
const testCount = testList.length;
|
|
63
64
|
|
|
64
|
-
const companyId = options.company
|
|
65
|
-
const companyName = options.company
|
|
65
|
+
const companyId = options.company?.companyId;
|
|
66
|
+
const companyName = options.company?.name;
|
|
66
67
|
const source = options.source || 'cli';
|
|
67
68
|
const user = options.user;
|
|
68
|
-
const companyPlan = options.company
|
|
69
|
-
const isStartUp = options.company
|
|
70
|
-
const projectName = options.projectData
|
|
69
|
+
const companyPlan = options.company?.planType;
|
|
70
|
+
const isStartUp = options.company?.isStartUp;
|
|
71
|
+
const projectName = options.projectData?.name;
|
|
71
72
|
const lightweightMode = options.lightweightMode;
|
|
72
73
|
const sessionType = utils.getSessionType(options);
|
|
73
74
|
|
|
@@ -95,7 +96,7 @@ class ParallelWorkerManager {
|
|
|
95
96
|
const onTestCompleted = async (wid, testId, testResult, sessionId, isRerun) => {
|
|
96
97
|
runningTests--;
|
|
97
98
|
const update = {};
|
|
98
|
-
if (lightweightMode
|
|
99
|
+
if (lightweightMode?.onlyTestIdsNoSuite) {
|
|
99
100
|
update.show = true;
|
|
100
101
|
}
|
|
101
102
|
if (testResult.seleniumStats) {
|
|
@@ -115,9 +116,9 @@ class ParallelWorkerManager {
|
|
|
115
116
|
update.gridHost = options.host;
|
|
116
117
|
}
|
|
117
118
|
if (options.grid || options.gridId) {
|
|
118
|
-
update.gridName = options.grid ||
|
|
119
|
-
update.gridType = options.gridData
|
|
120
|
-
update.gridProvider = options.gridData
|
|
119
|
+
update.gridName = options.grid || options.gridData?.name;
|
|
120
|
+
update.gridType = options.gridData?.type;
|
|
121
|
+
update.gridProvider = options.gridData?.provider;
|
|
121
122
|
} else if (options.useLocalChromeDriver) {
|
|
122
123
|
update.gridName = 'local-chrome-driver-from-options';
|
|
123
124
|
update.gridType = 'local-chrome';
|
|
@@ -177,7 +178,7 @@ class ParallelWorkerManager {
|
|
|
177
178
|
}
|
|
178
179
|
};
|
|
179
180
|
|
|
180
|
-
const onGridSlot = (
|
|
181
|
+
const onGridSlot = (_executionId, resultId, gridInfo) => testStatus.onGridSlot(_executionId, resultId, gridInfo);
|
|
181
182
|
|
|
182
183
|
options.userData = {
|
|
183
184
|
loginData: Object.assign({}, testimCustomToken.getTokenV3UserData(), {
|
|
@@ -214,7 +215,6 @@ class ParallelWorkerManager {
|
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
217
|
|
|
217
|
-
|
|
218
218
|
function schedule(fn, index) {
|
|
219
219
|
if (index === 0) {
|
|
220
220
|
fn();
|