@testim/testim-cli 3.253.0 → 3.255.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/OverrideTestDataBuilder.js +1 -1
- package/agent/routers/cliJsCode/index.js +4 -4
- package/agent/routers/cliJsCode/router.js +46 -42
- package/agent/routers/cliJsCode/service.js +18 -13
- package/agent/routers/codim/router.js +14 -17
- package/agent/routers/codim/router.test.js +19 -21
- package/agent/routers/codim/service.js +16 -16
- package/agent/routers/general/index.js +4 -8
- package/agent/routers/hybrid/registerRoutes.js +18 -18
- package/agent/routers/index.js +7 -7
- package/agent/routers/playground/router.js +11 -10
- package/agent/routers/playground/service.js +22 -23
- package/agent/routers/standalone-browser/registerRoutes.js +10 -10
- package/cdpTestRunner.js +4 -3
- package/chromiumInstaller.js +4 -5
- package/cli/onExit.js +2 -2
- package/cli.js +11 -10
- package/cliAgentMode.js +8 -8
- package/codim/codim-cli.js +20 -17
- package/codim/hybrid-utils.js +1 -1
- package/codim/measure-perf.js +9 -6
- package/codim/template.js/tests/examples/01-simple-text-validation.test.js +6 -6
- package/codim/template.js/tests/examples/02-using-locators.test.js +13 -15
- package/codim/template.js/tests/examples/03-using-hooks.test.js +17 -19
- package/codim/template.js/tests/examples/04-skip-and-only.test.js +16 -17
- package/codim/template.js/tests/examples/05-multiple-windows.test.js +16 -17
- package/codim/template.js/webpack.config.js +1 -1
- package/codim/template.ts/webpack.config.js +3 -3
- package/commons/AbortError.js +4 -4
- package/commons/detectDebugger.js +4 -2
- package/commons/featureFlags.js +8 -0
- package/commons/httpRequest.js +5 -1
- package/commons/httpRequestCounters.js +21 -10
- package/commons/lazyRequire.js +14 -12
- package/commons/logger.js +4 -4
- package/commons/performance-logger.js +14 -8
- package/commons/preloadTests.js +2 -2
- package/commons/prepareRunner.js +4 -2
- package/commons/prepareRunnerAndTestimStartUtils.js +40 -42
- package/commons/runnerFileCache.js +1 -1
- package/commons/socket/baseSocketServiceSocketIO.js +32 -34
- package/commons/socket/realDataService.js +6 -5
- package/commons/socket/realDataServiceSocketIO.js +4 -4
- package/commons/socket/remoteStepService.js +4 -3
- package/commons/socket/remoteStepServiceSocketIO.js +11 -12
- package/commons/socket/socketService.js +50 -52
- package/commons/socket/testResultServiceSocketIO.js +11 -11
- package/commons/testimDesiredCapabilitiesBuilder.js +3 -2
- package/commons/testimNgrok.js +2 -2
- package/commons/testimNgrok.test.js +1 -1
- package/commons/testimServicesApi.js +27 -20
- package/commons/testimTunnel.test.js +2 -1
- package/commons/xhr2.js +97 -100
- package/coverage/SummaryToObjectReport.js +0 -1
- package/coverage/jsCoverage.js +12 -10
- package/errors.js +5 -0
- package/fixLocalBuild.js +2 -0
- package/inputFileUtils.js +11 -9
- package/npm-shrinkwrap.json +2286 -1284
- package/package.json +9 -8
- package/player/appiumTestPlayer.js +1 -1
- package/player/chromeLauncherTestPlayer.js +0 -1
- package/player/services/tabService.js +15 -1
- package/player/services/tabServiceMock.js +166 -0
- package/player/stepActions/locateStepAction.js +2 -0
- package/player/stepActions/navigationStepAction.js +11 -10
- package/player/stepActions/sleepStepAction.js +4 -5
- package/player/stepActions/textStepAction.js +4 -11
- package/player/utils/imageCaptureUtils.js +81 -120
- package/player/utils/windowUtils.js +4 -3
- package/player/webdriver.js +26 -23
- package/processHandler.js +3 -3
- package/processHandler.test.js +1 -1
- package/reports/consoleReporter.js +3 -2
- package/reports/junitReporter.js +7 -9
- package/reports/reporter.js +34 -39
- package/runOptions.d.ts +260 -0
- package/runOptions.js +59 -44
- package/runner.js +14 -0
- package/runners/ParallelWorkerManager.js +9 -10
- package/runners/TestPlanRunner.js +142 -78
- package/runners/buildCodeTests.js +38 -37
- package/runners/runnerUtils.js +3 -3
- package/services/gridService.js +36 -40
- package/services/lambdatestService.js +3 -5
- package/stepPlayers/cliJsStepPlayback.js +22 -17
- package/testRunHandler.js +8 -0
- package/testRunStatus.js +9 -6
- package/utils/argsUtils.js +86 -0
- package/utils/argsUtils.test.js +32 -0
- package/utils/fsUtils.js +154 -0
- package/{utils.js → utils/index.js} +19 -262
- package/utils/promiseUtils.js +89 -0
- package/utils/stringUtils.js +98 -0
- package/utils/stringUtils.test.js +22 -0
- package/utils/timeUtils.js +25 -0
- package/utils/utils.test.js +27 -0
- package/workers/BaseWorker.js +16 -14
- package/workers/WorkerAppium.js +1 -1
- package/workers/WorkerExtension.js +6 -7
- package/workers/WorkerExtensionSingleBrowser.js +4 -4
- package/workers/WorkerSelenium.js +5 -2
- package/utils.test.js +0 -68
|
@@ -7,30 +7,28 @@ const { go, test, describe, before, beforeEach, afterEach, after, text } = requi
|
|
|
7
7
|
const { expect } = require('chai');
|
|
8
8
|
|
|
9
9
|
describe('Using hooks', () => {
|
|
10
|
+
before(() => {
|
|
11
|
+
// runs before all tests in this block
|
|
12
|
+
});
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
// runs before each test in this block
|
|
16
|
+
await go('http://demo.testim.io');
|
|
17
|
+
});
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
// runs before each test in this block
|
|
17
|
-
await go('http://demo.testim.io');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
afterEach(function() {
|
|
19
|
+
afterEach(() => {
|
|
21
20
|
// runs after each test in this block
|
|
22
|
-
|
|
21
|
+
});
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
after(() => {
|
|
25
24
|
// runs after all tests in this block
|
|
26
|
-
|
|
25
|
+
});
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
test('finds text element', async () => {
|
|
29
28
|
// Find an element using a vanilla CSS selector and extract its text
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
const title = await text('.theme__title___35Wsy');
|
|
30
|
+
|
|
31
|
+
// Validate the extracted text
|
|
32
|
+
expect(title).to.eq('Madan');
|
|
33
|
+
});
|
|
36
34
|
});
|
|
@@ -8,25 +8,24 @@ const { expect } = require('chai');
|
|
|
8
8
|
|
|
9
9
|
// Using `.only()`
|
|
10
10
|
describe.only('Test suite', () => {
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
await go('http://demo.testim.io');
|
|
13
|
+
});
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
test('finds text element', async () => {
|
|
16
|
+
const title = await text('.theme__title___35Wsy');
|
|
17
|
+
expect(title).to.eq('Madan');
|
|
18
|
+
});
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
test.skip('skips this test', async () => {
|
|
23
|
-
// this test will not be run
|
|
24
|
-
const title = await text('.Gallery__headline-1___2lHj5');
|
|
25
|
-
expect(title).to.eq('Your next destination');
|
|
26
|
-
});
|
|
20
|
+
// Using `skip()`
|
|
21
|
+
test.skip('skips this test', async () => {
|
|
22
|
+
// this test will not be run
|
|
23
|
+
const title = await text('.Gallery__headline-1___2lHj5');
|
|
24
|
+
expect(title).to.eq('Your next destination');
|
|
25
|
+
});
|
|
27
26
|
});
|
|
28
27
|
|
|
29
|
-
describe('Just another test', async() => {
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
describe('Just another test', async () => {
|
|
29
|
+
// this describe will not be run since the first describe
|
|
30
|
+
// above is using `.only()`
|
|
32
31
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
3
|
// Import Testim Dev Kit methods
|
|
4
4
|
const { go, click, waitForText, withContext, test, l, Locator } = require('testim');
|
|
@@ -7,26 +7,25 @@ const { go, click, waitForText, withContext, test, l, Locator } = require('testi
|
|
|
7
7
|
Locator.set(require('./locators/locators.js'));
|
|
8
8
|
|
|
9
9
|
test('Working with multiple windows', async () => {
|
|
10
|
-
|
|
10
|
+
await go('https://the-internet.herokuapp.com/');
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
await click(l('Multiple_Windows'));
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
// Open a new tab
|
|
15
|
+
await click(l('Click_Here'));
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// Import a `waitForText` method in the context of the newly opened tab
|
|
21
|
-
// using the `withContext` method (@see https://help.testim.io/docs/with-context)
|
|
22
|
-
// Please note we're using an alias since we've already imported `waitForText`
|
|
23
|
-
// to be used in the context of the main tab.
|
|
24
|
-
const { waitForText: waitForTextForTab } = withContext({
|
|
25
|
-
tabUrl: 'https://the-internet.herokuapp.com/windows/new'
|
|
26
|
-
});
|
|
17
|
+
// Run validation on the main tab
|
|
18
|
+
await waitForText(l('Opening_a_new_window'), 'Opening a new window');
|
|
27
19
|
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
// Import a `waitForText` method in the context of the newly opened tab
|
|
21
|
+
// using the `withContext` method (@see https://help.testim.io/docs/with-context)
|
|
22
|
+
// Please note we're using an alias since we've already imported `waitForText`
|
|
23
|
+
// to be used in the context of the main tab.
|
|
24
|
+
const { waitForText: waitForTextForTab } = withContext({
|
|
25
|
+
tabUrl: 'https://the-internet.herokuapp.com/windows/new',
|
|
26
|
+
});
|
|
30
27
|
|
|
28
|
+
// Run validation on the newly opened tab
|
|
29
|
+
await waitForTextForTab(l('New_Window'), 'New Window');
|
|
31
30
|
}); // end of test
|
|
32
31
|
|
package/commons/AbortError.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
class AbortError extends Error {
|
|
4
|
-
constructor(message =
|
|
4
|
+
constructor(message = 'aborted') {
|
|
5
5
|
super(message);
|
|
6
|
-
this.name =
|
|
6
|
+
this.name = 'AbortError';
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
module.exports = {
|
|
11
|
-
AbortError
|
|
12
|
-
}
|
|
11
|
+
AbortError,
|
|
12
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
2
3
|
const { DISABLE_DEBUGGER_INFINITE_TIMEOUT } = require('./config');
|
|
3
4
|
|
|
4
5
|
module.exports.isDebuggerConnected = () => {
|
|
@@ -12,6 +13,7 @@ module.exports.isDebuggerConnected = () => {
|
|
|
12
13
|
return true;
|
|
13
14
|
}
|
|
14
15
|
} catch (e) {
|
|
15
|
-
|
|
16
|
+
/* empty */
|
|
16
17
|
}
|
|
18
|
+
return false;
|
|
17
19
|
};
|
package/commons/featureFlags.js
CHANGED
|
@@ -67,6 +67,14 @@ class FeatureFlagsService {
|
|
|
67
67
|
Rox.setCustomStringProperty('projectId', projectId);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
setProjectType(projectType) {
|
|
71
|
+
Rox.setCustomStringProperty('projectType', projectType);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
setCompanyProductType(productType) {
|
|
75
|
+
Rox.setCustomStringProperty('productType', productType);
|
|
76
|
+
}
|
|
77
|
+
|
|
70
78
|
setCompanyId(companyId) {
|
|
71
79
|
Rox.setCustomStringProperty('companyId', companyId);
|
|
72
80
|
}
|
package/commons/httpRequest.js
CHANGED
|
@@ -79,7 +79,7 @@ function post({
|
|
|
79
79
|
.catch(logErrorAndRethrow('failed to post request', { url }));
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
function postFullRes(url, body, headers = {}, timeout = DEFAULT_REQUEST_TIMEOUT, retry) {
|
|
82
|
+
function postFullRes(url, body, headers = {}, timeout = DEFAULT_REQUEST_TIMEOUT, retry = 0) {
|
|
83
83
|
const request = superagent
|
|
84
84
|
.post(url)
|
|
85
85
|
.send(body)
|
|
@@ -215,6 +215,10 @@ function put(url, body, headers = {}, timeout = DEFAULT_REQUEST_TIMEOUT) {
|
|
|
215
215
|
.catch(logErrorAndRethrow('failed to put request', { url }));
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
/**
|
|
219
|
+
* @param {string} url
|
|
220
|
+
* @returns {Promise<superagent.Response>}
|
|
221
|
+
*/
|
|
218
222
|
function download(url) {
|
|
219
223
|
logger.info('start to download', { url });
|
|
220
224
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
'use
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
|
-
const { sum } = require('lodash');
|
|
4
|
-
const Bluebird = require('bluebird');
|
|
5
|
-
const dns = require('dns');
|
|
6
3
|
const _ = require('lodash');
|
|
7
4
|
const config = require('./config');
|
|
5
|
+
const dns = require('dns').promises;
|
|
6
|
+
const Bluebird = require('bluebird');
|
|
7
|
+
const { sum } = require('lodash');
|
|
8
|
+
const { promiseMap } = require('../utils/promiseUtils');
|
|
8
9
|
|
|
9
10
|
const logger = require('./logger').getLogger('http-request-counters');
|
|
10
11
|
|
|
@@ -18,7 +19,7 @@ const testNetworkConnectivity = async () => {
|
|
|
18
19
|
const hostnames = ['www.google.com', 'www.facebook.com', 'www.microsoft.com', 'testim.io'];
|
|
19
20
|
try {
|
|
20
21
|
// If any of these domains resolve we consider the connectivity to be ok
|
|
21
|
-
const result = Boolean(await
|
|
22
|
+
const result = Boolean(await promiseMap(hostnames, host => dns.lookup(host)));
|
|
22
23
|
if (!result) {
|
|
23
24
|
networkConnectivityTestFailed = true;
|
|
24
25
|
}
|
|
@@ -40,18 +41,28 @@ const ttl = 60 * 1000 * 15;
|
|
|
40
41
|
|
|
41
42
|
module.exports.makeCounters = () => {
|
|
42
43
|
const counters = {
|
|
43
|
-
call: new Map(),
|
|
44
|
-
success: new Map(),
|
|
45
|
-
fail: new Map(),
|
|
44
|
+
/** @type {Map<string, number>} */ call: new Map(),
|
|
45
|
+
/** @type {Map<string, number>} */ success: new Map(),
|
|
46
|
+
/** @type {Map<string, number>} */ fail: new Map(),
|
|
46
47
|
};
|
|
48
|
+
/**
|
|
49
|
+
* @param {counters[keyof counters]} counter
|
|
50
|
+
* @param {string} key
|
|
51
|
+
*/
|
|
47
52
|
function update(counter, key) {
|
|
48
53
|
const result = counter.get(key) || 0;
|
|
49
54
|
counter.set(key, result + 1);
|
|
50
55
|
setTimeout(() => {
|
|
51
|
-
const
|
|
52
|
-
counter.set(key,
|
|
56
|
+
const _result = counter.get(key) || 1;
|
|
57
|
+
counter.set(key, _result - 1);
|
|
53
58
|
}, ttl);
|
|
54
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* @template T, TArgs
|
|
62
|
+
* @param {(...args: TArgs) => T} fn
|
|
63
|
+
* @param {string=} name
|
|
64
|
+
* @return {(...args: TArgs) => Bluebird<Awaited<T>>}
|
|
65
|
+
*/
|
|
55
66
|
function wrapWithMonitoring(fn, name = fn.name) {
|
|
56
67
|
return Bluebird.method(async function (...args) {
|
|
57
68
|
update(counters.call, name);
|
package/commons/lazyRequire.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
|
-
const Bluebird = require('bluebird');
|
|
4
3
|
const npmWrapper = require('./npmWrapper');
|
|
5
4
|
const ora = require('ora');
|
|
6
5
|
const path = require('path');
|
|
7
|
-
const logger = require('./logger').getLogger(
|
|
6
|
+
const logger = require('./logger').getLogger('lazy-require');
|
|
8
7
|
const { getCliLocation } = require('../utils');
|
|
9
|
-
const { requireWithFallback } = require(
|
|
8
|
+
const { requireWithFallback } = require('./requireWithFallback');
|
|
10
9
|
|
|
10
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
11
11
|
const packageJson = require(path.resolve(getCliLocation(), 'package.json'));
|
|
12
12
|
const ongoingCalls = new Map();
|
|
13
13
|
|
|
@@ -29,14 +29,14 @@ module.exports = async function memoizedLazyRequireApi(dependency, options = {})
|
|
|
29
29
|
}
|
|
30
30
|
return requiredModule;
|
|
31
31
|
} catch (error) {
|
|
32
|
-
logger.warn(
|
|
32
|
+
logger.warn('failed to install dependency lazily', { dependency, err: error });
|
|
33
33
|
const depVersionToInstall = getVersionOfLazyDep(dependency);
|
|
34
34
|
const depWithVersions = `${dependency}@${depVersionToInstall}`;
|
|
35
35
|
|
|
36
36
|
const removeGlobal = process.argv.includes('npx');
|
|
37
|
-
const globalFlag = removeGlobal ? '' : '-g '
|
|
37
|
+
const globalFlag = removeGlobal ? '' : '-g ';
|
|
38
38
|
const errorMessage = `Installation of ${dependency} failed. This typically means you are running an out-of-date version of Node.js or NPM.` +
|
|
39
|
-
`Please manually install by running the following command: npm install ${globalFlag}${depWithVersions}
|
|
39
|
+
`Please manually install by running the following command: npm install ${globalFlag}${depWithVersions}`;
|
|
40
40
|
|
|
41
41
|
if (spinner) {
|
|
42
42
|
spinner.fail(errorMessage);
|
|
@@ -52,12 +52,12 @@ async function memoizedLazyRequire(identifier, options = {}) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
ongoingCalls.set(identifier, lazyRequireImpl(identifier, options));
|
|
55
|
-
ongoingCalls.get(identifier).catch(
|
|
55
|
+
ongoingCalls.get(identifier).catch(() => {
|
|
56
56
|
ongoingCalls.delete(identifier);
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
return ongoingCalls.get(identifier);
|
|
60
|
-
}
|
|
60
|
+
}
|
|
61
61
|
|
|
62
62
|
async function lazyRequireImpl(dependency) {
|
|
63
63
|
const packageLocally = npmWrapper.getPackageIfInstalledLocally(dependency);
|
|
@@ -75,10 +75,12 @@ async function lazyRequireImpl(dependency) {
|
|
|
75
75
|
return requireWithFallback(dependency);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
function installAllLazyDependencies() {
|
|
78
|
+
async function installAllLazyDependencies() {
|
|
79
79
|
const allLazyDependencies = Object.keys(packageJson.lazyDependencies);
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
for (const dep of allLazyDependencies) {
|
|
82
|
+
await lazyRequireImpl(dep);
|
|
83
|
+
}
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
if (require.main === module) {
|
|
@@ -94,7 +96,7 @@ function getVersionOfLazyDep(dependencyToFind) {
|
|
|
94
96
|
.find(([dep]) => dep === dependencyToFind);
|
|
95
97
|
|
|
96
98
|
if (!depEntry) {
|
|
97
|
-
throw new Error(
|
|
99
|
+
throw new Error('The given package name is not lazyDependencies');
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
return depEntry[1];
|
package/commons/logger.js
CHANGED
|
@@ -53,7 +53,7 @@ function getStreamsAndWaitForFlushPromise() {
|
|
|
53
53
|
const [transports, waitForFlush] = getStreamsAndWaitForFlushPromise();
|
|
54
54
|
const level = config.LOGGER_DEBUG ? 'debug' : 'info';
|
|
55
55
|
const defaultMeta = {};
|
|
56
|
-
if (isLocal.
|
|
56
|
+
if (!isLocal.includes('@echo')) {
|
|
57
57
|
Object.assign(defaultMeta, devFlags());
|
|
58
58
|
|
|
59
59
|
} else {
|
|
@@ -149,13 +149,13 @@ class Logger {
|
|
|
149
149
|
this.innerLog('crit', msg, data);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
innerLog(
|
|
152
|
+
innerLog(logLevel, msg, data = {}) {
|
|
153
153
|
try {
|
|
154
|
-
this._logger.log(
|
|
154
|
+
this._logger.log(logLevel, Object.assign({ meta: data }, { message: msg }, addExecutionMetadata(data.executionId)));
|
|
155
155
|
} catch (err) {
|
|
156
156
|
try {
|
|
157
157
|
this._logger.log('crit', Object.assign({ message: `failed to log message ${err.message}, ${err.stack}` }, addExecutionMetadata(data.executionId)));
|
|
158
|
-
} catch (
|
|
158
|
+
} catch (_err) {
|
|
159
159
|
// well what can we do
|
|
160
160
|
}
|
|
161
161
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
2
4
|
|
|
3
5
|
const MEASURE_TESTIM_CLI_PERFORMANCE = process.env.MEASURE_TESTIM_CLI_PERFORMANCE;
|
|
4
|
-
|
|
6
|
+
const epoch = Date.now();
|
|
5
7
|
let last = Date.now();
|
|
6
8
|
|
|
7
9
|
module.exports = {
|
|
@@ -9,7 +11,7 @@ module.exports = {
|
|
|
9
11
|
if (!MEASURE_TESTIM_CLI_PERFORMANCE) {
|
|
10
12
|
return;
|
|
11
13
|
}
|
|
12
|
-
|
|
14
|
+
const time = Date.now();
|
|
13
15
|
console.log(`${time - epoch}\t\t\t${time - last}\t\t\t`, ...args);
|
|
14
16
|
last = time;
|
|
15
17
|
},
|
|
@@ -25,7 +27,9 @@ module.exports = {
|
|
|
25
27
|
}
|
|
26
28
|
},
|
|
27
29
|
patchPromisePrototype() {
|
|
28
|
-
|
|
30
|
+
// TODO: Stop monkey patching shit
|
|
31
|
+
// eslint-disable-next-line no-extend-native
|
|
32
|
+
Promise.prototype.log = function log(message) {
|
|
29
33
|
if (!MEASURE_TESTIM_CLI_PERFORMANCE) {
|
|
30
34
|
return this;
|
|
31
35
|
}
|
|
@@ -40,7 +44,7 @@ module.exports = {
|
|
|
40
44
|
return this;
|
|
41
45
|
}
|
|
42
46
|
return this.tap(() => module.exports.log(message));
|
|
43
|
-
}
|
|
47
|
+
};
|
|
44
48
|
},
|
|
45
49
|
measureRequireTimes() {
|
|
46
50
|
if (!MEASURE_TESTIM_CLI_PERFORMANCE) {
|
|
@@ -48,12 +52,14 @@ module.exports = {
|
|
|
48
52
|
}
|
|
49
53
|
const {
|
|
50
54
|
performance,
|
|
51
|
-
PerformanceObserver
|
|
55
|
+
PerformanceObserver,
|
|
52
56
|
} = require('perf_hooks');
|
|
53
57
|
const mod = require('module');
|
|
54
58
|
|
|
55
59
|
// Monkey patch the require function
|
|
56
60
|
mod.Module.prototype.require = performance.timerify(mod.Module.prototype.require);
|
|
61
|
+
// TODO: Stop monkey patching shit
|
|
62
|
+
// eslint-disable-next-line no-global-assign
|
|
57
63
|
require = performance.timerify(require);
|
|
58
64
|
|
|
59
65
|
// Activate the observer
|
|
@@ -65,8 +71,8 @@ module.exports = {
|
|
|
65
71
|
obs.disconnect();
|
|
66
72
|
});
|
|
67
73
|
obs.observe({ entryTypes: ['function'], buffered: true });
|
|
68
|
-
}
|
|
69
|
-
}
|
|
74
|
+
},
|
|
75
|
+
};
|
|
70
76
|
|
|
71
77
|
// ew ~ Benji
|
|
72
78
|
module.exports.patchPromisePrototype();
|
package/commons/preloadTests.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const Promise = require('bluebird');
|
|
2
1
|
const _ = require('lodash');
|
|
2
|
+
const { promiseMap } = require('../utils');
|
|
3
3
|
const localRunnerCache = require('./runnerFileCache');
|
|
4
4
|
const servicesApi = require('./testimServicesApi.js');
|
|
5
5
|
|
|
@@ -15,7 +15,7 @@ async function preloadTests(options) {
|
|
|
15
15
|
projectId: options.project,
|
|
16
16
|
};
|
|
17
17
|
return await localRunnerCache.memoize(async () => {
|
|
18
|
-
const results = await
|
|
18
|
+
const results = await promiseMap(options.testId, testId => servicesApi.loadTest({ ...opts, testId }), { concurrency: 2 });
|
|
19
19
|
return _.keyBy(results, 'testData.id');
|
|
20
20
|
}, 'loadTests', TEN_HOURS, [opts, options.testId])();
|
|
21
21
|
}
|
package/commons/prepareRunner.js
CHANGED
|
@@ -24,7 +24,7 @@ Promise.resolve().then(() => {
|
|
|
24
24
|
|
|
25
25
|
async function prepare(options) {
|
|
26
26
|
/**
|
|
27
|
-
* @type {Promise}
|
|
27
|
+
* @type {globalThis.Promise<void>}
|
|
28
28
|
*/
|
|
29
29
|
let chromedriverPromise = Promise.resolve();
|
|
30
30
|
|
|
@@ -35,7 +35,7 @@ async function prepare(options) {
|
|
|
35
35
|
chromedriverPromise = prepareRunnerAndTestimStartUtils.prepareChromeDriver(
|
|
36
36
|
{ projectId: options.project, userId: options.user },
|
|
37
37
|
{ chromeBinaryLocation: options.chromeBinaryLocation },
|
|
38
|
-
Boolean(options.lightweightMode
|
|
38
|
+
Boolean(options.lightweightMode?.general)
|
|
39
39
|
);
|
|
40
40
|
options.useLocalChromeDriver = true;
|
|
41
41
|
}
|
|
@@ -61,6 +61,8 @@ async function prepare(options) {
|
|
|
61
61
|
|
|
62
62
|
async function prepareMockNetwork(location) {
|
|
63
63
|
logger.info('prepare MockNetwork', { location });
|
|
64
|
+
/** @type {Buffer} */
|
|
65
|
+
// @ts-expect-error There seems to be an actual bug in case the location is a URL.
|
|
64
66
|
const rulesJsonBuf = await utils.getSourceAsBuffer(location);
|
|
65
67
|
if (rulesJsonBuf.byteLength > MAX_RULE_FILE_SIZE_IN_MB * 1000000) {
|
|
66
68
|
throw new Error(`${PREPARE_MOCK_NETWORK_ERROR_PREFIX} exceeded ${MAX_RULE_FILE_SIZE_IN_MB}MB`);
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
// @ts-ignore
|
|
4
|
-
const Promise = require('bluebird');
|
|
5
3
|
const path = require('path');
|
|
6
|
-
const
|
|
7
|
-
const fs = Promise.promisifyAll(require('fs'));
|
|
4
|
+
const fs = require('fs');
|
|
8
5
|
const ms = require('ms');
|
|
9
6
|
const { serializeError } = require('serialize-error');
|
|
10
7
|
const { additionalLogDetails } = require('./logUtils');
|
|
@@ -12,7 +9,15 @@ const { additionalLogDetails } = require('./logUtils');
|
|
|
12
9
|
const config = require('./config');
|
|
13
10
|
const { ArgError, NpmPermissionsError } = require('../errors');
|
|
14
11
|
const {
|
|
15
|
-
getCliLocation,
|
|
12
|
+
getCliLocation,
|
|
13
|
+
isURL,
|
|
14
|
+
downloadAndSave,
|
|
15
|
+
getSource,
|
|
16
|
+
getLocalFileSizeInMB,
|
|
17
|
+
download,
|
|
18
|
+
unzipFile,
|
|
19
|
+
getSourcePath,
|
|
20
|
+
promiseMap,
|
|
16
21
|
} = require('../utils');
|
|
17
22
|
const localRunnerCache = require('./runnerFileCache');
|
|
18
23
|
const logger = require('./logger').getLogger('prepare runner and testim start');
|
|
@@ -32,63 +37,58 @@ module.exports = {
|
|
|
32
37
|
/**
|
|
33
38
|
* @param {string} location
|
|
34
39
|
*/
|
|
35
|
-
function prepareCustomExtension(location, unlimitedSize = false) {
|
|
40
|
+
async function prepareCustomExtension(location, unlimitedSize = false) {
|
|
36
41
|
if (!location) {
|
|
37
|
-
return
|
|
42
|
+
return undefined;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
if (isURL(location)) {
|
|
41
46
|
const destFile = path.join(process.cwd(), location.replace(/^.*[\\\/]/, ''));
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
})
|
|
49
|
-
.then(() => Promise.resolve(destFile));
|
|
47
|
+
const contentLength = await getRemoteFileSizeInMB(location);
|
|
48
|
+
if (contentLength > MAX_CUSTOM_EXT_SIZE_MB && !unlimitedSize) {
|
|
49
|
+
throw new ArgError(MAX_CUSTOM_SIZE_ERROR_MSG);
|
|
50
|
+
}
|
|
51
|
+
await downloadAndSave(location, destFile);
|
|
52
|
+
return destFile;
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
const destFile = path.resolve(location);
|
|
53
56
|
if (!fs.existsSync(destFile)) {
|
|
54
|
-
|
|
57
|
+
throw new ArgError(`Failed to find custom extension in location: ${destFile}`);
|
|
55
58
|
}
|
|
56
59
|
const fileSize = getLocalFileSizeInMB(destFile);
|
|
57
60
|
if (fileSize > MAX_CUSTOM_EXT_SIZE_MB && !unlimitedSize) {
|
|
58
|
-
|
|
61
|
+
throw new ArgError(MAX_CUSTOM_SIZE_ERROR_MSG);
|
|
59
62
|
}
|
|
60
|
-
return
|
|
63
|
+
return destFile;
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
|
|
64
67
|
/**
|
|
65
68
|
* @param {string} url
|
|
66
69
|
*/
|
|
67
|
-
function getRemoteFileSizeInMB(url) {
|
|
70
|
+
async function getRemoteFileSizeInMB(url) {
|
|
68
71
|
const httpRequest = require('./httpRequest');
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
});
|
|
72
|
+
try {
|
|
73
|
+
const res = await httpRequest.head(url);
|
|
74
|
+
const contentLengthHeader = res.headers['content-length'];
|
|
75
|
+
const contentLengthBytes = contentLengthHeader ? parseInt(contentLengthHeader, 10) : 0;
|
|
76
|
+
return contentLengthBytes / 1000000;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
logger.warn('failed to download custom extension', { err });
|
|
79
|
+
throw new ArgError(`Failed to download custom extension from location: ${url}`);
|
|
80
|
+
}
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
/**
|
|
82
|
-
*
|
|
83
84
|
* @param {string[]} locations
|
|
84
|
-
*
|
|
85
85
|
*/
|
|
86
86
|
function prepareExtension(locations) {
|
|
87
87
|
logger.info('prepare extension', { locations });
|
|
88
88
|
|
|
89
89
|
const fullLocations = locations.map(location => ({ location, path: getSourcePath(location) }));
|
|
90
90
|
return localRunnerCache.memoize(
|
|
91
|
-
() =>
|
|
91
|
+
() => promiseMap(fullLocations, ({ location, path }) => getSource(location, path)),
|
|
92
92
|
'prepareExtension',
|
|
93
93
|
MSEC_IN_HALF_DAY,
|
|
94
94
|
fullLocations
|
|
@@ -132,25 +132,23 @@ async function prepareChromeDriver(userDetails = {}, driverOptions = {}, skipIsR
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
function getPlayerVersion() {
|
|
135
|
+
async function getPlayerVersion() {
|
|
136
136
|
const url = `${config.BLOB_URL}/extension/sessionPlayer_LATEST_RELEASE`;
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
const res = await download(url);
|
|
138
|
+
return res.body.toString('utf8');
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
142
|
* @param {string} location
|
|
143
143
|
* @param {string | undefined} canary
|
|
144
|
-
*
|
|
145
|
-
* @returns {Promise<string>}
|
|
146
144
|
*/
|
|
147
|
-
function getPlayerLocation(location, canary) {
|
|
145
|
+
async function getPlayerLocation(location, canary) {
|
|
148
146
|
if (!isURL(location) || (isURL(location) && canary) || config.IS_ON_PREM) {
|
|
149
|
-
return
|
|
147
|
+
return location;
|
|
150
148
|
}
|
|
151
149
|
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
const ver = await getPlayerVersion();
|
|
151
|
+
return `${location}-${ver}`;
|
|
154
152
|
}
|
|
155
153
|
|
|
156
154
|
function getSessionPlayerFolder() {
|
|
@@ -179,7 +177,7 @@ async function downloadAndUnzip(loc, playerFileName, isRetry = false) {
|
|
|
179
177
|
}
|
|
180
178
|
}
|
|
181
179
|
|
|
182
|
-
function preparePlayer(location, canary) {
|
|
180
|
+
async function preparePlayer(location, canary) {
|
|
183
181
|
logger.info('prepare player', { location, canary });
|
|
184
182
|
const playerFileName = getPlayerDestination();
|
|
185
183
|
return localRunnerCache.memoize(
|