@mablhq/mabl-cli 2.16.4 → 2.19.13

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.
@@ -13,9 +13,10 @@ const query_string_1 = __importDefault(require("query-string"));
13
13
  const featureSet_1 = require("./featureSet");
14
14
  class MablApiClient extends basicApiClient_1.BasicApiClient {
15
15
  constructor(options) {
16
- var _a;
16
+ var _a, _b;
17
17
  super(options);
18
- this.baseApiUrl = (_a = options.apiUrl) !== null && _a !== void 0 ? _a : env_1.BASE_API_URL;
18
+ this.baseApiUrl =
19
+ (_b = (_a = options.apiUrl) !== null && _a !== void 0 ? _a : process.env.MABL_API_URL) !== null && _b !== void 0 ? _b : env_1.BASE_API_URL;
19
20
  }
20
21
  async getPlan(planId) {
21
22
  try {
@@ -65,9 +65,11 @@ class ChromiumBrowserEngine {
65
65
  }
66
66
  if (options.disableIsolation) {
67
67
  disableFeaturesFlags.push('IsolateOrigins');
68
+ disableFeaturesFlags.push('site-per-process');
69
+ }
70
+ if (disableFeaturesFlags.length) {
71
+ commandLineArgs.push(`--disable-features=${disableFeaturesFlags.join(',')}`);
68
72
  }
69
- disableFeaturesFlags.push('site-per-process');
70
- commandLineArgs.push(`--disable-features=${disableFeaturesFlags.join(',')}`);
71
73
  if (options.autoOpenDevtoolsForTabs) {
72
74
  commandLineArgs.push('--auto-open-devtools-for-tabs');
73
75
  }
@@ -8,7 +8,6 @@ var PageEvent;
8
8
  PageEvent["FrameAttached"] = "frameattached";
9
9
  PageEvent["FrameNavigated"] = "framenavigated";
10
10
  PageEvent["PageError"] = "pageerror";
11
- PageEvent["Request"] = "request";
12
11
  PageEvent["RequestWillBeSentExtraInfo"] = "requestwillbesentextrainfo";
13
12
  PageEvent["Response"] = "response";
14
13
  PageEvent["SecondaryWorldCreated"] = "secondaryworldcreated";
@@ -32,6 +32,7 @@ class PlaywrightHttpRequest {
32
32
  this.page = page;
33
33
  this.request = request;
34
34
  this.route = route;
35
+ this.requestIntercepted = false;
35
36
  try {
36
37
  this.documentId = playwright._toImpl(this.request)._documentId;
37
38
  }
@@ -45,15 +46,9 @@ class PlaywrightHttpRequest {
45
46
  if (this.route === undefined) {
46
47
  throw new Error('Unable to abort an unrouted request');
47
48
  }
49
+ this.requestIntercepted = true;
48
50
  return (_a = this.route) === null || _a === void 0 ? void 0 : _a.abort();
49
51
  }
50
- continue() {
51
- var _a;
52
- if (this.route === undefined) {
53
- throw new Error('Unable to continue an unrouted request');
54
- }
55
- return (_a = this.route) === null || _a === void 0 ? void 0 : _a.continue();
56
- }
57
52
  frame() {
58
53
  return this.page.getOrCreateFrame(this.request.frame());
59
54
  }
@@ -65,6 +60,7 @@ class PlaywrightHttpRequest {
65
60
  if (this.route === undefined) {
66
61
  throw new Error('Unable to respond an unrouted request');
67
62
  }
63
+ this.requestIntercepted = true;
68
64
  return (_a = this.route) === null || _a === void 0 ? void 0 : _a.fulfill(response);
69
65
  }
70
66
  url() {
@@ -73,5 +69,8 @@ class PlaywrightHttpRequest {
73
69
  headers() {
74
70
  return this.request.allHeaders();
75
71
  }
72
+ wasIntercepted() {
73
+ return this.requestIntercepted;
74
+ }
76
75
  }
77
76
  exports.PlaywrightHttpRequest = PlaywrightHttpRequest;
@@ -48,6 +48,7 @@ class PlaywrightPage extends events_1.default {
48
48
  this.browser = browser;
49
49
  this.delegate = delegate;
50
50
  this.playwrightFrames = new Map();
51
+ this.requestListeners = [];
51
52
  const guid = page._guid;
52
53
  this.pageId = (_a = delegate.getTargetId()) !== null && _a !== void 0 ? _a : guid;
53
54
  page.off('dialog', this.acceptDialogs);
@@ -75,6 +76,26 @@ class PlaywrightPage extends events_1.default {
75
76
  }
76
77
  });
77
78
  }
79
+ addRequestListener(listener) {
80
+ if (this.requestListeners.length === 0) {
81
+ void this.page.route('**/*', async (route) => {
82
+ const listeners = this.requestListeners;
83
+ if (listeners.length === 0) {
84
+ void route.continue();
85
+ return;
86
+ }
87
+ const request = new playwrightHttpRequest_1.PlaywrightHttpRequest(this, route.request(), route);
88
+ for (const listener of listeners) {
89
+ await listener(request);
90
+ if (request.wasIntercepted()) {
91
+ return;
92
+ }
93
+ }
94
+ await route.continue();
95
+ });
96
+ }
97
+ this.requestListeners.push(listener);
98
+ }
78
99
  createCDPSession() {
79
100
  return this.delegate.createCDPSession();
80
101
  }
@@ -108,11 +129,6 @@ class PlaywrightPage extends events_1.default {
108
129
  this.emit(browserLauncher_1.PageEvent.Response, new playwrightHttpResponse_1.PlaywrightHttpResponse(this, event));
109
130
  });
110
131
  }
111
- if (event === browserLauncher_1.PageEvent.Request && !this.listenerCount(event)) {
112
- void this.page.route('**/*', (route) => {
113
- this.emit(browserLauncher_1.PageEvent.Request, new playwrightHttpRequest_1.PlaywrightHttpRequest(this, route.request(), route));
114
- });
115
- }
116
132
  if (event === browserLauncher_1.PageEvent.FrameNavigated && !this.listenerCount(event)) {
117
133
  this.page.on('framenavigated', (frame) => {
118
134
  this.emit(browserLauncher_1.PageEvent.FrameNavigated, this.getOrCreateFrame(frame));
@@ -9,11 +9,18 @@ exports.command = `activate-key <${constants_1.CommandArgApiKey}>`;
9
9
  exports.describe = 'Activate auth by api key';
10
10
  exports.builder = {};
11
11
  exports.builder = (yargs) => {
12
- yargs.positional(constants_1.CommandArgApiKey, {
12
+ yargs
13
+ .positional(constants_1.CommandArgApiKey, {
13
14
  describe: 'API key (escape leading dashes with "\\" (e.g. "\\-yourKey")',
14
15
  type: 'string',
15
16
  nargs: 1,
16
17
  demand: 'API key argument required',
18
+ })
19
+ .option('skip-workspace', {
20
+ describe: 'Set to skip validating and setting the workspace',
21
+ default: false,
22
+ hidden: true,
23
+ type: 'boolean',
17
24
  });
18
25
  };
19
26
  exports.handler = (0, util_1.failWrapper)(activateApiKey);
@@ -23,7 +30,9 @@ async function activateApiKey(parsed) {
23
30
  const cleanApiKey = apiKey.replace(/^\\+/, '');
24
31
  const mablApi = await mablApiClientFactory_1.MablApiClientFactory.createApiClientFromApiKey(cleanApiKey);
25
32
  const apiKeyDetails = await mablApi.getApiKeyDetails();
26
- const apiKeyWorkspace = await mablApi.getWorkspace(apiKeyDetails.workspace_id);
27
- await cliConfigProvider_1.CliConfigProvider.setWorkspace(apiKeyWorkspace);
33
+ if (!parsed['skip-workspace']) {
34
+ const apiKeyWorkspace = await mablApi.getWorkspace(apiKeyDetails.workspace_id);
35
+ await cliConfigProvider_1.CliConfigProvider.setWorkspace(apiKeyWorkspace);
36
+ }
28
37
  return auth.activateApiKey(cleanApiKey);
29
38
  }
@@ -35,10 +35,11 @@ const runUtils_1 = require("./runUtils");
35
35
  const MobileAppFileCache_1 = require("../../../util/MobileAppFileCache");
36
36
  const fs = __importStar(require("fs"));
37
37
  const resourceUtil_1 = require("../../../util/resourceUtil");
38
+ const mablApiClientFactory_1 = require("../../../api/mablApiClientFactory");
38
39
  const chalk = require('chalk');
39
40
  const execution = require('../../../execution/index');
40
41
  exports.command = `run-mobile`;
41
- exports.describe = false;
42
+ exports.describe = 'Run a mobile test locally';
42
43
  exports.builder = (yargs) => {
43
44
  yargs
44
45
  .example('$0 tests run-mobile --id <id> --app-file <path> --platform <platform>', 'run mobile test locally by id')
@@ -71,6 +72,10 @@ exports.builder = (yargs) => {
71
72
  hidden: true,
72
73
  type: 'string',
73
74
  choices: [constants_1.ReporterOptions.Mochawesome],
75
+ })
76
+ .option(constants_1.CommandArgTestRunId, {
77
+ describe: 'The id of the test run to pull config from',
78
+ type: 'string',
74
79
  })
75
80
  .option(constants_1.CommandArgReporterOptions, {
76
81
  describe: 'Reporter options as comma separated key/values pairs. e.g. "reportDir=path/to,json=true"',
@@ -81,27 +86,33 @@ exports.builder = (yargs) => {
81
86
  describe: 'Full name of the device to use for the test',
82
87
  hidden: false,
83
88
  type: 'string',
89
+ })
90
+ .option(constants_1.CommandArgLabelsInclude, {
91
+ describe: 'Space delimited test labels. Run tests that match any label.',
92
+ type: 'array',
93
+ conflicts: [constants_1.CommandArgId],
94
+ })
95
+ .option(constants_1.CommandArgLabelsExclude, {
96
+ describe: 'Space delimited test labels. Exclude tests that match any label.',
97
+ type: 'array',
98
+ conflicts: [constants_1.CommandArgId],
84
99
  })
85
100
  .implies(constants_1.CommandArgReporterOptions, constants_1.CommandArgReporter)
86
- .middleware(inferMobilePlatform);
101
+ .middleware(inferMobilePlatform)
102
+ .check((argv) => {
103
+ (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, undefined);
104
+ validateMobileCommand(argv[constants_1.CommandArgMobileAppFile], argv[constants_1.CommandArgMobileAppFileId]);
105
+ return true;
106
+ });
87
107
  };
88
108
  const exitCodeOnError = 1;
89
109
  exports.handler = (0, util_1.failWrapper)(run, exitCodeOnError);
90
- function validateMobileCommand(id, appFile, appFileId, platform) {
91
- if (!id || (!appFile && !appFileId) || !platform) {
92
- loggingProvider_1.logger.error(chalk.red(`Please provide the arguments --${constants_1.CommandArgId}, --${constants_1.CommandArgMobileAppFile} or --${constants_1.CommandArgMobileAppFileId}, and --${constants_1.CommandArgMobilePlatform}`));
93
- return false;
94
- }
110
+ function validateMobileCommand(appFile, appFileId) {
95
111
  if (appFile && appFileId) {
96
- loggingProvider_1.logger.error(chalk.red(`Only one of { --${constants_1.CommandArgMobileAppFile}, --${constants_1.CommandArgMobileAppFileId} } may be specified`));
97
- return false;
112
+ throw new Error(`Only one of { --${constants_1.CommandArgMobileAppFile}, --${constants_1.CommandArgMobileAppFileId} } may be specified`);
98
113
  }
99
- return true;
100
114
  }
101
115
  async function run(parsed) {
102
- if (!validateMobileCommand(parsed[constants_1.CommandArgId], parsed[constants_1.CommandArgMobileAppFile], parsed[constants_1.CommandArgMobileAppFileId], parsed[constants_1.CommandArgMobilePlatform])) {
103
- process.exit(1);
104
- }
105
116
  const commandStartTime = Date.now();
106
117
  let workspaceId;
107
118
  try {
@@ -115,11 +126,22 @@ async function run(parsed) {
115
126
  loggingProvider_1.logger.error(`[ERROR] Missing the mobile tools for running tests, run ${chalk.magenta('mabl config install mobile-tools')} to install them`);
116
127
  return;
117
128
  }
118
- const platformName = (0, execution_1.parsePlatformName)(parsed[constants_1.CommandArgMobilePlatform]);
129
+ let platformName;
130
+ if (parsed[constants_1.CommandArgTestRunId]) {
131
+ const apiClient = await mablApiClientFactory_1.MablApiClientFactory.createApiClient();
132
+ const testRun = await apiClient.getJourneyRun(parsed[constants_1.CommandArgTestRunId]);
133
+ platformName = (0, runUtils_1.getPlatformFromTestRun)(testRun);
134
+ }
135
+ else {
136
+ platformName = (0, execution_1.parsePlatformName)(parsed[constants_1.CommandArgMobilePlatform]);
137
+ }
119
138
  await validateSystemRequirements(platformName, parsed[constants_1.CommandArgMobileDeviceName]);
120
139
  const testRunnerConfig = {
121
140
  _cliCreated: true,
122
141
  filterHttpRequests: false,
142
+ labelsExclude: parsed[constants_1.CommandArgLabelsExclude],
143
+ labelsInclude: parsed[constants_1.CommandArgLabelsInclude],
144
+ runId: parsed[constants_1.CommandArgTestRunId],
123
145
  testId: parsed.id,
124
146
  workspaceId,
125
147
  mobileConfig: {
@@ -3,11 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.logTestResults = void 0;
6
+ exports.getPlatformFromTestRun = exports.logTestResults = void 0;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const loggingProvider_1 = require("../../../providers/logging/loggingProvider");
9
9
  const reporter_1 = require("../../../reporters/reporter");
10
10
  const testsUtil_1 = require("../testsUtil");
11
+ const execution_1 = require("../../../execution");
11
12
  async function logTestResults(results, parsed, commandStartTime, rerunCommandTemplate) {
12
13
  loggingProvider_1.logger.logNewLine();
13
14
  loggingProvider_1.logger.info(`Test count: ${results.numTotalTests}`);
@@ -48,3 +49,12 @@ exports.logTestResults = logTestResults;
48
49
  function getTestResultDescription(result) {
49
50
  return `${result.testName}${result.scenarioName ? ` - Scenario: ${result.scenarioName}` : ''}`;
50
51
  }
52
+ function getPlatformFromTestRun(testRun) {
53
+ var _a, _b;
54
+ const platform = (_b = (_a = testRun.journey_parameters) === null || _a === void 0 ? void 0 : _a.mobile_device) === null || _b === void 0 ? void 0 : _b.platform;
55
+ if (!platform) {
56
+ throw new Error(`Unable to determine mobile platform from test run: ${testRun.id}`);
57
+ }
58
+ return (0, execution_1.parsePlatformName)(platform);
59
+ }
60
+ exports.getPlatformFromTestRun = getPlatformFromTestRun;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MobileTrainingActionsEventEmitter = exports.MobileTrainingMessageType = void 0;
4
+ const messaging_1 = require("../messaging");
5
+ var MobileTrainingMessageType;
6
+ (function (MobileTrainingMessageType) {
7
+ MobileTrainingMessageType["foundElement"] = "mobileFoundElement";
8
+ })(MobileTrainingMessageType = exports.MobileTrainingMessageType || (exports.MobileTrainingMessageType = {}));
9
+ class MobileTrainingActionsEventEmitter {
10
+ static emitFoundElementInfo(foundElement, isACandidateElement) {
11
+ return messaging_1.mablEventEmitter.dispatch(new messaging_1.MablCoreAction(MobileTrainingMessageType.foundElement, {
12
+ boundingBox: foundElement.boundingBox,
13
+ isACandidateElement,
14
+ }));
15
+ }
16
+ }
17
+ exports.MobileTrainingActionsEventEmitter = MobileTrainingActionsEventEmitter;