@testim/testim-cli 3.254.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.
Files changed (43) hide show
  1. package/agent/routers/codim/router.test.js +9 -12
  2. package/agent/routers/codim/service.js +16 -16
  3. package/agent/routers/playground/service.js +5 -7
  4. package/cli.js +4 -4
  5. package/cliAgentMode.js +4 -3
  6. package/codim/codim-cli.js +10 -8
  7. package/commons/featureFlags.js +8 -0
  8. package/commons/httpRequest.js +5 -1
  9. package/commons/httpRequestCounters.js +21 -10
  10. package/commons/lazyRequire.js +4 -3
  11. package/commons/preloadTests.js +2 -2
  12. package/commons/prepareRunner.js +4 -2
  13. package/commons/prepareRunnerAndTestimStartUtils.js +40 -41
  14. package/commons/runnerFileCache.js +1 -1
  15. package/commons/testimTunnel.test.js +2 -1
  16. package/coverage/SummaryToObjectReport.js +0 -1
  17. package/coverage/jsCoverage.js +12 -10
  18. package/inputFileUtils.js +11 -9
  19. package/npm-shrinkwrap.json +187 -444
  20. package/package.json +4 -3
  21. package/player/services/tabService.js +15 -1
  22. package/player/stepActions/locateStepAction.js +2 -0
  23. package/player/utils/imageCaptureUtils.js +81 -120
  24. package/player/webdriver.js +25 -22
  25. package/reports/junitReporter.js +6 -7
  26. package/reports/reporter.js +34 -39
  27. package/runOptions.d.ts +260 -0
  28. package/runOptions.js +53 -38
  29. package/runner.js +2 -1
  30. package/runners/ParallelWorkerManager.js +9 -10
  31. package/runners/TestPlanRunner.js +5 -9
  32. package/services/gridService.js +36 -40
  33. package/testRunStatus.js +8 -5
  34. package/utils/argsUtils.js +86 -0
  35. package/utils/argsUtils.test.js +32 -0
  36. package/utils/fsUtils.js +154 -0
  37. package/utils/index.js +10 -161
  38. package/utils/promiseUtils.js +13 -2
  39. package/utils/stringUtils.js +4 -2
  40. package/utils/stringUtils.test.js +22 -0
  41. package/utils/timeUtils.js +25 -0
  42. package/utils/utils.test.js +0 -41
  43. package/workers/WorkerExtension.js +6 -7
@@ -1,20 +1,16 @@
1
1
  'use strict';
2
2
 
3
3
  const router = require('./router');
4
-
5
4
  const path = require('path');
6
5
  const os = require('os');
7
6
  const compression = require('compression');
8
7
  const express = require('express');
9
-
10
- const app = express();
11
8
  const bodyParser = require('body-parser');
12
- const Promise = require('bluebird');
13
- const { promiseFromCallback } = require('../../../utils');
14
9
  const superagent = require('superagent');
10
+ const fs = require('fs');
15
11
  const { expect } = require('chai'); // eslint-disable-line import/no-extraneous-dependencies
16
12
 
17
- const fs = Promise.promisifyAll(require('fs'));
13
+ const app = express();
18
14
 
19
15
  app.use(bodyParser.urlencoded({ extended: false, limit: '50mb' }));
20
16
  app.use(compression());
@@ -25,25 +21,26 @@ app.use('/files', router.router);
25
21
  describe('codim router', () => {
26
22
  let listener;
27
23
  async function saveLocators(locatorsObject) {
28
- const request = superagent.post(`http://localhost:${listener.address().port}/files/locators`)
29
- .send(locatorsObject);
30
- await promiseFromCallback((callback) => request.end(callback));
24
+ const request = superagent.post(`http://localhost:${listener.address().port}/files/locators`).send(locatorsObject);
25
+ await request;
31
26
  }
32
27
 
33
28
  async function loadLocators() {
34
29
  const request = superagent.get(`http://localhost:${listener.address().port}/files/locators`);
35
- return await promiseFromCallback((callback) => request.end(callback)).then(x => x.text).then(JSON.parse);
30
+ const response = await request;
31
+ return JSON.parse(response.text);
36
32
  }
37
33
 
38
34
  let tmpDir;
35
+
39
36
  before((done) => {
40
37
  tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tdk-test-'));
41
38
  process.chdir(tmpDir);
42
- listener = app.listen(0, done);
39
+ listener = app.listen(0, () => done());
43
40
  });
44
41
 
45
42
  after(async () => {
46
- await fs.unlinkAsync(tmpDir).catch(() => {});
43
+ await fs.promises.unlink(tmpDir).catch(() => {});
47
44
  });
48
45
 
49
46
  it('saves locators', async () => {
@@ -2,17 +2,16 @@
2
2
 
3
3
  const _ = require('lodash');
4
4
  const path = require('path');
5
- const Promise = require('bluebird');
6
- const fs = Promise.promisifyAll(require('fs'));
5
+ const fsPromises = require('fs/promises');
7
6
  const { fromPairs } = require('lodash');
8
7
  const { buildCodeTests } = require('../../../runners/buildCodeTests');
9
8
  const { AbortError } = require('../../../commons/AbortError');
10
9
 
11
10
  const findTestFolder = _.memoize(async (fromFolder) => {
12
- const files = await fs.readdirAsync(fromFolder);
11
+ const files = await fsPromises.readdir(fromFolder);
13
12
  // this is either invoked by running the Testim CLI from inside the tests folder or from inside the `init` folder
14
13
  // so deal with the case we're inside tests.
15
- const isInProjectFolder = files.includes('tests') && (await fs.statAsync(path.join(fromFolder, 'tests'))).isDirectory();
14
+ const isInProjectFolder = files.includes('tests') && (await fsPromises.stat(path.join(fromFolder, 'tests'))).isDirectory();
16
15
  if (isInProjectFolder) {
17
16
  return path.join(fromFolder, 'tests');
18
17
  }
@@ -27,7 +26,7 @@ async function getLocalLocators() {
27
26
  // eslint-disable-next-line no-eval
28
27
  return eval(buffer.toString().replace(/require/g, '(x => /locator\.(.*)\.json/.exec(x)[1])'));
29
28
  }
30
- const locators = await fs.readFileAsync(locatorsFilePath).then(parseLocators, () => ({}));
29
+ const locators = await fsPromises.readFile(locatorsFilePath).then(parseLocators, () => ({}));
31
30
  return _(Object.keys(locators)).map((id) => {
32
31
  const escapedId = id.replace(/"/g, '\\"');
33
32
  return [escapedId, locators[id]];
@@ -36,7 +35,7 @@ async function getLocalLocators() {
36
35
 
37
36
  async function findTests(folder = process.cwd()) {
38
37
  const testFolder = await findTestFolder(folder);
39
- const filesWithStat = await fs.promises.readdir(testFolder, { withFileTypes: true });
38
+ const filesWithStat = await fsPromises.readdir(testFolder, { withFileTypes: true });
40
39
 
41
40
  // things we know are not tests but end in js
42
41
  const excluded = ['webpack.config.js', 'tsconfig.js', '.DS_Store', 'functions.js'];
@@ -70,13 +69,14 @@ async function getLocalLocatorContents(locators, full = false, originFolder = pr
70
69
  if (full) {
71
70
  const folder = await findTestFolder(originFolder);
72
71
  for (const key of Object.values(locators)) {
73
- props[key] = fs.promises.readFile(path.join(folder, 'locators', `locator.${key}.json`)).then(JSON.parse);
72
+ props[key] = fsPromises.readFile(path.join(folder, 'locators', `locator.${key}.json`)).then(JSON.parse);
74
73
  }
75
74
  }
76
75
  try {
77
76
  const contents = await promiseFromProps(props);
78
77
  return contents;
79
78
  } catch (e) {
79
+ // eslint-disable-next-line no-console
80
80
  console.error(e);
81
81
  return {};
82
82
  }
@@ -104,10 +104,10 @@ async function saveTest({
104
104
  if (filename.endsWith('locators/locators.js')) {
105
105
  throw new Error('Cannot override locators file from the internet as it is evaluated by the runner');
106
106
  }
107
- await fs.writeFileAsync(filename, body);
108
- await fs.mkdirAsync(path.join(folder, 'locators')).catch(() => {});
107
+ await fsPromises.writeFile(filename, body);
108
+ await fsPromises.mkdir(path.join(folder, 'locators')).catch(() => {});
109
109
  for (const { id, body } of locators) {
110
- await fs.writeFileAsync(path.join(folder, 'locators', `locator.${id}.json`), JSON.stringify(body));
110
+ await fsPromises.writeFile(path.join(folder, 'locators', `locator.${id}.json`), JSON.stringify(body));
111
111
  }
112
112
  const locatorMap = fromPairs(locators.map(({ name, id }) => [name, id]));
113
113
  const localLocatorMap = await getLocalLocators();
@@ -120,15 +120,15 @@ async function writeLocators(locatorsFilePath, locatorMap) {
120
120
  content += ` "${key}": require('./locator.${value}.json'),\n`;
121
121
  }
122
122
  content += '};';
123
- await fs.writeFileAsync(locatorsFilePath, content);
123
+ await fsPromises.writeFile(locatorsFilePath, content);
124
124
  }
125
125
  async function saveLocators(locators, { mergeIntoExisting } = { mergeIntoExisting: false }) {
126
126
  const folder = await findTestFolder(process.cwd());
127
127
  const locatorsFilePath = path.join(folder, 'locators', 'locators.js');
128
- await fs.mkdirAsync(path.join(folder, 'locators')).catch(() => {});
128
+ await fsPromises.mkdir(path.join(folder, 'locators')).catch(() => {});
129
129
 
130
130
  for (const { name, id, elementLocator } of locators) {
131
- await fs.writeFileAsync(path.join(folder, 'locators', `locator.${id}.json`), JSON.stringify({ name, id, elementLocator }));
131
+ await fsPromises.writeFile(path.join(folder, 'locators', `locator.${id}.json`), JSON.stringify({ name, id, elementLocator }));
132
132
  }
133
133
  const locatorMap = fromPairs(locators.map(({ name, id }) => [name, id]));
134
134
  if (mergeIntoExisting) {
@@ -139,14 +139,14 @@ async function saveLocators(locators, { mergeIntoExisting } = { mergeIntoExistin
139
139
  await writeLocators(locatorsFilePath, locatorMap);
140
140
  }
141
141
 
142
- async function compileFunctionsLibrary({ fileSystem, bypassWebpack } = {}, optionalAbortSignal) {
142
+ async function compileFunctionsLibrary({ fileSystem, bypassWebpack } = {}, optionalAbortSignal = undefined) {
143
143
  const folder = await findTestFolder(process.cwd());
144
- if (optionalAbortSignal && optionalAbortSignal.aborted) {
144
+ if (optionalAbortSignal?.aborted) {
145
145
  throw new AbortError();
146
146
  }
147
147
 
148
148
  const functionsFile = path.join(folder, 'functions.js');
149
- if (bypassWebpack && bypassWebpack.testim) {
149
+ if (bypassWebpack?.testim) {
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;
@@ -1,11 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const Promise = require('bluebird');
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');
6
+ const { fork } = require('child_process');
9
7
  const { ClientError, PlaygroundCodeError } = require('../../../errors');
10
8
 
11
9
  const CODE_TYPES = ['playwright', 'selenium', 'puppeteer'];
@@ -14,13 +12,13 @@ const runForks = {};
14
12
 
15
13
  async function createTempFile(fileName, data, encoding = 'utf8') {
16
14
  const fullPath = path.join(os.tmpdir(), fileName);
17
- await writeFileAsync(fullPath, data, encoding);
15
+ await fs.promises.writeFile(fullPath, data, encoding);
18
16
  return fullPath;
19
17
  }
20
18
 
21
19
  const forkAsync = (fileFullPath) => {
22
20
  let gotResolved;
23
- const promise = new Promise(resolve => gotResolved = resolve);
21
+ const promise = new Promise(resolve => { gotResolved = resolve; });
24
22
 
25
23
  const child = fork(fileFullPath, { stdio: ['inherit', 'inherit', 'inherit', 'ipc'] });
26
24
  promise.child = child;
@@ -30,7 +28,7 @@ const forkAsync = (fileFullPath) => {
30
28
  }
31
29
  const { type, error } = message;
32
30
  if (error && ['uncaughtException', 'unhandledRejection'].includes(type)) {
33
- return gotResolved({ error: Object.assign(new PlaygroundCodeError(), { innerStack: message.error.stack }) });
31
+ gotResolved({ error: Object.assign(new PlaygroundCodeError(), { innerStack: message.error.stack }) });
34
32
  }
35
33
  });
36
34
  child.on('error', (error) => {
package/cli.js CHANGED
@@ -68,14 +68,14 @@ async function main() {
68
68
  require('./commons/logger').setProjectId(processedOptions.project);
69
69
  require('./commons/runnerFileCache').setEncryptKey(typeof processedOptions.token === 'string' ? processedOptions.token : 'anonymous_encrypt_key');
70
70
 
71
- if (processedOptions.initCodimMode) {
71
+ if (utils.isInitCodimMode(processedOptions)) {
72
72
  const codimCli = require('./codim/codim-cli');
73
73
  return codimCli.init(processedOptions.initTestProject);
74
74
  }
75
- if (processedOptions.loginMode) {
75
+ if (utils.isLoginMode(processedOptions)) {
76
76
  return undefined;
77
77
  }
78
- if (processedOptions.createPrefechedData) {
78
+ if (utils.isCreatePrefetchedDataMode(processedOptions)) {
79
79
  const runnerFileCache = require('./commons/runnerFileCache');
80
80
  await runnerFileCache.clear();
81
81
  await prepareRunner.initializeUserWithAuth(processedOptions);
@@ -92,7 +92,7 @@ async function main() {
92
92
  return undefined;
93
93
  }
94
94
 
95
- if (processedOptions.tunnelOnlyMode) {
95
+ if (utils.isTunnelOnlyMode(processedOptions)) {
96
96
  await testRunner.init(processedOptions);
97
97
  await require('./commons/testimTunnel').serveTunneling(processedOptions);
98
98
  return undefined;
package/cliAgentMode.js CHANGED
@@ -32,9 +32,10 @@ module.exports = {
32
32
  };
33
33
 
34
34
  /**
35
- * @param {{ agentMode: boolean; }} options
35
+ * @type {(options: import('./runOptions').Options) => options is import('./runOptions').AgentModeOptions}
36
36
  */
37
37
  function shouldStartAgentMode(options) {
38
+ // @ts-ignore should be `as any`
38
39
  return options.agentMode;
39
40
  }
40
41
 
@@ -53,7 +54,7 @@ async function runAgentMode(options) {
53
54
  // Consider moving that into the agent server and add endpoint to start browser?
54
55
  testimStandaloneBrowser = await startTestimStandaloneBrowser(options);
55
56
  } catch (e) {
56
- if (e && e.message && e.message.includes('user data directory is already in use')) {
57
+ if (e?.message?.includes('user data directory is already in use')) {
57
58
  throw new ArgError('Please close all chrome browsers that were opened with "testim start" and try again');
58
59
  }
59
60
  throw e;
@@ -62,7 +63,7 @@ async function runAgentMode(options) {
62
63
 
63
64
  const agentServer = require('./agent/server');
64
65
 
65
- if (testimStandaloneBrowser && testimStandaloneBrowser.webdriverApi) {
66
+ if (testimStandaloneBrowser?.webdriverApi) {
66
67
  // if we're starting the agent here, pre-load the sessionPlayer so it loads faster
67
68
  // on first play
68
69
  const LOAD_PLAYER_DELAY = 6000;
@@ -2,12 +2,14 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- const { promisifyAll } = require('bluebird');
6
- const fse = require('fs-extra');
7
- const exec = promisifyAll(require('child_process')).execAsync;
5
+ const fs = require('fs');
6
+ const childProcess = require('child_process');
8
7
  const path = require('path');
9
8
  const validateNpmPackageName = require('validate-npm-package-name');
10
9
  const ArgError = require('../errors.js').ArgError;
10
+ const { promisify } = require('util');
11
+
12
+ const exec = promisify(childProcess.exec);
11
13
 
12
14
  module.exports.init = async function init(name) {
13
15
  const ora = require('ora');
@@ -25,7 +27,7 @@ module.exports.init = async function init(name) {
25
27
 
26
28
  const fullPath = path.resolve(name);
27
29
 
28
- if (fse.existsSync(fullPath) && fse.readdirSync(fullPath).length !== 0) {
30
+ if (fs.existsSync(fullPath) && fs.readdirSync(fullPath).length !== 0) {
29
31
  console.log(`${fullPath} is not empty. Quitting...`);
30
32
  process.exit(1);
31
33
  }
@@ -57,20 +59,20 @@ module.exports.init = async function init(name) {
57
59
 
58
60
  let spinner = ora(`Creating new test project in ${dest}`).start();
59
61
 
60
- await fse.copy(source, dest);
62
+ await fs.promises.copyFile(source, dest);
61
63
 
62
64
  const sourcePackageJson = path.join(__dirname, sourceFolder, 'package.json');
63
65
  const destPackageJson = path.join(process.cwd(), name, 'package.json');
64
66
 
65
- const packageContents = await fse.readFile(sourcePackageJson);
67
+ const packageContents = await fs.promises.readFile(sourcePackageJson);
66
68
 
67
69
  const newPackageJson = packageContents.toString().replace('~testim-codeful-test-project~', packageName);
68
70
 
69
- await fse.writeFile(destPackageJson, newPackageJson);
71
+ await fs.promises.writeFile(destPackageJson, newPackageJson);
70
72
 
71
73
  const gitIgnore = 'node_modules';
72
74
  const gitIgnoreFilePath = path.join(process.cwd(), name, '.gitignore');
73
- await fse.writeFile(gitIgnoreFilePath, gitIgnore);
75
+ await fs.promises.writeFile(gitIgnoreFilePath, gitIgnore);
74
76
 
75
77
  spinner.succeed();
76
78
  spinner = ora('Installing dependencies').start();
@@ -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
  }
@@ -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 strit';
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 Bluebird.any(hostnames.map(host => dns.promises.lookup(host))));
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 result = counter.get(key) || 1;
52
- counter.set(key, result - 1);
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);
@@ -1,6 +1,5 @@
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');
@@ -76,10 +75,12 @@ async function lazyRequireImpl(dependency) {
76
75
  return requireWithFallback(dependency);
77
76
  }
78
77
 
79
- function installAllLazyDependencies() {
78
+ async function installAllLazyDependencies() {
80
79
  const allLazyDependencies = Object.keys(packageJson.lazyDependencies);
81
80
 
82
- return Bluebird.each(allLazyDependencies, dep => lazyRequireImpl(dep));
81
+ for (const dep of allLazyDependencies) {
82
+ await lazyRequireImpl(dep);
83
+ }
83
84
  }
84
85
 
85
86
  if (require.main === module) {
@@ -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 Promise.map(options.testId, testId => servicesApi.loadTest({ ...opts, testId }), { concurrency: 2 });
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
  }
@@ -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 && options.lightweightMode.general)
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,9 +1,7 @@
1
1
  // @ts-check
2
2
 
3
- // @ts-ignore
4
- const Promise = require('bluebird');
5
3
  const path = require('path');
6
- const fs = Promise.promisifyAll(require('fs'));
4
+ const fs = require('fs');
7
5
  const ms = require('ms');
8
6
  const { serializeError } = require('serialize-error');
9
7
  const { additionalLogDetails } = require('./logUtils');
@@ -11,7 +9,15 @@ const { additionalLogDetails } = require('./logUtils');
11
9
  const config = require('./config');
12
10
  const { ArgError, NpmPermissionsError } = require('../errors');
13
11
  const {
14
- getCliLocation, isURL, downloadAndSave, getSource, getLocalFileSizeInMB, download, unzipFile, getSourcePath,
12
+ getCliLocation,
13
+ isURL,
14
+ downloadAndSave,
15
+ getSource,
16
+ getLocalFileSizeInMB,
17
+ download,
18
+ unzipFile,
19
+ getSourcePath,
20
+ promiseMap,
15
21
  } = require('../utils');
16
22
  const localRunnerCache = require('./runnerFileCache');
17
23
  const logger = require('./logger').getLogger('prepare runner and testim start');
@@ -31,63 +37,58 @@ module.exports = {
31
37
  /**
32
38
  * @param {string} location
33
39
  */
34
- function prepareCustomExtension(location, unlimitedSize = false) {
40
+ async function prepareCustomExtension(location, unlimitedSize = false) {
35
41
  if (!location) {
36
- return Promise.resolve();
42
+ return undefined;
37
43
  }
38
44
 
39
45
  if (isURL(location)) {
40
46
  const destFile = path.join(process.cwd(), location.replace(/^.*[\\\/]/, ''));
41
- return getRemoteFileSizeInMB(location)
42
- .then(contentLength => {
43
- if (contentLength > MAX_CUSTOM_EXT_SIZE_MB && !unlimitedSize) {
44
- throw new ArgError(MAX_CUSTOM_SIZE_ERROR_MSG);
45
- }
46
- return downloadAndSave(location, destFile);
47
- })
48
- .then(() => 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;
49
53
  }
50
54
 
51
55
  const destFile = path.resolve(location);
52
56
  if (!fs.existsSync(destFile)) {
53
- return Promise.reject(new ArgError(`Failed to find custom extension in location: ${destFile}`));
57
+ throw new ArgError(`Failed to find custom extension in location: ${destFile}`);
54
58
  }
55
59
  const fileSize = getLocalFileSizeInMB(destFile);
56
60
  if (fileSize > MAX_CUSTOM_EXT_SIZE_MB && !unlimitedSize) {
57
- return Promise.reject(new ArgError(MAX_CUSTOM_SIZE_ERROR_MSG));
61
+ throw new ArgError(MAX_CUSTOM_SIZE_ERROR_MSG);
58
62
  }
59
- return Promise.resolve(destFile);
63
+ return destFile;
60
64
  }
61
65
 
62
66
 
63
67
  /**
64
68
  * @param {string} url
65
69
  */
66
- function getRemoteFileSizeInMB(url) {
70
+ async function getRemoteFileSizeInMB(url) {
67
71
  const httpRequest = require('./httpRequest');
68
- return httpRequest.head(url)
69
- .then(res => {
70
- const contentLengthHeader = res.headers['content-length'];
71
- const contentLengthBytes = contentLengthHeader ? parseInt(contentLengthHeader, 10) : 0;
72
- return contentLengthBytes / 1000000;
73
- })
74
- .catch(err => {
75
- logger.warn('failed to download custom extension', { err });
76
- throw new ArgError(`Failed to download custom extension from location: ${url}`);
77
- });
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
+ }
78
81
  }
79
82
 
80
83
  /**
81
- *
82
84
  * @param {string[]} locations
83
- *
84
85
  */
85
86
  function prepareExtension(locations) {
86
87
  logger.info('prepare extension', { locations });
87
88
 
88
89
  const fullLocations = locations.map(location => ({ location, path: getSourcePath(location) }));
89
90
  return localRunnerCache.memoize(
90
- () => Promise.map(fullLocations, ({ location, path }) => getSource(location, path)),
91
+ () => promiseMap(fullLocations, ({ location, path }) => getSource(location, path)),
91
92
  'prepareExtension',
92
93
  MSEC_IN_HALF_DAY,
93
94
  fullLocations
@@ -131,25 +132,23 @@ async function prepareChromeDriver(userDetails = {}, driverOptions = {}, skipIsR
131
132
  }
132
133
  }
133
134
 
134
- function getPlayerVersion() {
135
+ async function getPlayerVersion() {
135
136
  const url = `${config.BLOB_URL}/extension/sessionPlayer_LATEST_RELEASE`;
136
- return download(url)
137
- .then(res => res.body.toString('utf8'));
137
+ const res = await download(url);
138
+ return res.body.toString('utf8');
138
139
  }
139
140
 
140
141
  /**
141
142
  * @param {string} location
142
143
  * @param {string | undefined} canary
143
- *
144
- * @returns {Promise<string>}
145
144
  */
146
- function getPlayerLocation(location, canary) {
145
+ async function getPlayerLocation(location, canary) {
147
146
  if (!isURL(location) || (isURL(location) && canary) || config.IS_ON_PREM) {
148
- return Promise.resolve(location);
147
+ return location;
149
148
  }
150
149
 
151
- return getPlayerVersion()
152
- .then(ver => `${location}-${ver}`);
150
+ const ver = await getPlayerVersion();
151
+ return `${location}-${ver}`;
153
152
  }
154
153
 
155
154
  function getSessionPlayerFolder() {
@@ -178,7 +177,7 @@ async function downloadAndUnzip(loc, playerFileName, isRetry = false) {
178
177
  }
179
178
  }
180
179
 
181
- function preparePlayer(location, canary) {
180
+ async function preparePlayer(location, canary) {
182
181
  logger.info('prepare player', { location, canary });
183
182
  const playerFileName = getPlayerDestination();
184
183
  return localRunnerCache.memoize(
@@ -96,7 +96,7 @@ function memoize(fn, fnName, duration = THREE_HOURS, parameters = undefined) {
96
96
  }
97
97
  logger.debug('cache miss:', { fnName });
98
98
  if (!cacheMissAllowed) {
99
- throw new Error(`Attemped to rebuild cache for ${originalFnName}. cache miss is not allowed with current run configuration`);
99
+ throw new Error(`Attempted to rebuild cache for ${originalFnName}. cache miss is not allowed with current run configuration`);
100
100
  }
101
101
  const value = await fn();
102
102
  if (value) {
@@ -66,7 +66,8 @@ describe('testimTunnel', () => {
66
66
 
67
67
  it('should handle connect errors', async () => {
68
68
  ltConnectStub.rejects('error');
69
- await expect(testimTunnel.connect({ tunnel: true, gridData: { type: 'testimLambdaTest', tunnel: 'lambdatest' } })).to.be.rejectedWith('Failed to start tunnel. Please contact support@testim.io');
69
+ const connectPromise = testimTunnel.connect({ tunnel: true, gridData: { type: 'testimLambdaTest', tunnel: 'lambdatest' } });
70
+ await expect(connectPromise).to.be.rejectedWith('Failed to start tunnel. Please contact support@testim.io');
70
71
  });
71
72
  });
72
73
 
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const { ReportBase } = require('istanbul-lib-report');
4
- const _ = require('lodash');
5
4
 
6
5
  class SummaryToObjectReport extends ReportBase {
7
6
  constructor(opts) {