@mablhq/mabl-cli 1.45.1 → 1.47.12

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 (53) hide show
  1. package/api/basicApiClient.js +6 -0
  2. package/api/featureSet.js +0 -4
  3. package/api/mablApiClient.js +8 -0
  4. package/browserEngines/browserEngine.js +40 -0
  5. package/browserEngines/browserEngines.js +14 -0
  6. package/browserEngines/chromiumBrowserEngine.js +176 -0
  7. package/browserEngines/firefoxBrowserEngine.js +84 -0
  8. package/browserEngines/unsupportedBrowserEngine.js +27 -0
  9. package/browserLauncher/frameBase.js +5 -1
  10. package/browserLauncher/pageEvent.js +1 -0
  11. package/browserLauncher/playwrightBrowserLauncher/browserDelegate.js +2 -0
  12. package/browserLauncher/playwrightBrowserLauncher/chromium/chromiumBrowserDelegate.js +55 -0
  13. package/browserLauncher/playwrightBrowserLauncher/chromium/chromiumElementHandleDelegate.js +62 -0
  14. package/browserLauncher/playwrightBrowserLauncher/chromium/chromiumFrameDelegate.js +12 -0
  15. package/browserLauncher/playwrightBrowserLauncher/chromium/chromiumPageDelegate.js +117 -0
  16. package/browserLauncher/playwrightBrowserLauncher/elementHandleDelegate.js +2 -0
  17. package/browserLauncher/playwrightBrowserLauncher/firefox/firefoxBrowserDelegate.js +51 -0
  18. package/browserLauncher/playwrightBrowserLauncher/firefox/firefoxElementHandleDelegate.js +61 -0
  19. package/browserLauncher/playwrightBrowserLauncher/firefox/firefoxFrameDelegate.js +23 -0
  20. package/browserLauncher/playwrightBrowserLauncher/firefox/firefoxPageDelegate.js +83 -0
  21. package/browserLauncher/playwrightBrowserLauncher/frameDelegate.js +2 -0
  22. package/browserLauncher/playwrightBrowserLauncher/pageDelegate.js +2 -0
  23. package/browserLauncher/playwrightBrowserLauncher/playwrightBrowser.js +41 -35
  24. package/browserLauncher/playwrightBrowserLauncher/playwrightBrowserLauncher.js +28 -3
  25. package/browserLauncher/playwrightBrowserLauncher/playwrightDom.js +32 -63
  26. package/browserLauncher/playwrightBrowserLauncher/playwrightFrame.js +35 -11
  27. package/browserLauncher/playwrightBrowserLauncher/playwrightHttpRequest.js +1 -25
  28. package/browserLauncher/playwrightBrowserLauncher/playwrightPage.js +28 -50
  29. package/commands/constants.js +6 -1
  30. package/commands/tests/testsUtil.js +14 -153
  31. package/commands/tests/tests_cmds/import.js +2 -2
  32. package/commands/tests/tests_cmds/run.js +35 -10
  33. package/commands/workspaces/workspace_cmds/copy.js +1 -2
  34. package/core/messaging/messaging.js +15 -1
  35. package/domUtil/index.js +1 -1
  36. package/execution/index.js +1 -1
  37. package/functions/apiTest/utils.js +47 -0
  38. package/functions/types.js +2 -0
  39. package/functions/utils.js +12 -0
  40. package/http/requestInterceptor.js +23 -8
  41. package/mablApi/index.js +1 -1
  42. package/mablscript/types/VariableNamespace.js +3 -3
  43. package/mablscriptFind/index.js +1 -1
  44. package/package.json +4 -3
  45. package/popupDismissal/index.js +7 -20
  46. package/resources/mablFind.js +1 -1
  47. package/resources/popupDismissal.js +1 -1
  48. package/util/actionabilityUtil.js +4 -4
  49. package/util/clickUtil.js +2 -2
  50. package/util/fileUploadUtil.js +1 -1
  51. package/util/jestUtil.js +21 -0
  52. package/util/pureUtil.js +8 -1
  53. package/mablscript/types/VariableDataType.js +0 -28
@@ -26,8 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.toBasicHttpAuthenticationCredentials = exports.generateChromiumPreferencesFile = exports.logTestInfoIfPresent = exports.milliSecondsToSeconds = exports.calculateTotalTimeSeconds = exports.extractTestRunConfig = exports.pullDownTestRunConfig = exports.validateRunCommandWithLabels = exports.validateRunEditCommand = exports.cleanupTestResources = exports.sleep = exports.editTheTest = exports.runTheTest = exports.prepareTrainerForSplitPlayback = exports.cleanUpInitialPages = exports.getExtensionBackgroundPageWithCliTool = exports.createBrowserForExecutionEngine = exports.addExecutionEngineLaunchArgs = exports.createBrowser = exports.prepareChromePreferencesDirectory = exports.findChrome = exports.searchForChrome = exports.getFinalUrl = exports.getTempChromePrefDirectory = void 0;
30
- const async_retry_1 = __importDefault(require("async-retry"));
29
+ exports.toBasicHttpAuthenticationCredentials = exports.logTestInfoIfPresent = exports.milliSecondsToSeconds = exports.calculateTotalTimeSeconds = exports.extractTestRunConfig = exports.pullDownTestRunConfig = exports.validateRunCommandWithLabels = exports.validateRunEditCommand = exports.cleanupTestResources = exports.sleep = exports.editTheTest = exports.runTheTest = exports.prepareTrainerForSplitPlayback = exports.cleanUpInitialPages = exports.getExtensionBackgroundPageWithCliTool = exports.createBrowserForExecutionEngine = exports.createBrowser = exports.getFinalUrl = void 0;
31
30
  const cli_table3_1 = __importDefault(require("cli-table3"));
32
31
  const fs = __importStar(require("fs-extra"));
33
32
  const os = __importStar(require("os"));
@@ -36,45 +35,14 @@ const browserLauncher_1 = require("../../browserLauncher/browserLauncher");
36
35
  const trainingSessionActions_1 = require("../../core/messaging/actions/trainingSessionActions");
37
36
  const logLineMessaging_1 = require("../../core/messaging/logLineMessaging");
38
37
  const messaging_1 = require("../../core/messaging/messaging");
39
- const env_1 = require("../../env/env");
40
38
  const cliConfigProvider_1 = require("../../providers/cliConfigProvider");
41
39
  const loggingProvider_1 = require("../../providers/logging/loggingProvider");
42
40
  const fileUploadUtil_1 = require("../../util/fileUploadUtil");
43
41
  const logUtils_1 = require("../../util/logUtils");
44
- const resourceUtil_1 = require("../../util/resourceUtil");
45
- const configKeys_1 = require("../config/config_cmds/configKeys");
46
42
  const constants_1 = require("../constants");
47
- const mobileEmulationUtil_1 = require("./mobileEmulationUtil");
48
43
  const trainerUtil_1 = require("./tests_cmds/trainerUtil");
49
44
  const chalk = require('chalk');
50
- const chromeFinder = require('chrome-launcher/dist/chrome-finder');
51
- const launchUtils = require('chrome-launcher/dist/utils');
52
- const tmp = require('tmp-promise');
53
- const baseExecutionEngineLaunchArgs = [
54
- '--unlimited-storage',
55
- '--disable-dev-shm-usage',
56
- '--enable-print-preview-register-promos',
57
- '--kiosk-printing',
58
- '--disable-infobars',
59
- '--enable-logging',
60
- '--v=0',
61
- '--enable-features=NetworkService,NetworkServiceInProcess',
62
- '--disable-features=site-per-process',
63
- '--disable-component-update',
64
- ];
65
- const ExecutionEngineFakeAudioFilePath = '/opt/media/mabl_test_audio.wav';
66
- const ExecutionEngineFakeVideoFilePath = '/opt/media/mabl_test_pattern.y4m';
67
45
  let RUNNING_TEST = false;
68
- function getTempChromePrefDirectory() {
69
- const tempDir = tmp.dirSync({
70
- mode: 0o777,
71
- prefix: `${env_1.CONF_FILE_PROJECT_NAME}-`,
72
- });
73
- const preferencesPath = path.normalize(`${tempDir.name}/chromePreferences`);
74
- fs.ensureDirSync(preferencesPath, 0o777);
75
- return preferencesPath;
76
- }
77
- exports.getTempChromePrefDirectory = getTempChromePrefDirectory;
78
46
  function getFinalUrl(test, parsedUrl) {
79
47
  const finalUrl = parsedUrl || test.url;
80
48
  if (!finalUrl) {
@@ -83,29 +51,8 @@ function getFinalUrl(test, parsedUrl) {
83
51
  return finalUrl;
84
52
  }
85
53
  exports.getFinalUrl = getFinalUrl;
86
- function searchForChrome() {
87
- const chromes = chromeFinder[launchUtils.getPlatform()]();
88
- return chromes.filter((chrome) => {
89
- const lowCase = chrome.toLowerCase();
90
- return !(lowCase.includes('canary') ||
91
- lowCase.includes('dev') ||
92
- lowCase.includes('beta'));
93
- })[0];
94
- }
95
- exports.searchForChrome = searchForChrome;
96
- async function findChrome() {
97
- const existingLocation = await cliConfigProvider_1.CliConfigProvider.getConfigProperty(configKeys_1.configKeys.browserPath);
98
- if (typeof existingLocation === 'string' &&
99
- existingLocation &&
100
- fs.existsSync(existingLocation)) {
101
- return existingLocation;
102
- }
103
- const chromePath = searchForChrome();
104
- await cliConfigProvider_1.CliConfigProvider.setConfigProperty(configKeys_1.configKeys.browserPath, chromePath);
105
- return chromePath;
106
- }
107
- exports.findChrome = findChrome;
108
- async function launchBrowserInstance(launchArgs, userDataDir, headless, credentials, options) {
54
+ async function launchBrowserInstance(engine, launchArgs, headless, credentials, options) {
55
+ const userDataDir = await engine.prepareBrowserPreferencesDirectory(options.windowPlacement);
109
56
  const optionsProxy = await maybeGetProxyOptions(options);
110
57
  if (optionsProxy) {
111
58
  options = {
@@ -161,20 +108,6 @@ function maybeLaunchBrowser(launchArgs, userDataDir, headless, credentials, opti
161
108
  credentials,
162
109
  });
163
110
  }
164
- async function prepareChromePreferencesDirectory(browserPreferences) {
165
- return (0, async_retry_1.default)(() => {
166
- const tempBrowserPreferencesDirectory = getTempChromePrefDirectory();
167
- fs.ensureDirSync(path.normalize(`${tempBrowserPreferencesDirectory}/Default`));
168
- const prefFilePath = path.normalize(`${tempBrowserPreferencesDirectory}/Default/Preferences`);
169
- fs.writeFileSync(prefFilePath, JSON.stringify(browserPreferences, null, 3));
170
- fs.chmodSync(path.normalize(`${tempBrowserPreferencesDirectory}/Default`), 0o777);
171
- fs.chmodSync(prefFilePath, 0o777);
172
- return tempBrowserPreferencesDirectory;
173
- }, {
174
- retries: 5,
175
- });
176
- }
177
- exports.prepareChromePreferencesDirectory = prepareChromePreferencesDirectory;
178
111
  function removeTempBrowserPreferencesDirectory(tempDirPath) {
179
112
  try {
180
113
  fs.removeSync(tempDirPath);
@@ -182,75 +115,28 @@ function removeTempBrowserPreferencesDirectory(tempDirPath) {
182
115
  catch (error) {
183
116
  }
184
117
  }
185
- async function createBrowser(browserWidth, browserHeight, headless, containerTesting, tempBrowserPreferencesDirectory, options) {
118
+ async function createBrowser(engine, browserWidth, browserHeight, headless, containerTesting, options) {
186
119
  var _a;
187
120
  const { credentials, disableIsolation, ignoreCertificateErrors, emulationConfig, enableExtensions, resourcesDirectoryOverride, } = options || {};
188
- const disableFeaturesFlags = [];
189
- const launchArgs = [];
190
- if (containerTesting) {
191
- launchArgs.push('--no-sandbox');
192
- }
193
- if (disableIsolation) {
194
- disableFeaturesFlags.push('IsolateOrigins');
195
- }
196
- disableFeaturesFlags.push('site-per-process');
197
- launchArgs.push(`--disable-features=${disableFeaturesFlags.join(',')}`);
198
- if (options === null || options === void 0 ? void 0 : options.autoOpenDevtoolsForTabs) {
199
- launchArgs.push('--auto-open-devtools-for-tabs');
200
- }
201
- const fakeMicrophoneMedia = (0, resourceUtil_1.findResource)('media/mabl_test_audio.wav', resourcesDirectoryOverride);
202
- const fakeWebcamMedia = (0, resourceUtil_1.findResource)('media/mabl_test_pattern.y4m', resourcesDirectoryOverride);
203
- const defaultDeviceDescriptor = addLaunchArgs(launchArgs, browserWidth, browserHeight, fakeMicrophoneMedia, fakeWebcamMedia, ignoreCertificateErrors, emulationConfig);
204
- let ignoreDefaultArgs;
205
- if (enableExtensions) {
206
- ignoreDefaultArgs = ['--disable-extensions'];
207
- }
208
- const maybeBrowser = await launchBrowserInstance(launchArgs, tempBrowserPreferencesDirectory, headless, credentials, {
121
+ const { commandLineArgs: launchArgs, ignoreCommandLineDefaultArgs: ignoreDefaultArgs, defaultDeviceDescriptor, engineSpecificOptions, } = engine.getBrowserLaunchOptions(containerTesting, options, browserWidth, browserHeight, ignoreCertificateErrors, emulationConfig, enableExtensions, disableIsolation, resourcesDirectoryOverride);
122
+ const maybeBrowser = await launchBrowserInstance(engine, launchArgs, headless, credentials, {
209
123
  ...options,
124
+ ...engineSpecificOptions,
210
125
  defaultDeviceDescriptor,
211
126
  ignoreDefaultArgs,
212
127
  userAgent: (_a = options === null || options === void 0 ? void 0 : options.userAgent) !== null && _a !== void 0 ? _a : emulationConfig === null || emulationConfig === void 0 ? void 0 : emulationConfig.device_config.user_agent,
213
128
  defaultUserAgent: options === null || options === void 0 ? void 0 : options.defaultUserAgent,
214
- enableScreencastModeForScreenshotTimeouts: false,
129
+ windowPlacement: options.windowPlacement,
215
130
  });
216
131
  if (!maybeBrowser) {
217
- throw new Error('Unable to start Chrome session');
132
+ throw new Error('Unable to start browser session');
218
133
  }
219
- maybeBrowser.on(browserLauncher_1.BrowserEvent.BrowserDestroyed, () => removeTempBrowserPreferencesDirectory(tempBrowserPreferencesDirectory));
134
+ maybeBrowser.on(browserLauncher_1.BrowserEvent.BrowserDestroyed, () => removeTempBrowserPreferencesDirectory(maybeBrowser.getBrowserPreferencesDirectory()));
220
135
  return maybeBrowser;
221
136
  }
222
137
  exports.createBrowser = createBrowser;
223
- function addLaunchArgs(launchArgs, browserWidth, browserHeight, fakeMicrophoneMediaPath, fakeWebcamMediaPath, ignoreCertificateErrors, emulationConfig) {
224
- var _a, _b;
225
- const defaultDeviceDescriptor = (0, mobileEmulationUtil_1.getDeviceDescriptorForEmulation)(emulationConfig);
226
- launchArgs.push(`--window-size=${(_a = defaultDeviceDescriptor === null || defaultDeviceDescriptor === void 0 ? void 0 : defaultDeviceDescriptor.width) !== null && _a !== void 0 ? _a : browserWidth},${(_b = defaultDeviceDescriptor === null || defaultDeviceDescriptor === void 0 ? void 0 : defaultDeviceDescriptor.height) !== null && _b !== void 0 ? _b : browserHeight}`);
227
- launchArgs.push('--use-fake-ui-for-media-stream');
228
- launchArgs.push('--use-fake-device-for-media-stream');
229
- launchArgs.push(`--use-file-for-fake-audio-capture=${fakeMicrophoneMediaPath}`);
230
- launchArgs.push(`--use-file-for-fake-video-capture=${fakeWebcamMediaPath}`);
231
- launchArgs.push('--disable-notifications');
232
- if (ignoreCertificateErrors) {
233
- launchArgs.push('--ignore-certificate-errors');
234
- }
235
- return defaultDeviceDescriptor;
236
- }
237
- function addExecutionEngineLaunchArgs(browserWidth, browserHeight, proxyInfo, emulationConfig) {
238
- const launchArgs = [...baseExecutionEngineLaunchArgs];
239
- if (proxyInfo.pacProxy) {
240
- launchArgs.push(`--proxy-pac-url=${proxyInfo.pacProxy}`);
241
- }
242
- else if (proxyInfo.socksProxy) {
243
- launchArgs.push(`--proxy-server=socks=${proxyInfo.socksProxy}`);
244
- }
245
- else {
246
- throw new Error('no proxy provided for cloud run');
247
- }
248
- const defaultDeviceDescriptor = addLaunchArgs(launchArgs, browserWidth, browserHeight, ExecutionEngineFakeAudioFilePath, ExecutionEngineFakeVideoFilePath, true, emulationConfig);
249
- loggingProvider_1.logger.debug(`launchArgs: ${JSON.stringify(launchArgs)}`);
250
- return { defaultDeviceDescriptor, launchArgs };
251
- }
252
- exports.addExecutionEngineLaunchArgs = addExecutionEngineLaunchArgs;
253
- async function createBrowserForExecutionEngine(browserWidth, browserHeight, headless, tempBrowserPreferencesDirectory, proxyInfo, options) {
138
+ async function createBrowserForExecutionEngine(engine, browserWidth, browserHeight, headless, proxyInfo, options) {
139
+ const tempBrowserPreferencesDirectory = await engine.prepareBrowserPreferencesDirectory();
254
140
  const { credentials, deviceEmulationConfig, userAgent } = options;
255
141
  if (deviceEmulationConfig && !userAgent) {
256
142
  options = {
@@ -258,13 +144,13 @@ async function createBrowserForExecutionEngine(browserWidth, browserHeight, head
258
144
  userAgent: deviceEmulationConfig.device_config.user_agent,
259
145
  };
260
146
  }
261
- const { launchArgs, defaultDeviceDescriptor } = addExecutionEngineLaunchArgs(browserWidth, browserHeight, proxyInfo, deviceEmulationConfig);
147
+ const { launchArgs, defaultDeviceDescriptor } = engine.getExecutionEngineBrowserLaunchOptions(browserWidth, browserHeight, proxyInfo, deviceEmulationConfig);
262
148
  if (defaultDeviceDescriptor) {
263
149
  options = { ...options, defaultDeviceDescriptor };
264
150
  }
265
151
  const maybeBrowser = await maybeLaunchBrowser(launchArgs, tempBrowserPreferencesDirectory, headless, credentials, options);
266
152
  if (!maybeBrowser) {
267
- throw new Error('Unable to start Chrome session');
153
+ throw new Error('Unable to start browser session');
268
154
  }
269
155
  return maybeBrowser;
270
156
  }
@@ -584,31 +470,6 @@ function logTestInfoIfPresent(description, infoArg) {
584
470
  }
585
471
  }
586
472
  exports.logTestInfoIfPresent = logTestInfoIfPresent;
587
- function generateChromiumPreferencesFile(windowPlacement) {
588
- const preferences = {
589
- download: {
590
- open_pdf_in_system_reader: true,
591
- prompt_for_download: false,
592
- },
593
- profile: {
594
- avatar_index: 5,
595
- name: 'mabl',
596
- },
597
- plugins: {
598
- always_open_pdf_externally: true,
599
- },
600
- };
601
- if (windowPlacement) {
602
- preferences.browser = {
603
- window_placement: {
604
- always_on_top: false,
605
- ...windowPlacement,
606
- },
607
- };
608
- }
609
- return preferences;
610
- }
611
- exports.generateChromiumPreferencesFile = generateChromiumPreferencesFile;
612
473
  function createDownloadDirectory() {
613
474
  return fs.mkdtempSync(path.join(os.tmpdir(), `mablTestRun-${Date.now()}-`));
614
475
  }
@@ -38,9 +38,9 @@ const RichPromise_1 = __importDefault(require("../../../util/RichPromise"));
38
38
  const pureUtil_1 = require("../../../util/pureUtil");
39
39
  const constants_1 = require("../../constants");
40
40
  const authenticationProvider_1 = require("../../../providers/authenticationProvider");
41
- const testsUtil_1 = require("../testsUtil");
42
41
  const asyncUtil_1 = require("../../../util/asyncUtil");
43
42
  const loggingProvider_1 = require("../../../providers/logging/loggingProvider");
43
+ const chromiumBrowserEngine_1 = require("../../../browserEngines/chromiumBrowserEngine");
44
44
  const { MablTestRunner, MablTestsRunner } = require('../../../execution');
45
45
  const DEFAULT_ASYNC_TIMEOUT_MILLIS = 120000;
46
46
  const CommandArgAuto = 'auto-save';
@@ -245,7 +245,7 @@ async function runTests(workspaceId, name, apiClient, importedTests) {
245
245
  var _a, _b;
246
246
  const flows = importedTests.map((steps) => stepsToFlow(workspaceId, steps));
247
247
  const firstUrl = (_a = flows.find((flow) => flow.url)) === null || _a === void 0 ? void 0 : _a.url;
248
- const browserPath = await (0, testsUtil_1.findChrome)();
248
+ const browserPath = await new chromiumBrowserEngine_1.ChromiumBrowserEngine().findBrowserExecutable();
249
249
  for (let flowIndex = 0; flowIndex < flows.length; flowIndex++) {
250
250
  loggingProvider_1.logger.info(`Running test ${flowIndex + 1} of ${flows.length}.`);
251
251
  const flow = flows[flowIndex];
@@ -9,6 +9,7 @@ const mablApi_1 = require("../../../mablApi");
9
9
  const reporter_1 = require("../../../reporters/reporter");
10
10
  const browserTypes_1 = require("../../browserTypes");
11
11
  const execution_1 = require("../../../execution");
12
+ const defaultEnv_1 = require("../../../env/defaultEnv");
12
13
  const chalk = require('chalk');
13
14
  const execution = require('../../../execution/index');
14
15
  exports.command = `run`;
@@ -161,25 +162,19 @@ exports.builder = (yargs) => {
161
162
  choices: Object.keys(mablApi_1.JourneyParameters.PageLoadWaitEnum).map((pageLoadWait) => pageLoadWait.toLowerCase()),
162
163
  })
163
164
  .option(constants_1.CommandArgBrowser, {
164
- describe: 'Target browser to execute the test against [chrome,edge]',
165
+ describe: `Target browser to execute the test against, must be one of ${constants_1.ValidBrowserTypesForLocalRuns}, defaults to ${constants_1.DefaultBrowserType}`,
165
166
  type: 'string',
166
167
  nargs: 1,
167
- choices: [browserTypes_1.BrowserType.Chrome, browserTypes_1.BrowserType.Edge],
168
+ choices: constants_1.ValidBrowserTypesForLocalRuns,
168
169
  hidden: true,
169
170
  })
170
171
  .check((argv) => {
171
172
  (0, testsUtil_1.validateRunCommandWithLabels)(argv[constants_1.CommandArgId], argv[constants_1.CommandArgLabelsInclude], argv[constants_1.CommandArgLabelsExclude], argv[constants_1.CommandArgTestRunId], argv[constants_1.CommandArgFromPlanId], true, argv[constants_1.CommandArgTestFile]);
172
- validateBrowserInput(argv[constants_1.CommandArgBrowser]);
173
173
  const httpHeaders = argv[constants_1.CommandArgHttpHeaders];
174
174
  (0, util_1.validateArrayInputs)(httpHeaders, 'HTTP headers must be SPACE delimited, e.g. "--http-headers "foo:bar" "baz:qux"');
175
175
  argv[constants_1.CommandArgHttpHeaders] = (0, util_1.validateValuePairInputs)('HTTP header', httpHeaders);
176
176
  return true;
177
177
  });
178
- function validateBrowserInput(browser) {
179
- if (browser && !['chrome', 'edge'].includes(browser)) {
180
- throw new Error('invalid browser type, valid browser types are [chrome,edge]');
181
- }
182
- }
183
178
  };
184
179
  const exitCodeOnError = 1;
185
180
  exports.handler = (0, util_1.failWrapper)(run, exitCodeOnError);
@@ -204,8 +199,14 @@ async function run(parsed) {
204
199
  case 'edge':
205
200
  browserType = browserTypes_1.BrowserType.Edge;
206
201
  break;
207
- default:
202
+ case 'firefox':
203
+ browserType = browserTypes_1.BrowserType.Firefox;
204
+ break;
205
+ case 'chrome':
208
206
  browserType = browserTypes_1.BrowserType.Chrome;
207
+ break;
208
+ default:
209
+ browserType = constants_1.DefaultBrowserType;
209
210
  }
210
211
  const testRunnerConfig = {
211
212
  _cliCreated: true,
@@ -269,9 +270,13 @@ async function run(parsed) {
269
270
  if (results.numFailedTests) {
270
271
  results.testResults.forEach((result) => {
271
272
  if (result.status === 'failed') {
272
- loggingProvider_1.logger.info(chalk.red(` - ${result.testName} (${(0, testsUtil_1.calculateTotalTimeSeconds)(result.startTime, result.endTime)}s)`));
273
+ loggingProvider_1.logger.info(chalk.red(` - ${result.testName} (${(0, testsUtil_1.calculateTotalTimeSeconds)(result.startTime, result.endTime)}s) - ${result.testId}`));
273
274
  }
274
275
  });
276
+ if (parsed.labels || parsed['from-plan-id'] || process.env.CI) {
277
+ loggingProvider_1.logger.info(`Rerun any failed test with the following command template:`);
278
+ loggingProvider_1.logger.info(`${chalk.magenta(generateRunCommandTemplate(parsed, results))}`);
279
+ }
275
280
  }
276
281
  loggingProvider_1.logger.info(`Total time: ${(0, testsUtil_1.calculateTotalTimeSeconds)(commandStartTime, Date.now())} seconds`);
277
282
  if (parsed.reporter) {
@@ -283,3 +288,23 @@ async function run(parsed) {
283
288
  return 'done';
284
289
  }
285
290
  exports.run = run;
291
+ function generateRunCommandTemplate(parsed, testResults) {
292
+ var _a, _b, _c, _d, _e, _f, _g;
293
+ const testResult = testResults.testResults[0];
294
+ let templateCommand = `${defaultEnv_1.SCRIPT_NAME} ${parsed._.join(' ')} \\\n`;
295
+ if ((_a = testResult === null || testResult === void 0 ? void 0 : testResult.rerunConfig) === null || _a === void 0 ? void 0 : _a.environmentId) {
296
+ templateCommand = `${templateCommand} --${constants_1.CommandArgEnvironmentId} ${(_b = testResult === null || testResult === void 0 ? void 0 : testResult.rerunConfig) === null || _b === void 0 ? void 0 : _b.environmentId} \\\n`;
297
+ }
298
+ if ((_c = testResult === null || testResult === void 0 ? void 0 : testResult.rerunConfig) === null || _c === void 0 ? void 0 : _c.credentialsId) {
299
+ templateCommand = `${templateCommand} --${constants_1.CommandArgCredentials} ${(_d = testResult === null || testResult === void 0 ? void 0 : testResult.rerunConfig) === null || _d === void 0 ? void 0 : _d.credentialsId} \\\n`;
300
+ }
301
+ if ((_e = testResult === null || testResult === void 0 ? void 0 : testResult.rerunConfig) === null || _e === void 0 ? void 0 : _e.url) {
302
+ templateCommand = `${templateCommand} --${constants_1.CommandArgUrl} ${(_f = testResult === null || testResult === void 0 ? void 0 : testResult.rerunConfig) === null || _f === void 0 ? void 0 : _f.url} \\\n`;
303
+ }
304
+ if ((_g = testResult === null || testResult === void 0 ? void 0 : testResult.rerunConfig) === null || _g === void 0 ? void 0 : _g.branch) {
305
+ templateCommand = `${templateCommand} --${constants_1.CommandArgMablBranch} ${testResult.rerunConfig.branch} \\\n`;
306
+ }
307
+ const testIdVal = parsed.id ? parsed.id : '<TEST-ID>';
308
+ templateCommand = `${templateCommand} --${constants_1.CommandArgId} ${testIdVal}`;
309
+ return templateCommand;
310
+ }
@@ -33,8 +33,7 @@ function getTimestamp() {
33
33
  }
34
34
  function hasOwnerRoleInWorkspace(user, workspaceId) {
35
35
  var _a;
36
- const hasOwnerRole = (_a = user.roles) === null || _a === void 0 ? void 0 : _a.some((role) => role.organization_id === workspaceId &&
37
- role.role === mablApi_1.UserRole.RoleEnum.Owner);
36
+ const hasOwnerRole = (_a = user.roles) === null || _a === void 0 ? void 0 : _a.some((role) => role.organization_id === workspaceId && role.role === mablApi_1.UserRoleEnum.Owner);
38
37
  if (!hasOwnerRole) {
39
38
  loggingProvider_1.logger.warn(chalk_1.default.yellow(`You must be an owner in both workspaces to copy. You do not have owner permissions in workspace: ${workspaceId}`));
40
39
  }
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.getDefaultLogMetadataForInfo = exports.MablCoreProcessAction = exports.MablCoreAction = exports.ObservationErrorSeverity = exports.ObservationErrorCode = exports.ExecutionPhase = exports.EventChannelMessageType = exports.getEmitter = exports.mablEventEmitter = exports.MablCoreEventEmitter = void 0;
4
+ exports.createAssertFailureMessage = exports.getDefaultLogMetadataForInfo = exports.MablCoreProcessAction = exports.MablCoreAction = exports.ObservationErrorSeverity = exports.ObservationErrorCode = exports.ExecutionPhase = exports.EventChannelMessageType = exports.getEmitter = exports.mablEventEmitter = exports.MablCoreEventEmitter = void 0;
5
5
  const events_1 = require("events");
6
6
  const loggingProvider_1 = require("../../providers/logging/loggingProvider");
7
+ const pureUtil_1 = require("../../util/pureUtil");
7
8
  const NO_ID_PLACEHOLDER = 'NO_ID_PROVIDED';
8
9
  class MablCoreEventEmitter {
9
10
  constructor(debug) {
@@ -101,3 +102,16 @@ function getDefaultLogMetadataForInfo(testId, testInvariantId) {
101
102
  };
102
103
  }
103
104
  exports.getDefaultLogMetadataForInfo = getDefaultLogMetadataForInfo;
105
+ function createAssertFailureMessage(assertFailure) {
106
+ if (assertFailure) {
107
+ const { reason, expected, found, continueOnFailure } = assertFailure;
108
+ return {
109
+ reason,
110
+ expected: (0, pureUtil_1.stringifyIfPresent)(expected),
111
+ found: (0, pureUtil_1.stringifyIfPresent)(found),
112
+ continueOnFailure,
113
+ };
114
+ }
115
+ return;
116
+ }
117
+ exports.createAssertFailureMessage = createAssertFailureMessage;