@testim/testim-cli 3.285.0 → 3.287.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.
Files changed (47) hide show
  1. package/agent/routers/cliJsCode/service.js +32 -9
  2. package/agent/routers/codim/service.js +10 -7
  3. package/cli/writeStackTrace.js +14 -13
  4. package/cli.js +12 -15
  5. package/commons/featureFlags.js +1 -0
  6. package/commons/getSessionPlayerRequire.js +23 -8
  7. package/commons/httpRequestCounters.js +2 -2
  8. package/commons/initializeUserWithAuth.js +1 -1
  9. package/commons/testimDesiredCapabilitiesBuilder.js +6 -1
  10. package/npm-shrinkwrap.json +2128 -964
  11. package/package.json +2 -2
  12. package/player/appiumTestPlayer.js +8 -20
  13. package/player/seleniumTestPlayer.js +10 -21
  14. package/player/services/frameLocator.js +4 -2
  15. package/player/services/tabService.js +13 -14
  16. package/player/stepActions/apiStepAction.js +6 -8
  17. package/player/stepActions/baseCliJsStepAction.js +3 -6
  18. package/player/stepActions/baseJsStepAction.js +10 -8
  19. package/player/stepActions/dropFileStepAction.js +5 -3
  20. package/player/stepActions/evaluateExpressionStepAction.js +6 -7
  21. package/player/stepActions/hoverStepAction.js +1 -3
  22. package/player/stepActions/inputFileStepAction.js +7 -6
  23. package/player/stepActions/locateStepAction.js +5 -6
  24. package/player/stepActions/mouseStepAction.js +9 -10
  25. package/player/stepActions/scrollStepAction.js +5 -7
  26. package/player/stepActions/selectOptionStepAction.js +3 -4
  27. package/player/stepActions/sfdcRecordedStepAction.js +1 -1
  28. package/player/stepActions/sfdcStepAction.js +1 -1
  29. package/player/stepActions/stepAction.js +7 -2
  30. package/player/stepActions/textStepAction.js +4 -7
  31. package/player/stepActions/textValidationStepAction.js +3 -10
  32. package/player/stepActions/wheelStepAction.js +1 -2
  33. package/player/utils/eyeSdkService.js +4 -3
  34. package/player/utils/stepActionUtils.js +9 -4
  35. package/player/webdriver.js +3 -3
  36. package/processHandler.js +3 -2
  37. package/processHandler.test.js +3 -4
  38. package/reports/debugReporter.js +1 -1
  39. package/runOptions.d.ts +2 -0
  40. package/runOptions.js +17 -8
  41. package/runners/TestPlanRunner.js +1 -1
  42. package/stepPlayers/hybridStepPlayback.js +23 -21
  43. package/stepPlayers/remoteStepPlayback.js +8 -6
  44. package/stepPlayers/tdkHybridStepPlayback.js +20 -17
  45. package/workers/BaseWorker.js +7 -6
  46. package/workers/WorkerAppium.js +8 -6
  47. package/workers/WorkerSelenium.js +16 -12
@@ -2,18 +2,20 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- const path = require('path');
5
+ const fs = require('fs');
6
6
  const os = require('os');
7
- const dataUriToBuffer = require('data-uri-to-buffer');
8
- const { spawn: threadSpawn, config } = require('threads');
7
+ const path = require('path');
9
8
  const fse = require('fs-extra');
10
- const fs = require('fs');
9
+ const dataUriToBuffer = require('data-uri-to-buffer');
11
10
  const utils = require('../../../utils');
12
- const logger = require('../../../commons/logger').getLogger('cli-service');
13
- const { getS3Artifact } = require('../../../commons/testimServicesApi');
14
11
  const npmWrapper = require('../../../commons/npmWrapper');
15
12
  const featureFlags = require('../../../commons/featureFlags');
13
+ const { spawn: threadSpawn, config } = require('threads');
16
14
  const { TimeoutError } = require('../../../errors');
15
+ const { getLogger } = require('../../../commons/logger');
16
+ const { getS3Artifact } = require('../../../commons/testimServicesApi');
17
+
18
+ const logger = getLogger('cli-service');
17
19
 
18
20
  /** @type {import('worker_threads') | false} */
19
21
  let workerThreads;
@@ -270,16 +272,17 @@ function runCode(transactionId, incomingParams, context, code, packageLocalLocat
270
272
  .finally(() => thread?.kill());
271
273
  }
272
274
 
273
- function requireOrImportMethod(path) {
275
+ /** @param {string} _path */
276
+ function requireOrImportMethod(_path) {
274
277
  try {
275
- return { sync: true, lib: require(path) };
278
+ return { sync: true, lib: require(_path) };
276
279
  } catch (err) {
277
280
  if (err.code === 'ERR_REQUIRE_ESM') {
278
281
  const pathModule = require('path');
279
282
 
280
283
  const lib = fs.promises.readFile(`${path}${pathModule.sep}package.json`).then(file => {
281
284
  const packageJson = JSON.parse(file);
282
- const fullPath = pathModule.join(path, packageJson.main || `${pathModule.sep}index.js`);
285
+ const fullPath = pathModule.join(_path, packageJson.main || `${pathModule.sep}index.js`);
283
286
  return import(fullPath);
284
287
  });
285
288
 
@@ -563,6 +566,7 @@ function runCodeWithWorkerThread(
563
566
  .finally(() => thread?.terminate());
564
567
  }
565
568
 
569
+ /** @param {string} installFolder */
566
570
  async function removeFolder(installFolder) {
567
571
  try {
568
572
  await fse.remove(installFolder);
@@ -571,10 +575,24 @@ async function removeFolder(installFolder) {
571
575
  }
572
576
  }
573
577
 
578
+ /**
579
+ * @param {string} stepResultId
580
+ * @param {string} testResultId
581
+ * @param {string} stepId
582
+ * @param {number} retryIndex
583
+ */
574
584
  function getTransactionId(stepResultId, testResultId, stepId, retryIndex) {
575
585
  return `${testResultId}_${stepId}_${stepResultId}_${retryIndex}`;
576
586
  }
577
587
 
588
+ /**
589
+ * @param {string} stepId
590
+ * @param {string} testResultId
591
+ * @param {number} retryIndex
592
+ * @param {{ packageName: string; packageVersion: string }[]} packageData
593
+ * @param {string} stepResultId
594
+ * @param {number} timeout
595
+ */
578
596
  function installPackage(stepId, testResultId, retryIndex, packageData, stepResultId, timeout) {
579
597
  const transactionId = getTransactionId(stepResultId, testResultId, stepId, retryIndex);
580
598
  return runNpmInstall(transactionId, packageData, timeout).then(({ data }) => data);
@@ -622,6 +640,11 @@ function runCodeWithPackages(code, stepId, incomingParams, context, testResultId
622
640
  }).then(res => Object.assign({}, res, { nodeVersion: process.version }));
623
641
  }
624
642
 
643
+ /**
644
+ * @param {string} transactionId
645
+ * @param {{ packageName: string; packageVersion: string }[]} packageData
646
+ * @param {number} timeout
647
+ */
625
648
  async function runNpmInstall(transactionId, packageData, timeout) {
626
649
  const packages = packageData.map(data => `${data.packageName}@${data.packageVersion}`);
627
650
  const localPackageInstallFolder = getLocalPackageInstallFolder();
@@ -150,19 +150,22 @@ async function compileFunctionsLibrary({ fileSystem, bypassWebpack } = {}, optio
150
150
  const Module = require('module');
151
151
  // attempt to require without webpack compile - useful for puppeteer/selenium hybrid
152
152
  const originalRequire = Module.prototype.require;
153
- Module.prototype.require = function requireThatOverridesSessionPlayer(id) {
153
+ Module.prototype.require = function requireThatOverridesTestimTDK(id) {
154
154
  if (id === 'testim') {
155
155
  return bypassWebpack.testim;
156
156
  }
157
157
  // eslint-disable-next-line prefer-rest-params
158
158
  return originalRequire.apply(this, arguments);
159
159
  };
160
- // delete references to the old __testim from previous invocations
161
- delete require.cache[require.resolve(functionsFile)];
162
- const functions = require(functionsFile);
163
- // asynchronous required not supported - is this fine?
164
- Module.prototype.require = originalRequire;
165
- return functions;
160
+ try {
161
+ // delete references to the old __testim from previous invocations
162
+ delete require.cache[require.resolve(functionsFile)];
163
+ const functions = require(functionsFile); // eslint-disable-line import/no-dynamic-require
164
+ // asynchronous require not supported - is this fine?
165
+ return functions;
166
+ } finally {
167
+ Module.prototype.require = originalRequire;
168
+ }
166
169
  }
167
170
  const functionsAsAWebpackModule = await buildCodeTests([functionsFile], {
168
171
  output: {
@@ -1,26 +1,27 @@
1
+ /* eslint-disable no-console */
1
2
 
3
+ const os = require('os');
4
+ const path = require('path');
5
+ const chalk = require('chalk');
6
+ const fse = require('fs-extra');
2
7
 
3
8
  module.exports.writeStackTrace = function writeStackTrace(err) {
4
- if(err && err.message && err.message.includes('SIGINT')) {
9
+ if (err?.message?.includes('SIGINT')) {
5
10
  return; // no need to generate a log file for a sigint.
6
11
  }
7
12
  try {
8
- const homedir = require('os-homedir')();
9
- const fse = require('fs-extra');
10
- const path = require('path');
13
+ const homedir = os.homedir();
11
14
 
12
15
  fse.ensureDirSync(path.resolve(homedir, '.testim_logs'));
13
- const logfilename = path.resolve(homedir, '.testim_logs', new Date().toISOString().replace(/:|\./g, '_') + '.log');
16
+ const logFileName = path.resolve(homedir, '.testim_logs', `${new Date().toISOString().replace(/:|\./g, '_')}.log`);
14
17
  console.log('Oops :( The test runner has encountered an unexpected error. A complete log of this run can be found in:');
15
- console.log(`\t${logfilename}`);
18
+ console.log(`\t${logFileName}`);
16
19
 
17
- if (err && err.message && err.message.includes('Unable to compile TypeScript') && err.stack.includes('runner/src')
18
- && process.argv.some(x => x.includes('player-require-path'))) {
19
- const chalk = require('chalk');
20
+ if (err?.message?.includes('Unable to compile TypeScript') && err.stack.includes('runner/src') && process.argv.some(x => x.includes('player-require-path'))) {
20
21
  console.log(chalk.red('Looks like you got a TypeScript compile error champ - but it\'s not a very good one because we use TypeScript in transpile-only mode'));
21
- console.log(chalk.red(`change require('ts-node/register/transpile-only'); to require('ts-node/register'); for better errors`));
22
+ console.log(chalk.red('change require(\'ts-node/register/transpile-only\'); to require(\'ts-node/register\'); for better errors'));
22
23
  }
23
24
 
24
- fse.writeFileSync(logfilename, err + "\n" + err.stack + "\n\n" + JSON.stringify(err, Object.getOwnPropertyNames(err), 2));
25
- } catch (err) { }
26
- }
25
+ fse.writeFileSync(logFileName, `${err}\n${err.stack}\n\n${JSON.stringify(err, Object.getOwnPropertyNames(err), 2)}`);
26
+ } catch { /* noop */ }
27
+ };
package/cli.js CHANGED
@@ -4,26 +4,23 @@
4
4
  'use strict';
5
5
 
6
6
  require('./polyfills');
7
- const options = require('./runOptions');
7
+ const semver = require('semver');
8
8
  const EventEmitter = require('events');
9
- const logger = require('./commons/logger').getLogger('cli-entry');
10
- const { onExit, ignoreFailingTestsInExitCode } = require('./cli/onExit');
9
+ const utils = require('./utils');
11
10
  const testRunner = require('./runner');
11
+ const options = require('./runOptions');
12
+ const agentMode = require('./cliAgentMode');
13
+ const perf = require('./commons/performance-logger');
12
14
  const prepareRunner = require('./commons/prepareRunner');
15
+ const prepareRunnerAndTestimStartUtils = require('./commons/prepareRunnerAndTestimStartUtils');
16
+ const { getLogger } = require('./commons/logger');
13
17
  const { CLI_MODE } = require('./commons/constants');
18
+ const { registerProcessForCleanup } = require('./processHandler');
19
+ const { NoArgsError, SeleniumError, ArgError } = require('./errors');
20
+ const { onExit, ignoreFailingTestsInExitCode } = require('./cli/onExit');
14
21
  const { updateRemoteRunFailure } = require('./commons/testimServicesApi');
15
- const prepareRunnerAndTestimStartUtils = require('./commons/prepareRunnerAndTestimStartUtils');
16
22
 
17
- const {
18
- NoArgsError,
19
- SeleniumError,
20
- ArgError,
21
- } = require('./errors');
22
-
23
- const utils = require('./utils');
24
- const semver = require('semver');
25
- const perf = require('./commons/performance-logger');
26
- const agentMode = require('./cliAgentMode');
23
+ const logger = getLogger('cli-entry');
27
24
 
28
25
  try {
29
26
  require('./fixLocalBuild');
@@ -56,7 +53,7 @@ async function checkNodeVersion() {
56
53
  async function main() {
57
54
  console.log('Starting Testim.io CLI');
58
55
  perf.log('Starting Testim.io CLI');
59
- require('./processHandler')(onExit);
56
+ registerProcessForCleanup(onExit);
60
57
 
61
58
  checkNodeVersion().catch(err => {
62
59
  console.log('Argument Error:', err.message);
@@ -62,6 +62,7 @@ class FeatureFlagsService {
62
62
  LTNetworkCapabilities: new Rox.Flag(),
63
63
  downloadToBase64: new Rox.Flag(),
64
64
  dec2022eolBrowsers: new Rox.Flag(),
65
+ publicGridURL: new Rox.Configuration('public-grid.testim.io'),
65
66
  };
66
67
  Rox.register('default', this.flags);
67
68
  }
@@ -1,13 +1,28 @@
1
1
  'use strict';
2
2
 
3
- const perf = require('./performance-logger');
3
+ const { join, resolve } = require('path');
4
4
 
5
- perf.log('getSessionPlayerRequire start');
6
- const { getSessionPlayerFolder } = require('./prepareRunnerAndTestimStartUtils');
5
+ /** @type {{ playerPath: string | undefined }} */ module.exports.options = { playerPath: undefined };
7
6
 
8
- const testimAppDataFolder = getSessionPlayerFolder();
9
- /** @type {import('clickim/src/background/sessionPlayerInit').SessionPlayerInit} */
10
- const sessionPlayer = require(require('path').join(testimAppDataFolder, 'sessionPlayer.js')); // eslint-disable-line import/no-dynamic-require
7
+ /** @type {import('clickim/src/background/sessionPlayerInit').SessionPlayerInit | undefined} */ let sessionPlayerInit;
11
8
 
12
- module.exports = sessionPlayer;
13
- perf.log('getSessionPlayerRequire end');
9
+ module.exports.getSessionPlayer = () => {
10
+ if (sessionPlayerInit) {
11
+ return sessionPlayerInit;
12
+ }
13
+ const perf = require('./performance-logger');
14
+
15
+ perf.log('getSessionPlayerRequire start');
16
+ const { getSessionPlayerFolder } = require('./prepareRunnerAndTestimStartUtils');
17
+
18
+ const testimAppDataFolder = getSessionPlayerFolder();
19
+ const { options } = module.exports;
20
+ const path = options.playerPath ? resolve(options.playerPath, 'src/background/sessionPlayerInit.ts') : join(testimAppDataFolder, 'sessionPlayer.js');
21
+
22
+ /** @type {import('clickim/src/background/sessionPlayerInit').SessionPlayerInit} */
23
+ const sessionPlayer = require(path); // eslint-disable-line import/no-dynamic-require
24
+
25
+ perf.log('getSessionPlayerRequire end');
26
+ sessionPlayerInit = sessionPlayer;
27
+ return sessionPlayer;
28
+ };
@@ -62,13 +62,13 @@ module.exports.makeCounters = () => {
62
62
  * @template T, TArgs
63
63
  * @param {(...args: TArgs[]) => T} fn
64
64
  * @param {string=} name
65
- * @return {(...args: TArgs[]) => Promise<Awaited<T>>}
65
+ * @return {(...args: TArgs[]) => Promise<T>}
66
66
  */
67
67
  function wrapWithMonitoring(fn, name = fn.name) {
68
68
  return async function (...args) {
69
69
  update(counters.call, name);
70
70
  try {
71
- const result = await fn.call(this, ...args);
71
+ const result = await fn.call(null, ...args);
72
72
  update(counters.success, name);
73
73
  return result;
74
74
  } catch (e) {
@@ -15,7 +15,7 @@ function preloadSlowRequires(mode) {
15
15
  }
16
16
  // heuristic to pay the cost of loading the sessionPlayer here while we are waiting for the backend
17
17
  try {
18
- require('./getSessionPlayerRequire');
18
+ require('./getSessionPlayerRequire').getSessionPlayer();
19
19
  // jsdom for the same reason, we don't require workerSelenium here since it actually takes longer to load
20
20
  // then it takes for the backend to return 🤯
21
21
  require('jsdom');
@@ -529,7 +529,11 @@ function buildSeleniumOptions(browserOptions, testName, testRunConfig, gridInfo,
529
529
  * One level targeting (either grid provider, host, browser name or browser version): { "devicefarm": { selenium_version: '3.141.59' } }
530
530
  * Two level targeting: { "internet explorer": { "11": { selenium_version: '3.141.59' } } }
531
531
  */
532
- const hostToProvider = { 'hub.lambdatest.com': 'lambdatest', 'public-grid.testim.io': 'testim', 'testgrid-devicefarm.us-west-2.amazonaws.com': 'devicefarm' };
532
+ const hostToProvider = {
533
+ 'hub.lambdatest.com': 'lambdatest',
534
+ [featureFlags.flags.publicGridURL.getValue()]: 'testim',
535
+ 'testgrid-devicefarm.us-west-2.amazonaws.com': 'devicefarm',
536
+ };
533
537
  const byGrid = (capabilities) => capabilities[gridInfo.provider] || capabilities[opts.host] || capabilities[hostToProvider[opts.host]];
534
538
  const getTargetingGroup = capabilities =>
535
539
  byGrid(capabilities) ||
@@ -576,6 +580,7 @@ function buildAppiumOptions({ projectType, gridInfo, testRunConfig, nativeApp, o
576
580
  hostname: gridInfo.host,
577
581
  port: gridInfo.port,
578
582
  path: `/v0/${gridInfo.accessToken}/wd/hub`,
583
+ logLevel: options.appiumLogLevel,
579
584
  // connectionRetryTimeout: 900000, -- not used for now
580
585
  };
581
586
  //TODO: check if more caps should be defined as default