@testim/testim-cli 3.244.0 → 3.247.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 (36) hide show
  1. package/chromiumInstaller.js +92 -0
  2. package/cli.js +89 -87
  3. package/cliAgentMode.js +39 -114
  4. package/commons/chromedriverWrapper.js +4 -3
  5. package/commons/featureFlags.js +1 -0
  6. package/commons/getSessionPlayerRequire.js +2 -1
  7. package/commons/httpRequestCounters.test.js +9 -9
  8. package/commons/npmWrapper.js +3 -3
  9. package/commons/npmWrapper.test.js +1 -1
  10. package/commons/prepareRunner.js +2 -0
  11. package/commons/socket/baseSocketServiceSocketIO.js +2 -2
  12. package/commons/testimDesiredCapabilitiesBuilder.js +1 -3
  13. package/commons/testimServicesApi.js +45 -39
  14. package/errors.js +2 -1
  15. package/npm-shrinkwrap.json +467 -366
  16. package/package.json +2 -2
  17. package/player/WebDriverHttpRequest.js +45 -37
  18. package/player/chromeLauncherTestPlayer.js +2 -2
  19. package/player/services/playbackTimeoutCalculator.js +5 -1
  20. package/player/stepActions/RefreshStepAction.js +2 -4
  21. package/player/stepActions/inputFileStepAction.js +6 -2
  22. package/player/stepActions/sfdcStepAction.js +27 -0
  23. package/player/stepActions/stepActionRegistrar.js +12 -0
  24. package/player/utils/screenshotUtils.js +2 -2
  25. package/player/utils/windowUtils.js +3 -3
  26. package/player/webdriver.js +51 -52
  27. package/runOptions.js +3 -1
  28. package/runner.js +44 -41
  29. package/services/lambdatestService.js +2 -2
  30. package/services/lambdatestService.test.js +2 -1
  31. package/testRunHandler.js +7 -3
  32. package/testRunStatus.js +2 -1
  33. package/utils.js +54 -15
  34. package/utils.test.js +26 -0
  35. package/workers/BaseWorker.js +9 -7
  36. package/workers/BaseWorker.test.js +11 -12
@@ -0,0 +1,92 @@
1
+ const path = require('path');
2
+ const os = require('os');
3
+ const fs = require('fs-extra');
4
+ const { ArgError } = require('./errors');
5
+ const { unzipFile } = require('./utils');
6
+ const { downloadAndSave, TESTIM_BROWSER_DIR } = require('./utils');
7
+ const ora = require('ora');
8
+
9
+
10
+ const CHROMIUM_VERSION = '1000968'; // '973306' = 101.0.4899.0 '1000968' = 103.0.5046.0;
11
+ const DOWNLOAD_CHROME_FOLDER = path.join(TESTIM_BROWSER_DIR, `chrome-${CHROMIUM_VERSION}`);
12
+
13
+ function getCurrentPlatform() {
14
+ const osType = os.type().toLowerCase();
15
+ if (osType === 'darwin') {
16
+ return os.arch() === 'arm' ? 'mac_arm' : 'mac';
17
+ }
18
+ if (osType === 'windows_nt') {
19
+ return os.arch() === 'x64' ? 'win64' : 'win32';
20
+ }
21
+ return 'linux';
22
+ }
23
+
24
+ async function downloadAndInstallChromium() {
25
+ /** Inspired by puppeteer's implementation https://github.com/puppeteer/puppeteer/blob/main/src/node/BrowserFetcher.ts#L45 */
26
+ const platform = getCurrentPlatform();
27
+
28
+ // example download url: https://storage.googleapis.com/chromium-browser-snapshots/Mac/1000968/chrome-mac.zip
29
+ const storageBaseUrl = 'https://storage.googleapis.com/chromium-browser-snapshots';
30
+ const platformFolder = {
31
+ linux: 'Linux_x64',
32
+ mac: 'Mac',
33
+ mac_arm: 'Mac_Arm',
34
+ win32: 'Win',
35
+ win64: 'Win_x64',
36
+ };
37
+ if (!(platform in platformFolder)) {
38
+ throw new ArgError(`Unsupported platform: ${platform}`);
39
+ }
40
+ // Windows archive name changed at r591479.
41
+ const winArchiveName = parseInt(CHROMIUM_VERSION, 10) > 591479 ? 'chrome-win' : 'chrome-win32';
42
+ const platformArchiveName = {
43
+ linux: 'chrome-linux',
44
+ mac: 'chrome-mac',
45
+ mac_arm: 'chrome-mac',
46
+ win32: winArchiveName,
47
+ win64: winArchiveName,
48
+ };
49
+ const binaryPaths = {
50
+ linux: 'chrome',
51
+ mac: 'Chromium.app/Contents/MacOS/Chromium',
52
+ mac_arm: 'Chromium.app/Contents/MacOS/Chromium',
53
+ win32: 'chrome.exe',
54
+ win64: 'chrome.exe',
55
+ };
56
+ const downloadUrl = `${storageBaseUrl}/${platformFolder[platform]}/${CHROMIUM_VERSION}/${platformArchiveName[platform]}.zip`;
57
+ const downloadArchivePath = path.join(DOWNLOAD_CHROME_FOLDER, platformArchiveName[platform]);
58
+ const downloadedZipFile = `${downloadArchivePath}.zip`;
59
+ const binaryPath = path.join(downloadArchivePath, binaryPaths[platform]);
60
+
61
+ if (await fs.pathExists(binaryPath)) {
62
+ return binaryPath;
63
+ }
64
+ if (!(await fs.pathExists(downloadedZipFile))) {
65
+ const downloadSpinner = ora('Downloading Chromium').start();
66
+ try {
67
+ await fs.mkdirp(DOWNLOAD_CHROME_FOLDER);
68
+ await downloadAndSave(downloadUrl, downloadedZipFile);
69
+ // todo - We can add a failover here if the download fails if we host the file too
70
+ } catch (e) {
71
+ const errorMessage = `Failed to download Chromium: ${e.message}`;
72
+ downloadSpinner.fail(errorMessage);
73
+ throw new Error(errorMessage);
74
+ }
75
+ downloadSpinner.succeed();
76
+ }
77
+ const extractSpinner = ora('Extracting Chromium').start();
78
+ try {
79
+ await unzipFile(downloadedZipFile, DOWNLOAD_CHROME_FOLDER);
80
+ } catch (e) {
81
+ const errorMessage = `Failed to extract Chromium: ${e.message}`;
82
+ extractSpinner.fail(errorMessage);
83
+ throw new Error(errorMessage);
84
+ }
85
+ extractSpinner.succeed();
86
+ return binaryPath;
87
+ }
88
+
89
+ module.exports = {
90
+ CHROMIUM_VERSION,
91
+ downloadAndInstallChromium,
92
+ };
package/cli.js CHANGED
@@ -11,6 +11,7 @@ const { onExit, ignoreFailingTestsInExitCode } = require('./cli/onExit');
11
11
  const testRunner = require('./runner');
12
12
  const prepareRunner = require('./commons/prepareRunner');
13
13
  const { CLI_MODE } = require('./commons/constants');
14
+ const { updateRemoteRunFailure } = require('./commons/testimServicesApi');
14
15
  const prepareRunnerAndTestimStartUtils = require('./commons/prepareRunnerAndTestimStartUtils');
15
16
 
16
17
  const {
@@ -21,7 +22,6 @@ const {
21
22
 
22
23
  const utils = require('./utils');
23
24
  const semver = require('semver');
24
- const Promise = require('bluebird');
25
25
  const perf = require('./commons/performance-logger');
26
26
  const agentMode = require('./cliAgentMode');
27
27
 
@@ -35,19 +35,19 @@ 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 [major, minor, patch] = process.version.split('.');
39
- // all good
40
- if (major !== 'v12') {
41
- return;
38
+ const majorVersion = Number(process.version.replace('v', '').split('.')[0]);
39
+ const dateHasPassed = new Date('2022-08-01T00:00:00.000Z') <= new Date();
40
+
41
+ if (majorVersion < 14 && dateHasPassed) {
42
+ throw new ArgError('Testim.io CLI supports Node.js 14 and above, please upgrade to a newer Node.js version');
42
43
  }
43
44
 
44
- const minorParsed = Number(minor);
45
- if (minorParsed < 20) {
46
- console.log('\x1b[33m%s\x1b[0m', 'Testim.io CLI will stop supporting Node.js < 12.20 on March 1st 2022, please upgrade to a newer Node.js version');
45
+ if (majorVersion < 14) {
46
+ console.log('\x1b[33m%s\x1b[0m', 'Testim.io CLI will stop supporting Node.js < 14 on August 1st 2022, please upgrade to a newer Node.js version');
47
47
  }
48
48
  }
49
49
 
50
- function main() {
50
+ async function main() {
51
51
  console.log('Starting Testim.io CLI');
52
52
  perf.log('Starting Testim.io CLI');
53
53
  require('./processHandler')(onExit);
@@ -57,91 +57,93 @@ function main() {
57
57
  process.exit(1);
58
58
  });
59
59
 
60
- return Promise.try(() => options.process())
61
- .log('in main, after options.process')
62
- .tap(options => {
63
- require('./commons/logger').setProxyUri(global.proxyUri);
64
- if (options.parallel && options.parallel > 5) {
65
- EventEmitter.defaultMaxListeners = options.parallel * 2;
66
- }
67
- require('./commons/logger').setProjectId(options.project);
68
- require('./commons/runnerFileCache').setEncryptKey(typeof options.token === 'string' ? options.token : 'anonymous_encrypt_key');
69
- })
70
- .then(async (options) => {
71
- if (options.initCodimMode) {
72
- const codimCli = require('./codim/codim-cli');
73
- return codimCli.init(options.initTestProject);
74
- }
75
- if (options.loginMode) {
76
- return undefined;
77
- }
78
- if (options.createPrefechedData) {
79
- const runnerFileCache = require('./commons/runnerFileCache');
80
- await runnerFileCache.clear();
81
- await prepareRunner.initializeUserWithAuth(options);
82
- await require('./commons/preloadTests').preloadTests(options);
83
- if (!options.playerRequirePath && options.mode !== CLI_MODE.EXTENSION) {
84
- await prepareRunnerAndTestimStartUtils.preparePlayer(options.playerLocation, options.canary);
85
- }
86
- const res = await runnerFileCache.waitForSave();
87
- if (res.success) {
88
- console.log(`created prefeched data at ${runnerFileCache.getCacheFileLocation()}`);
89
- } else {
90
- console.error('failed to create prefech data', res.error);
91
- }
92
- return undefined;
93
- }
94
-
95
- if (options.tunnelOnlyMode) {
96
- await testRunner.init(options);
97
- await require('./commons/testimTunnel').serveTunneling(options);
98
- return undefined;
99
- }
100
-
101
- if (agentMode.shouldStartAgentMode(options)) {
102
- return agentMode.runAgentMode(options);
60
+ try {
61
+ const processedOptions = await options.process();
62
+ perf.log('in main, after options.process');
63
+ require('./commons/logger').setProxyUri(global.proxyUri);
64
+ if (processedOptions.parallel && processedOptions.parallel > 5) {
65
+ EventEmitter.defaultMaxListeners = processedOptions.parallel * 2;
66
+ }
67
+ require('./commons/logger').setProjectId(processedOptions.project);
68
+ require('./commons/runnerFileCache').setEncryptKey(typeof processedOptions.token === 'string' ? processedOptions.token : 'anonymous_encrypt_key');
69
+
70
+ if (processedOptions.initCodimMode) {
71
+ const codimCli = require('./codim/codim-cli');
72
+ return codimCli.init(processedOptions.initTestProject);
73
+ }
74
+ if (processedOptions.loginMode) {
75
+ return undefined;
76
+ }
77
+ if (processedOptions.createPrefechedData) {
78
+ const runnerFileCache = require('./commons/runnerFileCache');
79
+ await runnerFileCache.clear();
80
+ await prepareRunner.initializeUserWithAuth(processedOptions);
81
+ await require('./commons/preloadTests').preloadTests(processedOptions);
82
+ if (!processedOptions.playerRequirePath && processedOptions.mode !== CLI_MODE.EXTENSION) {
83
+ await prepareRunnerAndTestimStartUtils.preparePlayer(processedOptions.playerLocation, processedOptions.canary);
103
84
  }
104
-
105
- if (options.saveRCALocally) {
106
- const { port } = await require('./services/localRCASaver').initServer(options);
107
- options.localRCASaver = `http://localhost:${port}`;
108
- }
109
-
110
- if (options.exitCodeIgnoreFailingTests) {
111
- ignoreFailingTestsInExitCode();
85
+ const res = await runnerFileCache.waitForSave();
86
+ if (res.success) {
87
+ console.log(`created prefeched data at ${runnerFileCache.getCacheFileLocation()}`);
88
+ } else {
89
+ console.error('failed to create prefech data', res.error);
112
90
  }
113
-
114
- perf.log('right before testRunner.init/prepareRunner.prepare');
115
- return Promise.all([
116
- testRunner.init(options),
117
- prepareRunner.prepare(options),
118
- ])
119
- .log('right after testRunner.init/prepareRunner.prepare')
120
- .then(([init, customExtensionLocalLocation]) => testRunner.run(options, customExtensionLocalLocation));
121
- })
122
- .catch(NoArgsError, () => {
91
+ return undefined;
92
+ }
93
+
94
+ if (processedOptions.tunnelOnlyMode) {
95
+ await testRunner.init(processedOptions);
96
+ await require('./commons/testimTunnel').serveTunneling(processedOptions);
97
+ return undefined;
98
+ }
99
+
100
+ if (agentMode.shouldStartAgentMode(processedOptions)) {
101
+ return agentMode.runAgentMode(processedOptions);
102
+ }
103
+
104
+ if (processedOptions.saveRCALocally) {
105
+ const { port } = await require('./services/localRCASaver').initServer(processedOptions);
106
+ processedOptions.localRCASaver = `http://localhost:${port}`;
107
+ }
108
+
109
+ if (processedOptions.exitCodeIgnoreFailingTests) {
110
+ ignoreFailingTestsInExitCode();
111
+ }
112
+
113
+ perf.log('right before testRunner.init/prepareRunner.prepare');
114
+ const [customExtensionLocalLocation] = await Promise.all([
115
+ prepareRunner.prepare(processedOptions),
116
+ testRunner.init(processedOptions),
117
+ ]);
118
+ perf.log('right after testRunner.init/prepareRunner.prepare');
119
+ return await testRunner.run(processedOptions, customExtensionLocalLocation);
120
+ } catch (err) {
121
+ if (err instanceof NoArgsError) {
123
122
  // display help by default
124
- })
125
- .catch(ArgError, err => {
123
+ return undefined;
124
+ }
125
+ const argsForRemoteRunFailure = utils.getArgsOnRemoteRunFailure();
126
+ if (argsForRemoteRunFailure) {
127
+ await updateRemoteRunFailure({ ...argsForRemoteRunFailure, error: err.message }).catch(() => { /* */ });
128
+ }
129
+ if (err instanceof ArgError) {
126
130
  console.log('Argument Error:', err.message);
127
131
  return err;
128
- })
129
- .catch(SeleniumError, err => {
132
+ }
133
+ if (err instanceof SeleniumError) {
130
134
  console.log('Selenium Error:', err.message);
131
135
  return err;
132
- })
133
- .catch(err => {
134
- console.log('Error:', err.message);
135
- logger.error('runner ended with unexpected error', { err });
136
- return err;
137
- })
138
- .then(result => {
139
- if (Array.isArray(result) && result.length === 0) {
140
- console.log('No tests ran');
141
- }
142
- onExit(result);
143
- });
136
+ }
137
+ console.log('Error:', err.message);
138
+ logger.error('runner ended with unexpected error', { err });
139
+ return err;
140
+ }
144
141
  }
145
142
 
146
- main();
143
+ main().then(result => {
144
+ if (Array.isArray(result) && result.length === 0) {
145
+ console.log('No tests ran');
146
+ }
147
+ onExit(result);
148
+ });
147
149
 
package/cliAgentMode.js CHANGED
@@ -4,26 +4,23 @@
4
4
  'use strict';
5
5
 
6
6
  const path = require('path');
7
- const os = require('os');
8
7
  const fs = require('fs-extra');
9
8
  const ms = require('ms');
10
9
  const WebSocket = require('ws');
11
10
  const Bluebird = require('bluebird');
12
11
  const ChromeLauncher = require('chrome-launcher');
13
- const httpRequest = require('./commons/httpRequest');
14
12
  const config = require('./commons/config');
15
13
  const { ArgError } = require('./errors');
16
14
  const lazyRequire = require('./commons/lazyRequire');
17
15
  const prepareUtils = require('./commons/prepareRunnerAndTestimStartUtils');
18
- const { unzipFile } = require('./utils');
19
- const { downloadAndSave } = require('./utils');
16
+ const { downloadAndSave, unzipFile, getCdpAddressForHost, TESTIM_BROWSER_DIR } = require('./utils');
20
17
  const ora = require('ora');
18
+ const { downloadAndInstallChromium, CHROMIUM_VERSION } = require('./chromiumInstaller');
19
+
21
20
 
22
21
  const LOG_LEVEL = config.WEBDRIVER_DEBUG ? 'verbose' : 'silent';
23
22
  const EXTENSION_CACHE_TIME = ms('1h');
24
- const HOMEDIR = os.homedir();
25
- const TESTIM_BROWSER_PROFILES_CONTAINER = path.join(HOMEDIR, '.testim-browser-profile');
26
- const USER_DATA_DIR = path.join(TESTIM_BROWSER_PROFILES_CONTAINER, 'profile');
23
+ const USER_DATA_DIR = path.join(TESTIM_BROWSER_DIR, 'profile');
27
24
 
28
25
  // https://github.com/bayandin/chromedriver/blob/5013f2124888c50fff15dc2ff8287288f780b046/chrome_launcher.cc#L105
29
26
  const CHOMEDRIVER_DEVTOOLS_ACTIVE_PORT_FILENAME = 'DevToolsActivePort';
@@ -111,84 +108,37 @@ function getStartedWithStart() {
111
108
  return startedWithStart;
112
109
  }
113
110
 
114
- function getCurrentPlatform() {
115
- const osType = os.type().toLowerCase();
116
- if (osType === 'darwin') {
117
- return os.arch() === 'arm' ? 'mac_arm' : 'mac';
118
- }
119
- if (osType === 'windows_nt') {
120
- return os.arch() === 'x64' ? 'win64' : 'win32';
111
+ function isPidRunning(pid) {
112
+ try {
113
+ return process.kill(pid, 0);
114
+ } catch {
115
+ return false;
121
116
  }
122
- return 'linux';
123
117
  }
124
- async function downloadAndInstallChrome(platform, revision, saveLocation) {
125
- // Inspired by puppeteer's implementation https://github.com/puppeteer/puppeteer/blob/main/src/node/BrowserFetcher.ts#L45
126
- // example download url: https://storage.googleapis.com/chromium-browser-snapshots/Mac/1000968/chrome-mac.zip
127
- const storageBaseUrl = 'https://storage.googleapis.com/chromium-browser-snapshots';
128
- const platformFolder = {
129
- linux: 'Linux_x64',
130
- mac: 'Mac',
131
- mac_arm: 'Mac_Arm',
132
- win32: 'Win',
133
- win64: 'Win_x64',
134
- };
135
- if (!(platform in platformFolder)) {
136
- throw new ArgError(`Unsupported platform: ${platform}`);
137
- }
138
- // Windows archive name changed at r591479.
139
- const winArchiveName = parseInt(revision, 10) > 591479 ? 'chrome-win' : 'chrome-win32';
140
- const platformArchiveName = {
141
- linux: 'chrome-linux',
142
- mac: 'chrome-mac',
143
- mac_arm: 'chrome-mac',
144
- win32: winArchiveName,
145
- win64: winArchiveName,
146
- };
147
- const binaryPaths = {
148
- linux: 'chrome',
149
- mac: 'Chromium.app/Contents/MacOS/Chromium',
150
- mac_arm: 'Chromium.app/Contents/MacOS/Chromium',
151
- win32: 'chrome.exe',
152
- win64: 'chrome.exe',
118
+
119
+ async function startFixedVersionChromium(options, extensionBase64, downloadedExtensionPathUnzipped) {
120
+ const CHROMIUM_PROCESS_INFO_FILE = path.join(TESTIM_BROWSER_DIR, `chrome-${CHROMIUM_VERSION}-process`);
121
+ const CHECK_CHROMIUM_RUNNING_INTERVAL = 3000;
122
+
123
+ const onBrowserClosed = () => {
124
+ fs.removeSync(CHROMIUM_PROCESS_INFO_FILE);
125
+ // eslint-disable-next-line no-console
126
+ console.log('\n\nBrowser session ended');
127
+ process.exit(0);
153
128
  };
154
- const downloadUrl = `${storageBaseUrl}/${platformFolder[platform]}/${revision}/${platformArchiveName[platform]}.zip`;
155
- const downloadArchivePath = path.join(saveLocation, platformArchiveName[platform]);
156
- const downloadedZipFile = `${downloadArchivePath}.zip`;
157
- const binaryPath = path.join(downloadArchivePath, binaryPaths[platform]);
158
129
 
159
- if (await fs.pathExists(binaryPath)) {
160
- return binaryPath;
161
- }
162
- if (!(await fs.pathExists(downloadedZipFile))) {
163
- const downloadSpinner = ora('Downloading Chromium').start();
164
- try {
165
- await fs.mkdirp(saveLocation);
166
- await downloadAndSave(downloadUrl, downloadedZipFile);
167
- // todo - We can add a failover here if the download fails if we host the file too
168
- } catch (e) {
169
- const errorMessage = `Failed to download Chromium: ${e.message}`;
170
- downloadSpinner.fail(errorMessage);
171
- throw new Error(errorMessage);
130
+
131
+ if (fs.existsSync(CHROMIUM_PROCESS_INFO_FILE)) {
132
+ const processInfo = fs.readJSONSync(CHROMIUM_PROCESS_INFO_FILE);
133
+ if (isPidRunning(processInfo.pid)) { // if a previous instance of our browser is still running, use it and exit if it does
134
+ const monitorPidForExit = () => (isPidRunning(processInfo.pid) ? setTimeout(monitorPidForExit, CHECK_CHROMIUM_RUNNING_INTERVAL) : onBrowserClosed());
135
+ monitorPidForExit();
136
+ return {
137
+ webdriverApi: processInfo,
138
+ };
172
139
  }
173
- downloadSpinner.succeed();
174
140
  }
175
- const extractSpinner = ora('Extracting Chromium').start();
176
- try {
177
- await unzipFile(downloadedZipFile, saveLocation);
178
- } catch (e) {
179
- const errorMessage = `Failed to extract Chromium: ${e.message}`;
180
- extractSpinner.fail(errorMessage);
181
- throw new Error(errorMessage);
182
- }
183
- extractSpinner.succeed();
184
- return binaryPath;
185
- }
186
-
187
- async function startFixedVersionBrowser(options, extensionBase64, downloadedExtensionPathUnzipped) {
188
- const CHROME_VERSION = '1000968';
189
- const DOWNLOAD_CHROME_FOLDER = path.join(TESTIM_BROWSER_PROFILES_CONTAINER, `chrome-${CHROME_VERSION}`);
190
-
191
- const chromeBinary = await downloadAndInstallChrome(getCurrentPlatform(), CHROME_VERSION, DOWNLOAD_CHROME_FOLDER);
141
+ const chromeBinary = await downloadAndInstallChromium();
192
142
 
193
143
  if (!(await fs.pathExists(USER_DATA_DIR))) {
194
144
  await fs.mkdirp(USER_DATA_DIR);
@@ -210,12 +160,12 @@ async function startFixedVersionBrowser(options, extensionBase64, downloadedExte
210
160
  };
211
161
  const appUrl = `${options.extensionPath ? 'http://localhost:3000/app/' : 'https://app.testim.io'}?startMode=true`;
212
162
  const chrome = await ChromeLauncher.launch({ chromeFlags, startingUrl: appUrl, ignoreDefaultFlags: true, userDataDir: USER_DATA_DIR, chromePath: chromeBinary, envVars });
213
- const onBrowserClosed = () => process.exit(0);
214
-
163
+ const processInfo = { port: chrome.port, pid: chrome.pid, cdpUrl: await getCdpAddressForHost(`localhost:${chrome.port}`) };
164
+ fs.writeJSONSync(CHROMIUM_PROCESS_INFO_FILE, processInfo);
215
165
  chrome.process.once('exit', onBrowserClosed);
216
166
  chrome.process.once('close', onBrowserClosed);
217
167
  return {
218
- webdriverApi: chrome,
168
+ webdriverApi: processInfo,
219
169
  };
220
170
  }
221
171
 
@@ -226,8 +176,8 @@ async function startTestimStandaloneBrowser(options) {
226
176
  const fullExtensionUrl = `${config.EDGE_URL}/extension/testim-full-master.zip`;
227
177
  const extensionFilename = path.basename(fullExtensionUrl);
228
178
 
229
- const downloadedExtensionPath = path.join(TESTIM_BROWSER_PROFILES_CONTAINER, extensionFilename);
230
- const downloadedExtensionPathUnzipped = path.join(TESTIM_BROWSER_PROFILES_CONTAINER, `${extensionFilename}__unzipped__`);
179
+ const downloadedExtensionPath = path.join(TESTIM_BROWSER_DIR, extensionFilename);
180
+ const downloadedExtensionPathUnzipped = path.join(TESTIM_BROWSER_DIR, `${extensionFilename}__unzipped__`);
231
181
 
232
182
  let shouldDownloadExtension = !(options.ext || options.extensionPath);
233
183
 
@@ -236,7 +186,7 @@ async function startTestimStandaloneBrowser(options) {
236
186
  const stat = await fs.stat(downloadedExtensionPath);
237
187
  shouldDownloadExtension = (Date.now() - EXTENSION_CACHE_TIME > stat.mtimeMs);
238
188
  }
239
- await fs.mkdirp(TESTIM_BROWSER_PROFILES_CONTAINER);
189
+ await fs.mkdirp(TESTIM_BROWSER_DIR);
240
190
 
241
191
  if (shouldDownloadExtension) {
242
192
  const spinner = ora('Downloading Testim Editor').start();
@@ -268,7 +218,7 @@ async function startTestimStandaloneBrowser(options) {
268
218
 
269
219
  const extensionBase64 = options.extensionPath ? null : (await fs.readFile(options.ext || downloadedExtensionPath)).toString('base64');
270
220
  if (options.downloadBrowser) {
271
- return await startFixedVersionBrowser(options, extensionBase64, downloadedExtensionPathUnzipped);
221
+ return await startFixedVersionChromium(options, extensionBase64, downloadedExtensionPathUnzipped);
272
222
  }
273
223
  await prepareUtils.prepareChromeDriver(
274
224
  { projectId: options.project },
@@ -301,9 +251,8 @@ async function startTestimStandaloneBrowser(options) {
301
251
  // save the initial URL we navigated to so we don't consider it the AuT
302
252
  webdriverApi.initialUrl = appUrl;
303
253
  try {
304
- //TODO(Benji) do we want this to be exactly getCdpAddress or should this fail less gracefully indicating the agent did not start correctly?
305
- const debuggerAddress = await httpRequest.get(`http://${webdriverInitResponse.value['goog:chromeOptions'].debuggerAddress}/json/version`);
306
- webdriverApi.cdpUrl = debuggerAddress.webSocketDebuggerUrl;
254
+ //TODO(Benji) do we want this to be exactly getCdpAddressForHost or should this fail less gracefully indicating the agent did not start correctly?
255
+ webdriverApi.cdpUrl = await getCdpAddressForHost(webdriverInitResponse.value['goog:chromeOptions'].debuggerAddress);
307
256
  } catch (e) {
308
257
  // ignore error
309
258
  }
@@ -392,31 +341,7 @@ async function readAndValidateChromedriverDevToolsActivePortFile() {
392
341
  throw new Error('invalid devtools browser url');
393
342
  }
394
343
 
395
- let webSocketDebuggerUrl;
396
- try {
397
- /**
398
- example response
399
-
400
- {
401
- "Browser": "Chrome/81.0.4044.138",
402
- "Protocol-Version": "1.3",
403
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
404
- "V8-Version": "8.1.307.32",
405
- "WebKit-Version": "537.36 (@8c6c7ba89cc9453625af54f11fd83179e23450fa)",
406
- "webSocketDebuggerUrl": "ws://localhost:58938/devtools/browser/d4290379-ec08-4d03-a41a-ab9d9d4c36ac"
407
- }
408
- */
409
- ({ webSocketDebuggerUrl } = await httpRequest.get(
410
- `http://localhost:${port}/json/version`,
411
- undefined,
412
- undefined,
413
- // short timeout, as we try to connect to localhost
414
- 100
415
- ));
416
- } catch (e) {
417
- throw new Error('unable to connect to devtools http server');
418
- }
419
-
344
+ const webSocketDebuggerUrl = await getCdpAddressForHost(`localhost:${port}`, 500);
420
345
  // invariant check
421
346
  if (!webSocketDebuggerUrl.endsWith(browserCDPURLLine)) {
422
347
  throw new Error('invariant webSocketDebuggerUrl miss match');
@@ -1,10 +1,11 @@
1
1
  /* eslint-disable no-console */
2
2
 
3
3
  const fkill = require('fkill');
4
+ const pRetry = require('p-retry');
4
5
 
5
6
  const httpRequest = require('./httpRequest');
6
7
  const npmWrapper = require('./npmWrapper');
7
- const { getCliLocation, runWithRetries } = require('../utils');
8
+ const { getCliLocation } = require('../utils');
8
9
  const { requireWithFallback } = require('./requireWithFallback');
9
10
 
10
11
  const PACKAGE_NAME = 'chromedriver';
@@ -40,7 +41,7 @@ const start = async () => {
40
41
 
41
42
  const isReady = async ({ chromeBinaryLocation }) => {
42
43
  // 100 tries, every 30ms
43
- await runWithRetries(async () => {
44
+ await pRetry(async () => {
44
45
  const statusResponse = await httpRequest.get(`${DRIVER_BASE_URL}/status`);
45
46
  if (!statusResponse || !statusResponse.value || !statusResponse.value.ready) {
46
47
  throw new Error('status failed');
@@ -59,7 +60,7 @@ const isReady = async ({ chromeBinaryLocation }) => {
59
60
  throw new Error('create session failed');
60
61
  }
61
62
  await httpRequest.delete(`${DRIVER_BASE_URL}/session/${sessionResponse.sessionId}`);
62
- }, 100, 30);
63
+ }, { retries: 100, minTimeout: 30 });
63
64
  };
64
65
 
65
66
  module.exports = {
@@ -45,6 +45,7 @@ class FeatureFlagsService {
45
45
  warnOnBadNetwork: new Rox.Flag(false),
46
46
  overrideAzureStorageUrl: new Rox.Flag(),
47
47
  useJsInputCodeInSafari: new Rox.Flag(),
48
+ useJsInputCodeInFirefox: new Rox.Flag(),
48
49
  autoSaveDownloadFileFireFox: new Rox.Flag(true),
49
50
  safariSelectOptionDispatchEventOnSelectElement: new Rox.Flag(true),
50
51
  experimentalPreCodeCompilation: new Rox.Flag(true),
@@ -21,7 +21,8 @@ const testimAppDataFolder = getSessionPlayerFolder();
21
21
  stepParamBuilder: typeof import('../../../clickim/src/common/stepParamsBuilder').StepParamsBuilder;
22
22
  stepParamExpressionEvaluator: import('../../../clickim/src/common/stepParamExpressionEvaluator');
23
23
  manifestVersion: string | undefined;
24
- EyeSdkBuilder: typeof import('../../../clickim/src/background/eyeSdkBuilder').EyeSdkBuilder
24
+ EyeSdkBuilder: typeof import('../../../clickim/src/background/eyeSdkBuilder').EyeSdkBuilder;
25
+ sfdc: typeof import('sfdc-engine');
25
26
  }}
26
27
  */
27
28
  const sessionPlayer = require(require('path').join(testimAppDataFolder, 'sessionPlayer.js')); // eslint-disable-line import/no-dynamic-require
@@ -1,5 +1,5 @@
1
+ const pRetry = require('p-retry');
1
2
  const { makeCounters } = require('./httpRequestCounters');
2
- const { runWithRetries } = require('../utils');
3
3
  const { rejects, doesNotReject, strictEqual } = require('assert');
4
4
 
5
5
  describe('the http request counters', () => {
@@ -8,29 +8,29 @@ describe('the http request counters', () => {
8
8
  wrapWithMonitoring = makeCounters();
9
9
  });
10
10
 
11
- it('Marks an always failing network as unhealthy', async () => {
12
- const fn = runWithRetries(wrapWithMonitoring(() => { throw new Error('bad network'); }), 30, 1);
11
+ it('marks an always failing network as unhealthy', async () => {
12
+ const fn = pRetry(wrapWithMonitoring(() => { throw new Error('bad network'); }), { retries: 30, minTimeout: 0, maxTimeout: 0 });
13
13
  await rejects(fn);
14
14
  strictEqual(await wrapWithMonitoring.isNetworkHealthy(), false);
15
15
  });
16
16
 
17
- it('Marks an unstable network as unhealthy', async () => {
18
- const fn = runWithRetries(wrapWithMonitoring(() => { throw new Error('bad network'); }), 30, 1);
19
- const fn2 = runWithRetries(wrapWithMonitoring(() => 'hello'), 20, 1);
17
+ it('marks an unstable network as unhealthy', async () => {
18
+ const fn = pRetry(wrapWithMonitoring(() => { throw new Error('bad network'); }), { retries: 30, minTimeout: 0, maxTimeout: 0 });
19
+ const fn2 = pRetry(wrapWithMonitoring(() => 'hello'), { retries: 20, minTimeout: 0, maxTimeout: 0 });
20
20
  await rejects(fn);
21
21
  await doesNotReject(fn2);
22
22
  strictEqual(await wrapWithMonitoring.isNetworkHealthy(), false);
23
23
  });
24
24
 
25
- it('Marks a trivial amount of failed requests as healthy', async () => {
26
- const fn = runWithRetries(wrapWithMonitoring(() => { throw new Error('bad network'); }), 30, 1);
25
+ it('marks a trivial amount of failed requests as healthy', async () => {
26
+ const fn = pRetry(wrapWithMonitoring(() => { throw new Error('bad network'); }), { retries: 30, minTimeout: 0, maxTimeout: 0 });
27
27
  await rejects(fn);
28
28
  const fn2 = wrapWithMonitoring(() => 'hello');
29
29
  await Promise.all(Array(290).fill().map(fn2));
30
30
  strictEqual(await wrapWithMonitoring.isNetworkHealthy(), true);
31
31
  });
32
32
 
33
- it('Marks a healthy network as healthy', async () => {
33
+ it('marks a healthy network as healthy', async () => {
34
34
  const fn2 = wrapWithMonitoring(() => 'hello');
35
35
  await Promise.all(Array(200).fill().map(fn2));
36
36
  strictEqual(await wrapWithMonitoring.isNetworkHealthy(), true);