@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
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
3
|
const express = require('express');
|
|
4
|
+
|
|
4
5
|
const router = express.Router();
|
|
5
6
|
const logger = require('../../../commons/logger').getLogger('playground-router');
|
|
6
|
-
const {ClientError, PlaygroundCodeError} = require('../../../errors');
|
|
7
|
-
const {runPlaygroundTest, stopPlaygroundTest, CODE_TYPES} = require('./service');
|
|
8
|
-
const {DISABLE_AGENT_ORIGIN_CHECK} = require('../../../commons/config');
|
|
7
|
+
const { ClientError, PlaygroundCodeError } = require('../../../errors');
|
|
8
|
+
const { runPlaygroundTest, stopPlaygroundTest, CODE_TYPES } = require('./service');
|
|
9
|
+
const { DISABLE_AGENT_ORIGIN_CHECK } = require('../../../commons/config');
|
|
9
10
|
|
|
10
11
|
const VALID_HOSTS = ['localhost', 'app.testim.io', 'playground.testim.io', 'staging.testim.io', 'app.staging.testim.cc', 'tta-crm.tricentis.com'];
|
|
11
12
|
|
|
12
13
|
const parseUrl = (url) => {
|
|
13
|
-
if(!url) {
|
|
14
|
+
if (!url) {
|
|
14
15
|
return {};
|
|
15
16
|
}
|
|
16
17
|
try {
|
|
@@ -21,17 +22,17 @@ const parseUrl = (url) => {
|
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
const checkReferer = (req, res, next) => {
|
|
24
|
-
if(DISABLE_AGENT_ORIGIN_CHECK) {
|
|
25
|
+
if (DISABLE_AGENT_ORIGIN_CHECK) {
|
|
25
26
|
return next();
|
|
26
27
|
}
|
|
27
28
|
const referer = req.headers.referer;
|
|
28
29
|
const origin = req.headers.origin;
|
|
29
|
-
if(!referer && !origin) {
|
|
30
|
+
if (!referer && !origin) {
|
|
30
31
|
return res.status(400).send();
|
|
31
32
|
}
|
|
32
33
|
const refererUrl = parseUrl(referer);
|
|
33
34
|
const originUrl = parseUrl(origin);
|
|
34
|
-
if(!VALID_HOSTS.includes(refererUrl.hostname) && !VALID_HOSTS.includes(originUrl.hostname)) {
|
|
35
|
+
if (!VALID_HOSTS.includes(refererUrl.hostname) && !VALID_HOSTS.includes(originUrl.hostname)) {
|
|
35
36
|
return res.status(400).send();
|
|
36
37
|
}
|
|
37
38
|
return next();
|
|
@@ -50,11 +51,11 @@ router.post('/run', [checkReferer], async (req, res) => {
|
|
|
50
51
|
await runPlaygroundTest(body);
|
|
51
52
|
res.send({ success: true });
|
|
52
53
|
} catch (e) {
|
|
53
|
-
if(e instanceof ClientError) {
|
|
54
|
+
if (e instanceof ClientError) {
|
|
54
55
|
res.status(404).send({ success: false });
|
|
55
56
|
return undefined;
|
|
56
57
|
}
|
|
57
|
-
if(e instanceof PlaygroundCodeError) {
|
|
58
|
+
if (e instanceof PlaygroundCodeError) {
|
|
58
59
|
res.json({ success: false, type: 'playground-error', stack: e.innerStack });
|
|
59
60
|
return undefined;
|
|
60
61
|
}
|
|
@@ -1,48 +1,47 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const util = require('util');
|
|
5
|
-
const writeFileAsync = util.promisify(require('fs').writeFile);
|
|
3
|
+
const fs = require('fs');
|
|
6
4
|
const path = require('path');
|
|
7
|
-
const {fork} = require('child_process');
|
|
8
5
|
const os = require('os');
|
|
9
|
-
const {
|
|
6
|
+
const { fork } = require('child_process');
|
|
7
|
+
const { ClientError, PlaygroundCodeError } = require('../../../errors');
|
|
8
|
+
|
|
10
9
|
const CODE_TYPES = ['playwright', 'selenium', 'puppeteer'];
|
|
11
10
|
|
|
12
11
|
const runForks = {};
|
|
13
12
|
|
|
14
13
|
async function createTempFile(fileName, data, encoding = 'utf8') {
|
|
15
14
|
const fullPath = path.join(os.tmpdir(), fileName);
|
|
16
|
-
await
|
|
15
|
+
await fs.promises.writeFile(fullPath, data, encoding);
|
|
17
16
|
return fullPath;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
const forkAsync = (fileFullPath) => {
|
|
21
20
|
let gotResolved;
|
|
22
|
-
const promise = new Promise(resolve => gotResolved = resolve);
|
|
21
|
+
const promise = new Promise(resolve => { gotResolved = resolve; });
|
|
23
22
|
|
|
24
|
-
const child = fork(fileFullPath, {stdio: [
|
|
23
|
+
const child = fork(fileFullPath, { stdio: ['inherit', 'inherit', 'inherit', 'ipc'] });
|
|
25
24
|
promise.child = child;
|
|
26
25
|
child.on('message', (message) => {
|
|
27
|
-
if(!message) {
|
|
26
|
+
if (!message) {
|
|
28
27
|
return;
|
|
29
28
|
}
|
|
30
|
-
const {type, error} = message;
|
|
31
|
-
if(error && ['uncaughtException', 'unhandledRejection'].includes(type)) {
|
|
32
|
-
|
|
29
|
+
const { type, error } = message;
|
|
30
|
+
if (error && ['uncaughtException', 'unhandledRejection'].includes(type)) {
|
|
31
|
+
gotResolved({ error: Object.assign(new PlaygroundCodeError(), { innerStack: message.error.stack }) });
|
|
33
32
|
}
|
|
34
33
|
});
|
|
35
34
|
child.on('error', (error) => {
|
|
36
|
-
gotResolved({error});
|
|
35
|
+
gotResolved({ error });
|
|
37
36
|
});
|
|
38
37
|
child.on('exit', (exitCode) => {
|
|
39
|
-
gotResolved({exitCode});
|
|
38
|
+
gotResolved({ exitCode });
|
|
40
39
|
});
|
|
41
40
|
|
|
42
41
|
return promise;
|
|
43
42
|
};
|
|
44
43
|
|
|
45
|
-
async function runCodeLocally({code}) {
|
|
44
|
+
async function runCodeLocally({ code }) {
|
|
46
45
|
const forkId = Date.now();
|
|
47
46
|
try {
|
|
48
47
|
const codeWithExtra = `
|
|
@@ -60,25 +59,25 @@ async function runCodeLocally({code}) {
|
|
|
60
59
|
const fileFullPath = await createTempFile(`tst-playground-${Date.now()}.js`, codeWithExtra);
|
|
61
60
|
const promiseExec = forkAsync(fileFullPath);
|
|
62
61
|
runForks[forkId] = promiseExec.child;
|
|
63
|
-
const {error, exitCode} = await promiseExec;
|
|
64
|
-
if(error) {
|
|
62
|
+
const { error, exitCode } = await promiseExec;
|
|
63
|
+
if (error) {
|
|
65
64
|
throw error;
|
|
66
65
|
}
|
|
67
|
-
if(exitCode !== 0) {
|
|
66
|
+
if (exitCode !== 0) {
|
|
68
67
|
throw new Error(`Process exited with exit code: ${exitCode}`);
|
|
69
68
|
}
|
|
70
69
|
return undefined;
|
|
71
70
|
} finally {
|
|
72
|
-
if(runForks[forkId]) {
|
|
71
|
+
if (runForks[forkId]) {
|
|
73
72
|
runForks[forkId].kill();
|
|
74
73
|
delete runForks[forkId];
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
77
|
|
|
79
|
-
async function runPlaygroundTest({code, type}) {
|
|
80
|
-
if(['playwright', 'puppeteer', 'selenium'].includes(type)) {
|
|
81
|
-
return runCodeLocally({code});
|
|
78
|
+
async function runPlaygroundTest({ code, type }) {
|
|
79
|
+
if (['playwright', 'puppeteer', 'selenium'].includes(type)) {
|
|
80
|
+
return runCodeLocally({ code });
|
|
82
81
|
}
|
|
83
82
|
throw new ClientError();
|
|
84
83
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
3
4
|
|
|
4
5
|
const express = require('express');
|
|
5
6
|
|
|
6
7
|
module.exports = {
|
|
7
|
-
standaloneBrowserRoutes
|
|
8
|
-
}
|
|
8
|
+
standaloneBrowserRoutes,
|
|
9
|
+
};
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @param {{
|
|
@@ -13,33 +14,32 @@ module.exports = {
|
|
|
13
14
|
}} [testimStandaloneBrowser]
|
|
14
15
|
*/
|
|
15
16
|
function standaloneBrowserRoutes(testimStandaloneBrowser) {
|
|
16
|
-
|
|
17
17
|
const router = express.Router();
|
|
18
18
|
|
|
19
|
-
router.get(
|
|
19
|
+
router.get('/cdp-url', (req, res) => {
|
|
20
20
|
if (!testimStandaloneBrowser) {
|
|
21
21
|
res.status(503).send({
|
|
22
|
-
error:
|
|
22
|
+
error: 'Testim standalone browser is not running',
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
res.status(200).send({
|
|
29
|
-
url: testimStandaloneBrowser.webdriverApi.cdpUrl
|
|
29
|
+
url: testimStandaloneBrowser.webdriverApi.cdpUrl,
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
router.get(
|
|
33
|
+
router.get('/status', (req, res) => {
|
|
34
34
|
if (!testimStandaloneBrowser) {
|
|
35
35
|
res.status(503).send({
|
|
36
|
-
ok: false
|
|
36
|
+
ok: false,
|
|
37
37
|
});
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
res.status(200).send({
|
|
42
|
-
ok: true
|
|
42
|
+
ok: true,
|
|
43
43
|
});
|
|
44
44
|
});
|
|
45
45
|
|
package/cdpTestRunner.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const WebSocket = require('ws');
|
|
2
2
|
const Promise = require('bluebird');
|
|
3
|
+
const { promiseFromCallback } = require('./utils');
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class CDPTestRunner {
|
|
@@ -20,11 +21,11 @@ class CDPTestRunner {
|
|
|
20
21
|
}
|
|
21
22
|
const websocket = new WebSocket(this._cdpUrl, { timeout });
|
|
22
23
|
|
|
23
|
-
const openPromise =
|
|
24
|
+
const openPromise = promiseFromCallback((cb) => {
|
|
24
25
|
websocket.once('open', cb);
|
|
25
26
|
});
|
|
26
27
|
|
|
27
|
-
const errorPromise =
|
|
28
|
+
const errorPromise = promiseFromCallback((cb) => {
|
|
28
29
|
websocket.once('error', cb);
|
|
29
30
|
}).catch(() => {
|
|
30
31
|
websocket.close();
|
|
@@ -46,7 +47,7 @@ class CDPTestRunner {
|
|
|
46
47
|
this._cdpCallbacks.delete(object.id);
|
|
47
48
|
if (object.error) {
|
|
48
49
|
callback.reject(new Error(object.error));
|
|
49
|
-
} else if (object.result.exceptionDetails
|
|
50
|
+
} else if (object.result.exceptionDetails?.exception) {
|
|
50
51
|
callback.reject(new Error(object.result.exceptionDetails.exception.description));
|
|
51
52
|
} else {
|
|
52
53
|
callback.resolve(object.result);
|
package/chromiumInstaller.js
CHANGED
|
@@ -2,8 +2,7 @@ const path = require('path');
|
|
|
2
2
|
const os = require('os');
|
|
3
3
|
const fs = require('fs-extra');
|
|
4
4
|
const { ArgError } = require('./errors');
|
|
5
|
-
const { unzipFile } = require('./utils');
|
|
6
|
-
const { downloadAndSave, TESTIM_BROWSER_DIR } = require('./utils');
|
|
5
|
+
const { downloadAndSave, unzipFile, TESTIM_BROWSER_DIR } = require('./utils');
|
|
7
6
|
const ora = require('ora');
|
|
8
7
|
|
|
9
8
|
|
|
@@ -30,7 +29,7 @@ async function downloadAndInstallChromium() {
|
|
|
30
29
|
const platformFolder = {
|
|
31
30
|
linux: 'Linux_x64',
|
|
32
31
|
mac: 'Mac',
|
|
33
|
-
mac_arm: 'Mac_Arm',
|
|
32
|
+
mac_arm: 'Mac_Arm', // eslint-disable-line camelcase
|
|
34
33
|
win32: 'Win',
|
|
35
34
|
win64: 'Win_x64',
|
|
36
35
|
};
|
|
@@ -42,14 +41,14 @@ async function downloadAndInstallChromium() {
|
|
|
42
41
|
const platformArchiveName = {
|
|
43
42
|
linux: 'chrome-linux',
|
|
44
43
|
mac: 'chrome-mac',
|
|
45
|
-
mac_arm: 'chrome-mac',
|
|
44
|
+
mac_arm: 'chrome-mac', // eslint-disable-line camelcase
|
|
46
45
|
win32: winArchiveName,
|
|
47
46
|
win64: winArchiveName,
|
|
48
47
|
};
|
|
49
48
|
const binaryPaths = {
|
|
50
49
|
linux: 'chrome',
|
|
51
50
|
mac: 'Chromium.app/Contents/MacOS/Chromium',
|
|
52
|
-
mac_arm: 'Chromium.app/Contents/MacOS/Chromium',
|
|
51
|
+
mac_arm: 'Chromium.app/Contents/MacOS/Chromium', // eslint-disable-line camelcase
|
|
53
52
|
win32: 'chrome.exe',
|
|
54
53
|
win64: 'chrome.exe',
|
|
55
54
|
};
|
package/cli/onExit.js
CHANGED
|
@@ -48,12 +48,12 @@ module.exports.ignoreFailingTestsInExitCode = function () {
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
module.exports.onExit = async function onExit(exitValue) {
|
|
51
|
-
if (exitValue
|
|
51
|
+
if (exitValue?.stack) {
|
|
52
52
|
if (!isCi) {
|
|
53
53
|
writeStackTrace(exitValue);
|
|
54
54
|
} else {
|
|
55
55
|
// eslint-disable-next-line no-console
|
|
56
|
-
console.error(exitValue, exitValue
|
|
56
|
+
console.error(exitValue, exitValue.stack);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
package/cli.js
CHANGED
|
@@ -35,15 +35,16 @@ async function checkNodeVersion() {
|
|
|
35
35
|
throw new ArgError(`Required node version ${version} not satisfied with current version ${process.version}`);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
const
|
|
39
|
-
const
|
|
38
|
+
const minimalNodeVersion = 16;
|
|
39
|
+
const majorVersion = Number(process.versions.node.split('.')[0]);
|
|
40
|
+
const dateHasPassed = new Date('2023-04-30T00:00:00.000Z') <= new Date();
|
|
40
41
|
|
|
41
|
-
if (majorVersion <
|
|
42
|
-
throw new ArgError(
|
|
42
|
+
if (majorVersion < minimalNodeVersion && dateHasPassed) {
|
|
43
|
+
throw new ArgError(`Testim.io CLI supports Node.js ${minimalNodeVersion} and above, please upgrade to a newer Node.js version`);
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
if (majorVersion <
|
|
46
|
-
console.log('\x1b[33m%s\x1b[0m',
|
|
46
|
+
if (majorVersion < minimalNodeVersion) {
|
|
47
|
+
console.log('\x1b[33m%s\x1b[0m', `Testim.io CLI will stop supporting Node.js < ${minimalNodeVersion} on April 30 2023, please upgrade to a newer Node.js version`);
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
|
|
@@ -67,14 +68,14 @@ async function main() {
|
|
|
67
68
|
require('./commons/logger').setProjectId(processedOptions.project);
|
|
68
69
|
require('./commons/runnerFileCache').setEncryptKey(typeof processedOptions.token === 'string' ? processedOptions.token : 'anonymous_encrypt_key');
|
|
69
70
|
|
|
70
|
-
if (processedOptions
|
|
71
|
+
if (utils.isInitCodimMode(processedOptions)) {
|
|
71
72
|
const codimCli = require('./codim/codim-cli');
|
|
72
73
|
return codimCli.init(processedOptions.initTestProject);
|
|
73
74
|
}
|
|
74
|
-
if (processedOptions
|
|
75
|
+
if (utils.isLoginMode(processedOptions)) {
|
|
75
76
|
return undefined;
|
|
76
77
|
}
|
|
77
|
-
if (processedOptions
|
|
78
|
+
if (utils.isCreatePrefetchedDataMode(processedOptions)) {
|
|
78
79
|
const runnerFileCache = require('./commons/runnerFileCache');
|
|
79
80
|
await runnerFileCache.clear();
|
|
80
81
|
await prepareRunner.initializeUserWithAuth(processedOptions);
|
|
@@ -91,7 +92,7 @@ async function main() {
|
|
|
91
92
|
return undefined;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
if (processedOptions
|
|
95
|
+
if (utils.isTunnelOnlyMode(processedOptions)) {
|
|
95
96
|
await testRunner.init(processedOptions);
|
|
96
97
|
await require('./commons/testimTunnel').serveTunneling(processedOptions);
|
|
97
98
|
return undefined;
|
package/cliAgentMode.js
CHANGED
|
@@ -7,13 +7,12 @@ const path = require('path');
|
|
|
7
7
|
const fs = require('fs-extra');
|
|
8
8
|
const ms = require('ms');
|
|
9
9
|
const WebSocket = require('ws');
|
|
10
|
-
const Bluebird = require('bluebird');
|
|
11
10
|
const ChromeLauncher = require('chrome-launcher');
|
|
12
11
|
const config = require('./commons/config');
|
|
13
12
|
const { ArgError } = require('./errors');
|
|
14
13
|
const lazyRequire = require('./commons/lazyRequire');
|
|
15
14
|
const prepareUtils = require('./commons/prepareRunnerAndTestimStartUtils');
|
|
16
|
-
const { downloadAndSave, unzipFile, getCdpAddressForHost, TESTIM_BROWSER_DIR } = require('./utils');
|
|
15
|
+
const { downloadAndSave, unzipFile, getCdpAddressForHost, TESTIM_BROWSER_DIR, promiseFromCallback } = require('./utils');
|
|
17
16
|
const ora = require('ora');
|
|
18
17
|
const { downloadAndInstallChromium, CHROMIUM_VERSION } = require('./chromiumInstaller');
|
|
19
18
|
|
|
@@ -33,9 +32,10 @@ module.exports = {
|
|
|
33
32
|
};
|
|
34
33
|
|
|
35
34
|
/**
|
|
36
|
-
* @
|
|
35
|
+
* @type {(options: import('./runOptions').Options) => options is import('./runOptions').AgentModeOptions}
|
|
37
36
|
*/
|
|
38
37
|
function shouldStartAgentMode(options) {
|
|
38
|
+
// @ts-ignore should be `as any`
|
|
39
39
|
return options.agentMode;
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -54,7 +54,7 @@ async function runAgentMode(options) {
|
|
|
54
54
|
// Consider moving that into the agent server and add endpoint to start browser?
|
|
55
55
|
testimStandaloneBrowser = await startTestimStandaloneBrowser(options);
|
|
56
56
|
} catch (e) {
|
|
57
|
-
if (e
|
|
57
|
+
if (e?.message?.includes('user data directory is already in use')) {
|
|
58
58
|
throw new ArgError('Please close all chrome browsers that were opened with "testim start" and try again');
|
|
59
59
|
}
|
|
60
60
|
throw e;
|
|
@@ -63,7 +63,7 @@ async function runAgentMode(options) {
|
|
|
63
63
|
|
|
64
64
|
const agentServer = require('./agent/server');
|
|
65
65
|
|
|
66
|
-
if (testimStandaloneBrowser
|
|
66
|
+
if (testimStandaloneBrowser?.webdriverApi) {
|
|
67
67
|
// if we're starting the agent here, pre-load the sessionPlayer so it loads faster
|
|
68
68
|
// on first play
|
|
69
69
|
const LOAD_PLAYER_DELAY = 6000;
|
|
@@ -361,7 +361,7 @@ async function readAndValidateChromedriverDevToolsActivePortFile() {
|
|
|
361
361
|
async function tryToCloseBrowserWithCDPUrl(webSocketDebuggerUrl, timeout = 100) {
|
|
362
362
|
const websocketConnection = await wsConnectAndOpen(webSocketDebuggerUrl, timeout);
|
|
363
363
|
|
|
364
|
-
return
|
|
364
|
+
return promiseFromCallback(cb => {
|
|
365
365
|
websocketConnection.send(JSON.stringify({
|
|
366
366
|
id: 0,
|
|
367
367
|
method: 'Browser.close',
|
|
@@ -376,13 +376,13 @@ async function tryToCloseBrowserWithCDPUrl(webSocketDebuggerUrl, timeout = 100)
|
|
|
376
376
|
async function wsConnectAndOpen(webSocketDebuggerUrl, timeout = 100) {
|
|
377
377
|
const websocket = new WebSocket(webSocketDebuggerUrl, { timeout });
|
|
378
378
|
|
|
379
|
-
const openPromise =
|
|
379
|
+
const openPromise = promiseFromCallback((cb) => {
|
|
380
380
|
websocket.once('open', cb);
|
|
381
381
|
}).then(() => {
|
|
382
382
|
websocket.removeAllListeners();
|
|
383
383
|
});
|
|
384
384
|
|
|
385
|
-
const errorPromise =
|
|
385
|
+
const errorPromise = promiseFromCallback((cb) => {
|
|
386
386
|
websocket.once('error', cb);
|
|
387
387
|
}).catch(() => {
|
|
388
388
|
websocket.close();
|
package/codim/codim-cli.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const childProcess = require('child_process');
|
|
6
7
|
const path = require('path');
|
|
7
|
-
const validateNpmPackageName = require(
|
|
8
|
+
const validateNpmPackageName = require('validate-npm-package-name');
|
|
8
9
|
const ArgError = require('../errors.js').ArgError;
|
|
10
|
+
const { promisify } = require('util');
|
|
11
|
+
|
|
12
|
+
const exec = promisify(childProcess.exec);
|
|
9
13
|
|
|
10
14
|
module.exports.init = async function init(name) {
|
|
11
15
|
const ora = require('ora');
|
|
@@ -16,19 +20,19 @@ module.exports.init = async function init(name) {
|
|
|
16
20
|
type: 'text',
|
|
17
21
|
name: 'project',
|
|
18
22
|
message: 'Please enter Project name',
|
|
19
|
-
validate: value => String(value).length > 3
|
|
23
|
+
validate: value => String(value).length > 3,
|
|
20
24
|
});
|
|
21
25
|
name = response.project;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
const
|
|
28
|
+
const fullPath = path.resolve(name);
|
|
25
29
|
|
|
26
|
-
if (
|
|
27
|
-
console.log(`${
|
|
30
|
+
if (fs.existsSync(fullPath) && fs.readdirSync(fullPath).length !== 0) {
|
|
31
|
+
console.log(`${fullPath} is not empty. Quitting...`);
|
|
28
32
|
process.exit(1);
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
const packageName =
|
|
35
|
+
const packageName = fullPath.substr(Math.max(fullPath.lastIndexOf('/'), fullPath.lastIndexOf('\\')) + 1);
|
|
32
36
|
|
|
33
37
|
const nameValidity = validateNpmPackageName(packageName);
|
|
34
38
|
|
|
@@ -36,7 +40,7 @@ module.exports.init = async function init(name) {
|
|
|
36
40
|
if (nameValidity.errors) nameValidity.errors.forEach((e) => console.log(e));
|
|
37
41
|
if (nameValidity.warnings) nameValidity.warnings.forEach((e) => console.log(e));
|
|
38
42
|
|
|
39
|
-
throw new ArgError(
|
|
43
|
+
throw new ArgError('Package name is not valid');
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
const response = await prompts({
|
|
@@ -45,7 +49,7 @@ module.exports.init = async function init(name) {
|
|
|
45
49
|
message: 'Add support for TypeScript?',
|
|
46
50
|
initial: true,
|
|
47
51
|
active: 'no',
|
|
48
|
-
inactive: 'yes'
|
|
52
|
+
inactive: 'yes',
|
|
49
53
|
});
|
|
50
54
|
|
|
51
55
|
const sourceFolder = response.isJs ? 'template.js' : 'template.ts';
|
|
@@ -55,20 +59,20 @@ module.exports.init = async function init(name) {
|
|
|
55
59
|
|
|
56
60
|
let spinner = ora(`Creating new test project in ${dest}`).start();
|
|
57
61
|
|
|
58
|
-
await
|
|
62
|
+
await fs.promises.copyFile(source, dest);
|
|
59
63
|
|
|
60
64
|
const sourcePackageJson = path.join(__dirname, sourceFolder, 'package.json');
|
|
61
65
|
const destPackageJson = path.join(process.cwd(), name, 'package.json');
|
|
62
66
|
|
|
63
|
-
const packageContents = await
|
|
67
|
+
const packageContents = await fs.promises.readFile(sourcePackageJson);
|
|
64
68
|
|
|
65
69
|
const newPackageJson = packageContents.toString().replace('~testim-codeful-test-project~', packageName);
|
|
66
70
|
|
|
67
|
-
await
|
|
71
|
+
await fs.promises.writeFile(destPackageJson, newPackageJson);
|
|
68
72
|
|
|
69
73
|
const gitIgnore = 'node_modules';
|
|
70
74
|
const gitIgnoreFilePath = path.join(process.cwd(), name, '.gitignore');
|
|
71
|
-
await
|
|
75
|
+
await fs.promises.writeFile(gitIgnoreFilePath, gitIgnore);
|
|
72
76
|
|
|
73
77
|
spinner.succeed();
|
|
74
78
|
spinner = ora('Installing dependencies').start();
|
|
@@ -77,5 +81,4 @@ module.exports.init = async function init(name) {
|
|
|
77
81
|
spinner.succeed();
|
|
78
82
|
|
|
79
83
|
console.log(`Testim Dev Kit project folder successfully created in ${dest}.`);
|
|
80
|
-
|
|
81
84
|
};
|
package/codim/hybrid-utils.js
CHANGED
|
@@ -4,7 +4,7 @@ module.exports.getArgumentsFromContext = async function getArgumentsFromContext(
|
|
|
4
4
|
step,
|
|
5
5
|
context,
|
|
6
6
|
locatorStrategy
|
|
7
|
-
)
|
|
7
|
+
) {
|
|
8
8
|
const argsInCorrectOrder = fixArgsOrdering(step.parameterNames.map(p => p.displayName), context.incomingParams.as);
|
|
9
9
|
|
|
10
10
|
return await Promise.all(argsInCorrectOrder.map(arg => {
|
package/codim/measure-perf.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
1
2
|
const startTime = Date.now();
|
|
2
3
|
let last = 0;
|
|
3
4
|
global.log = function time(message) {
|
|
4
|
-
|
|
5
|
-
console.log(time, time - last
|
|
5
|
+
const time = Date.now() - startTime;
|
|
6
|
+
console.log(time, time - last, message);
|
|
6
7
|
last = time;
|
|
7
8
|
};
|
|
8
9
|
global.perf = function perf(fn) {
|
|
@@ -12,22 +13,24 @@ global.perf = function perf(fn) {
|
|
|
12
13
|
} finally {
|
|
13
14
|
console.log(fn.name, 'took', Date.now() - start);
|
|
14
15
|
}
|
|
15
|
-
}
|
|
16
|
+
};
|
|
16
17
|
|
|
17
18
|
require('bluebird').prototype.log = function log(message) {
|
|
18
19
|
return this.tap(() => global.log(message));
|
|
19
|
-
}
|
|
20
|
+
};
|
|
20
21
|
|
|
21
22
|
global.measureRequireTimes = () => {
|
|
22
23
|
'use strict';
|
|
24
|
+
|
|
23
25
|
const {
|
|
24
26
|
performance,
|
|
25
|
-
PerformanceObserver
|
|
27
|
+
PerformanceObserver,
|
|
26
28
|
} = require('perf_hooks');
|
|
27
29
|
const mod = require('module');
|
|
28
30
|
|
|
29
31
|
// Monkey patch the require function
|
|
30
32
|
mod.Module.prototype.require = performance.timerify(mod.Module.prototype.require);
|
|
33
|
+
// eslint-disable-next-line no-global-assign
|
|
31
34
|
require = performance.timerify(require);
|
|
32
35
|
|
|
33
36
|
// Activate the observer
|
|
@@ -39,4 +42,4 @@ global.measureRequireTimes = () => {
|
|
|
39
42
|
obs.disconnect();
|
|
40
43
|
});
|
|
41
44
|
obs.observe({ entryTypes: ['function'], buffered: true });
|
|
42
|
-
}
|
|
45
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
3
|
// Import Testim Dev Kit methods
|
|
4
4
|
const { go, text, test, l, Locator } = require('testim');
|
|
@@ -10,12 +10,12 @@ const { expect } = require('chai');
|
|
|
10
10
|
Locator.set(require('./locators/locators.js'));
|
|
11
11
|
|
|
12
12
|
test('Simple text validation', async () => {
|
|
13
|
-
|
|
13
|
+
await go('http://demo.testim.io');
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
// Grab the title text and store it in a const
|
|
16
|
+
const title = await text(l('SPACE_&_BEYOND'));
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
// Use Chai to assert the title text is correct
|
|
19
|
+
expect(title).to.eq('Space & Beyond');
|
|
20
20
|
}); // end of test
|
|
21
21
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
3
|
// Import Testim Dev Kit methods
|
|
4
4
|
const { go, waitForText, scrollToElement, click, test, l, Locator } = require('testim');
|
|
@@ -7,24 +7,22 @@ const { go, waitForText, scrollToElement, click, test, l, Locator } = require('t
|
|
|
7
7
|
Locator.set(require('./locators/locators.js'));
|
|
8
8
|
|
|
9
9
|
test('Using locators', async () => {
|
|
10
|
+
await go('http://demo.testim.io');
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
// Load the SELECT DESTINATION button smart locator. This locator
|
|
13
|
+
// can now be used by any method that accepts a smart locator as
|
|
14
|
+
// as argument.
|
|
15
|
+
//
|
|
16
|
+
// You can read more about the advantages of working with smart
|
|
17
|
+
// locators in the Testim documentation:
|
|
18
|
+
// https://help.testim.io/docs/working-with-locators
|
|
19
|
+
const selectDestButton = l('SELECT_DESTINATION');
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
// can now be used by any method that accepts a smart locator as
|
|
15
|
-
// as argument.
|
|
16
|
-
//
|
|
17
|
-
// You can read more about the advantages of working with smart
|
|
18
|
-
// locators in the Testim documentation:
|
|
19
|
-
// https://help.testim.io/docs/working-with-locators
|
|
20
|
-
const selectDestButton = l("SELECT_DESTINATION");
|
|
21
|
+
await waitForText(selectDestButton, 'Select Destination');
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
await scrollToElement(selectDestButton);
|
|
25
|
-
|
|
26
|
-
await click(selectDestButton);
|
|
23
|
+
await scrollToElement(selectDestButton);
|
|
27
24
|
|
|
25
|
+
await click(selectDestButton);
|
|
28
26
|
}); // end of test
|
|
29
27
|
|
|
30
28
|
|