@mablhq/mabl-cli 2.78.5 → 2.79.7

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.
@@ -58,8 +58,7 @@ const assignXrayToMablTest = (0, mcpMablTool_1.defineTool)({
58
58
  return (0, common_1.stringToToolResult)(`There is already an Xray test case assigned to this mabl test. Follow the instructions below:
59
59
  ## Instructions
60
60
  1. Show the user the following information about the currently assigned Xray test case:
61
- Name: ${xrayBinding.name}.
62
- Description: ${xrayBinding.description}.
61
+ Xray Issue ID: ${xrayBinding.external_entity_id}.
63
62
  2. Ask the user if they want to overwrite the existing assignment. If they do, call #assign_xray_to_mabl_test again with the overwrite parameter set to true.`, true);
64
63
  }
65
64
  await mablApiClient.deleteExternalTcmBinding(externalTcmPlatform, mablEntityType, mablEntityId);
@@ -7,7 +7,7 @@ const messaging_1 = require("../../../../core/messaging/messaging");
7
7
  const mablApi_1 = require("../../../../mablApi");
8
8
  const common_1 = require("../utils/common");
9
9
  const featureSet_1 = require("../../../../api/featureSet");
10
- const openUtils_1 = require("../../../../core/trainer/openUtils");
10
+ const testUtils_1 = require("../utils/testUtils");
11
11
  const createMablTest = (0, mcpMablTool_1.defineTool)({
12
12
  schema: {
13
13
  name: 'create_mabl_test',
@@ -124,21 +124,11 @@ const createMablTest = (0, mcpMablTool_1.defineTool)({
124
124
  testType: params.isApiTest ? mablApi_1.TestTypeEnum.Api : mablApi_1.TestTypeEnum.Browser,
125
125
  apiDescription: params.apiSpec,
126
126
  }, mablApiClient, false);
127
- if (process.platform === openUtils_1.WIN32_PLATFORM) {
128
- return {
129
- content: [
130
- {
131
- type: 'text',
132
- text: `Tell the user to open the following URL to launch the mabl trainer: ${trainerUrl}. Show it using a link, trying to hide the URL full url. If you have a tool that can open a link use it.`,
133
- },
134
- ],
135
- };
136
- }
137
127
  return {
138
128
  content: [
139
129
  {
140
130
  type: 'text',
141
- text: 'A mabl trainer was launched. You can finish the test creation in the mabl trainer app.',
131
+ text: (0, testUtils_1.getCreateTestOutputText)(trainerUrl),
142
132
  },
143
133
  ],
144
134
  };
@@ -6,6 +6,7 @@ const trainingSessions_1 = require("../../../../core/trainer/trainingSessions");
6
6
  const mablApi_1 = require("../../../../mablApi");
7
7
  const common_1 = require("../utils/common");
8
8
  const featureSet_1 = require("../../../../api/featureSet");
9
+ const testUtils_1 = require("../utils/testUtils");
9
10
  const createMablTestFromPlan = (0, mcpMablTool_1.defineTool)({
10
11
  schema: {
11
12
  name: 'create_mabl_test_from_plan',
@@ -28,7 +29,7 @@ const createMablTestFromPlan = (0, mcpMablTool_1.defineTool)({
28
29
  const deployment = await mablApiClient.getDeploymentEntity(artifact?.test_information?.deployment_id);
29
30
  url = deployment.uri;
30
31
  }
31
- await (0, trainingSessions_1.trainNewTest)({
32
+ const trainerUrl = await (0, trainingSessions_1.trainNewTest)({
32
33
  environmentId: artifact?.test_information?.environment_id,
33
34
  applicationId: artifact?.test_information?.application_id,
34
35
  credentialsId: artifact?.test_information?.credentials_id,
@@ -56,7 +57,7 @@ const createMablTestFromPlan = (0, mcpMablTool_1.defineTool)({
56
57
  content: [
57
58
  {
58
59
  type: 'text',
59
- text: `A mabl trainer was launched with the planned test. You can finish the test creation in the mabl trainer app.`,
60
+ text: (0, testUtils_1.getCreateTestOutputText)(trainerUrl),
60
61
  },
61
62
  ],
62
63
  };
@@ -14,6 +14,7 @@ exports.getXrayTestCasesOutputSchema = zod_1.z.object({
14
14
  summary: zod_1.z.string().optional(),
15
15
  })),
16
16
  xrayProjectId: zod_1.z.string().optional(),
17
+ nextOffset: zod_1.z.number().optional(),
17
18
  });
18
19
  const getXrayTestCases = (0, mcpMablTool_1.defineTool)({
19
20
  schema: {
@@ -25,6 +26,16 @@ const getXrayTestCases = (0, mcpMablTool_1.defineTool)({
25
26
  .string()
26
27
  .describe('The id of the Xray project to get test cases for, if not provided, all Xray projects will be queried and if there are multiple, the user will be prompted to select from which of these projects to get test cases')
27
28
  .optional(),
29
+ limit: zod_1.z
30
+ .number()
31
+ .min(1)
32
+ .max(100)
33
+ .optional()
34
+ .describe('Maximum number of test cases to return (default: 50)'),
35
+ offset: zod_1.z
36
+ .number()
37
+ .optional()
38
+ .describe('The offset to use to get the next page of test cases'),
28
39
  }),
29
40
  outputSchema: exports.getXrayTestCasesOutputSchema,
30
41
  type: 'readOnly',
@@ -32,11 +43,14 @@ const getXrayTestCases = (0, mcpMablTool_1.defineTool)({
32
43
  handle: async (params, context) => {
33
44
  const { mablApiClient, workspaceId } = context.getCurrentContext();
34
45
  let xrayProjectId = params.xrayProjectId;
46
+ const limit = params.limit || 50;
47
+ const offset = params.offset || 0;
35
48
  if (!context.featureIsAvailable(featureSet_1.XRAY_INTEGRATION_FEATURE_FLAG)) {
36
49
  return (0, common_1.stringToToolResult)('Xray integration is not available for your account. Please contact your workspace owner or mabl support for assistance in activating this feature.', true);
37
50
  }
38
51
  if (!xrayProjectId) {
39
- const { integrations: xrayProjects } = await mablApiClient.queryIntegrations(workspaceId, mablApi_1.IntegrationTypeEnum.XraySync);
52
+ const { integrations } = await mablApiClient.queryIntegrations(workspaceId, mablApi_1.IntegrationTypeEnum.XraySync);
53
+ const xrayProjects = integrations.filter((integration) => integration.enabled);
40
54
  if (!xrayProjects || xrayProjects.length === 0) {
41
55
  return (0, common_1.stringToToolResult)('There are no Xray projects configured for this workspace. Go into your workspace to set it up.', true);
42
56
  }
@@ -47,15 +61,24 @@ const getXrayTestCases = (0, mcpMablTool_1.defineTool)({
47
61
  }
48
62
  xrayProjectId = xrayProjects[0].id;
49
63
  }
50
- const { tests: xrayTests } = await mablApiClient.queryXrayTests(xrayProjectId);
64
+ const { tests: xrayTests } = await mablApiClient.queryXrayTests(xrayProjectId, limit, offset);
65
+ const nextOffset = offset + limit;
66
+ const nextPageExists = xrayTests && xrayTests.length === limit;
51
67
  return {
52
68
  content: [
53
69
  {
54
70
  type: 'text',
55
- text: (0, common_1.jsonPrettyPrint)({ xrayTests, xrayProjectId }),
71
+ text: `
72
+ ${xrayTests?.length ?? 0} test cases returned from the Xray project
73
+ ${nextPageExists
74
+ ? `Next page exists, call #get_xray_test_cases again with the offset parameter set to ${nextOffset} to get the next page`
75
+ : 'No next page exists, you have reached the end of the test cases'}
76
+ The retrieved test cases are: ${(0, common_1.jsonPrettyPrint)({ xrayTests })}
77
+ The tests that are already being used by other test will not be shown here.
78
+ `,
56
79
  },
57
80
  ],
58
- structuredContent: { xrayTests, xrayProjectId },
81
+ structuredContent: { xrayTests, xrayProjectId, nextOffset },
59
82
  };
60
83
  },
61
84
  });
@@ -4,6 +4,7 @@ exports.normalizeTest = exports.testOutputSchema = void 0;
4
4
  exports.needCredentialsError = needCredentialsError;
5
5
  exports.getViewTestUrl = getViewTestUrl;
6
6
  exports.getTestOutputUrl = getTestOutputUrl;
7
+ exports.getCreateTestOutputText = getCreateTestOutputText;
7
8
  const zod_1 = require("zod");
8
9
  const env_1 = require("../../../../env/env");
9
10
  exports.testOutputSchema = zod_1.z.object({
@@ -51,3 +52,8 @@ function getViewTestUrl(workspaceId, invariantId) {
51
52
  function getTestOutputUrl(workspaceId, testRunId) {
52
53
  return `${env_1.BASE_APP_URL}/workspaces/${workspaceId}/output/test-runs/${testRunId}`;
53
54
  }
55
+ function getCreateTestOutputText(trainerUrl) {
56
+ return `
57
+ A mabl trainer was launched. If you don't see the trainer running, open the following URL: ${trainerUrl} to open the trainer manually (Show the URL to the user using and explain to the user how to open it. If you have a tool that can open a link use it.).
58
+ `;
59
+ }
@@ -408,44 +408,42 @@ function validateRunCommandWithLabels(testId, suppliedLabelsInclude, suppliedLab
408
408
  }
409
409
  return true;
410
410
  }
411
- async function pullDownTestRunConfig(testRunId, apiClient) {
412
- const journeyRun = await apiClient.getTestRun(testRunId);
413
- const planRun = await apiClient.getPlanRun(journeyRun.parent_execution);
414
- const testDatatablevariables = journeyRun?.journey_parameters?.user_variables &&
415
- (0, utils_1.variableRowAsScenario)(journeyRun?.journey_parameters?.user_variables);
416
- const dataTableId = testDatatablevariables?.table_id;
417
- if (dataTableId) {
418
- await apiClient.getDataTable(dataTableId);
419
- }
411
+ function buildTestRunConfig(testRun, filterHttpRequests, planRun) {
412
+ const testDatatablevariables = testRun.journey_parameters?.user_variables &&
413
+ (0, utils_1.variableRowAsScenario)(testRun.journey_parameters?.user_variables);
420
414
  return {
421
- basicAuthCredentialsId: planRun?.run_policy?.http_auth_credentials_required
422
- ? planRun?.run_policy?.http_auth_credentials_id
423
- : undefined,
424
- branchName: journeyRun.journey_parameters?.source_control_tag,
425
- credentialsId: planRun.run_policy?.credentials_required
426
- ? planRun.run_policy?.credentials_id
427
- : undefined,
415
+ basicAuthCredentialsId: testRun.journey_parameters?.http_auth_credentials_id ??
416
+ (planRun?.run_policy?.http_auth_credentials_required
417
+ ? planRun?.run_policy?.http_auth_credentials_id
418
+ : undefined),
419
+ branchName: testRun.journey_parameters?.source_control_tag,
420
+ credentialsId: testRun.journey_parameters?.credentials_id ??
421
+ (planRun?.run_policy?.credentials_required
422
+ ? planRun?.run_policy?.credentials_id
423
+ : undefined),
428
424
  dataTableVariables: testDatatablevariables,
429
- deviceEmulation: journeyRun.journey_parameters?.device_emulation,
430
- environmentId: journeyRun.journey_parameters?.deployment?.environment_id,
425
+ deviceEmulation: testRun.journey_parameters?.device_emulation,
426
+ environmentId: testRun.journey_parameters?.deployment?.environment_id,
431
427
  extraHttpHeaders: planRun?.run_policy?.http_headers_required === true
432
- ? planRun?.run_policy?.http_headers?.reduce((headers, header) => ({
433
- ...headers,
434
- [header.name]: header.value ?? '',
435
- }), {})
428
+ ? headerArrayToRecord(planRun?.run_policy?.http_headers)
436
429
  : undefined,
437
- filterHttpRequests: false,
438
- importedVariables: journeyRun.journey_parameters?.imported_variables,
439
- linkCrawlerStartingUrlOverride: getLinkCrawlerUrlOverride(journeyRun),
440
- localizationOptions: journeyRun.localization_options,
441
- pageLoadWait: journeyRun.journey_parameters?.page_load_wait,
442
- runId: journeyRun.id,
443
- testId: journeyRun.journey?.invariant_id,
444
- url: journeyRun.journey_parameters?.deployment?.uri,
430
+ filterHttpRequests,
431
+ importedVariables: testRun.journey_parameters?.imported_variables,
432
+ linkCrawlerStartingUrlOverride: getLinkCrawlerUrlOverride(testRun),
433
+ localizationOptions: testRun.localization_options,
434
+ pageLoadWait: testRun.journey_parameters?.page_load_wait,
435
+ runId: testRun.id,
436
+ testId: testRun.journey?.invariant_id,
437
+ url: testRun.journey_parameters?.deployment?.uri,
445
438
  };
446
439
  }
440
+ async function pullDownTestRunConfig(testRunId, apiClient) {
441
+ const testRun = await apiClient.getTestRun(testRunId);
442
+ const planRun = await apiClient.getPlanRun(testRun.parent_execution);
443
+ return buildTestRunConfig(testRun, false, planRun);
444
+ }
447
445
  async function extractTestRunConfig(executionMessage, apiClient) {
448
- const journeyRun = executionMessage.journey_run ??
446
+ const testRun = executionMessage.journey_run ??
449
447
  (await apiClient.getTestRun(executionMessage.journey_run_id));
450
448
  const planRun = executionMessage.plan_run;
451
449
  const maybeRunnerType = planRun?.run_policy
@@ -453,31 +451,8 @@ async function extractTestRunConfig(executionMessage, apiClient) {
453
451
  ?
454
452
  planRun?.run_policy?.nodejs_runtime_variant
455
453
  : undefined;
456
- const config = {
457
- basicAuthCredentialsId: planRun?.run_policy?.http_auth_credentials_required
458
- ? planRun?.run_policy?.http_auth_credentials_id
459
- : undefined,
460
- branchName: journeyRun?.journey_parameters?.source_control_tag,
461
- credentialsId: planRun?.run_policy?.credentials_required
462
- ? planRun?.run_policy?.credentials_id
463
- : undefined,
464
- dataTableVariables: journeyRun?.journey_parameters?.user_variables &&
465
- (0, utils_1.variableRowAsScenario)(journeyRun?.journey_parameters?.user_variables),
466
- deviceEmulation: journeyRun?.journey_parameters?.device_emulation,
467
- environmentId: journeyRun?.journey_parameters?.deployment?.environment_id,
468
- extraHttpHeaders: planRun?.run_policy?.http_headers_required === true
469
- ? headerArrayToRecord(planRun?.run_policy?.http_headers)
470
- : undefined,
471
- filterHttpRequests: true,
472
- importedVariables: journeyRun.journey_parameters?.imported_variables,
473
- linkCrawlerStartingUrlOverride: getLinkCrawlerUrlOverride(journeyRun),
474
- localizationOptions: journeyRun.localization_options,
475
- pageLoadWait: journeyRun.journey_parameters?.page_load_wait,
476
- runId: journeyRun.id,
477
- runnerType: maybeRunnerType,
478
- testId: journeyRun?.journey?.invariant_id,
479
- url: journeyRun?.journey_parameters?.deployment?.uri,
480
- };
454
+ const config = buildTestRunConfig(testRun, true, planRun);
455
+ config.runnerType = maybeRunnerType;
481
456
  if (executionMessage.test_type === mablApi_1.TestTypeEnum.Mobile) {
482
457
  const mobileMessage = executionMessage;
483
458
  config.mobileConfig = {
@@ -63,8 +63,12 @@ async function createConfigServer(config) {
63
63
  return new Promise((resolve, reject) => {
64
64
  server.listen(0, () => {
65
65
  const port = server.address().port;
66
- const requestPromise = new Promise((resolveRequest) => {
66
+ const requestPromise = new Promise((resolveRequest, rejectRequest) => {
67
+ const timeout = (0, timers_1.setTimeout)(() => {
68
+ rejectRequest(new Error('Failed to open the trainer automatically. User will open the trainer manually.'));
69
+ }, 10_000);
67
70
  server.on('close', () => {
71
+ global.clearTimeout(timeout);
68
72
  resolveRequest();
69
73
  });
70
74
  });