@testim/testim-cli 3.245.0 → 3.248.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.
@@ -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');
@@ -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
@@ -10,6 +10,7 @@ const Ajv = require('ajv');
10
10
  const prepareRunnerAndTestimStartUtils = require('./prepareRunnerAndTestimStartUtils');
11
11
  const mockNetworkRuleFileSchema = require('./mockNetworkRuleFileSchema.json');
12
12
  const { initializeUserWithAuth } = require('./initializeUserWithAuth');
13
+ const { downloadAndInstallChromium } = require('../chromiumInstaller');
13
14
 
14
15
  const MAX_RULE_FILE_SIZE_IN_MB = 1;
15
16
  const PREPARE_MOCK_NETWORK_ERROR_PREFIX = 'JSON file supplied to --mock-network-pattern';
@@ -30,6 +31,7 @@ async function prepare(options) {
30
31
  const hasNoGrid = !options.host && !options.gridId && !options.grid && (!options.testPlan || options.testPlan.length === 0);
31
32
  const isTdkRun = options.files.length !== 0;
32
33
  if ((hasNoGrid && isTdkRun) || options.useLocalChromeDriver) {
34
+ options.chromeBinaryLocation = options.downloadBrowser ? await downloadAndInstallChromium() : options.chromeBinaryLocation;
33
35
  chromedriverPromise = prepareRunnerAndTestimStartUtils.prepareChromeDriver(
34
36
  { projectId: options.project, userId: options.user },
35
37
  { chromeBinaryLocation: options.chromeBinaryLocation },
@@ -536,13 +536,11 @@ function buildSeleniumOptions(browserOptions, testName, testRunConfig, gridInfo,
536
536
  );
537
537
 
538
538
  let predefinedTestimExtension = null;
539
- if (!browserOptions.ext && _.endsWith(gridInfo.host, '.testim.io') && !browserOptions.canary && browserOptions.mode === CLI_MODE.EXTENSION) {
539
+ if (!browserOptions.ext && !browserOptions.extensionPath && _.endsWith(gridInfo.host, '.testim.io') && !browserOptions.canary && browserOptions.mode === CLI_MODE.EXTENSION) {
540
540
  if (browser === 'chrome') {
541
541
  predefinedTestimExtension = '/opt/testim-headless';
542
542
  } else if (browser === 'edge-chromium') {
543
543
  predefinedTestimExtension = 'C:/selenium/testim-headless';
544
- } else if (browser === 'firefox') {
545
- predefinedTestimExtension = '/opt/testim-firefox-profile';
546
544
  }
547
545
  }
548
546
 
@@ -476,6 +476,10 @@ function deleteCloudflareTunnel(companyId, tunnelId) {
476
476
  }
477
477
  }
478
478
 
479
+ function updateRemoteRunFailure(body) {
480
+ return httpRequest.post({ url: `${config.SERVICES_HOST}/result/remoteRunFailure`, body });
481
+ }
482
+
479
483
  module.exports = {
480
484
  getS3Artifact,
481
485
  getTestPlan,
@@ -511,4 +515,5 @@ module.exports = {
511
515
  getCloudflareTunnel,
512
516
  forceUpdateCloudflareTunnelRoutes,
513
517
  deleteCloudflareTunnel,
518
+ updateRemoteRunFailure,
514
519
  };