@browserstack/mcp-server 1.1.9 → 1.2.1

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 (124) hide show
  1. package/README.md +4 -1
  2. package/dist/config.d.ts +13 -0
  3. package/dist/config.js +4 -6
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.js +21 -31
  6. package/dist/lib/api.d.ts +2 -0
  7. package/dist/lib/api.js +10 -5
  8. package/dist/lib/apiClient.d.ts +31 -0
  9. package/dist/lib/apiClient.js +108 -0
  10. package/dist/lib/constants.d.ts +17 -0
  11. package/dist/lib/device-cache.d.ts +9 -0
  12. package/dist/lib/device-cache.js +3 -3
  13. package/dist/lib/error.d.ts +7 -0
  14. package/dist/lib/fuzzy.d.ts +1 -0
  15. package/dist/lib/get-auth.d.ts +2 -0
  16. package/dist/lib/get-auth.js +8 -0
  17. package/dist/lib/inmemory-store.d.ts +1 -0
  18. package/dist/lib/instrumentation.d.ts +4 -0
  19. package/dist/lib/instrumentation.js +8 -7
  20. package/dist/lib/local.d.ts +3 -0
  21. package/dist/lib/local.js +3 -3
  22. package/dist/lib/types.d.ts +4 -0
  23. package/dist/lib/types.js +1 -0
  24. package/dist/lib/utils.d.ts +4 -0
  25. package/dist/lib/version-resolver.d.ts +6 -0
  26. package/dist/logger.d.ts +3 -0
  27. package/dist/logger.js +20 -4
  28. package/dist/oninitialized.d.ts +2 -0
  29. package/dist/oninitialized.js +2 -7
  30. package/dist/server-factory.d.ts +25 -0
  31. package/dist/server-factory.js +70 -0
  32. package/dist/tools/accessibility.d.ts +3 -0
  33. package/dist/tools/accessibility.js +21 -12
  34. package/dist/tools/accessiblity-utils/accessibility-rag.d.ts +12 -0
  35. package/dist/tools/accessiblity-utils/accessibility-rag.js +22 -12
  36. package/dist/tools/accessiblity-utils/report-fetcher.d.ts +8 -0
  37. package/dist/tools/accessiblity-utils/report-fetcher.js +26 -16
  38. package/dist/tools/accessiblity-utils/report-parser.d.ts +23 -0
  39. package/dist/tools/accessiblity-utils/report-parser.js +3 -3
  40. package/dist/tools/accessiblity-utils/scanner.d.ts +25 -0
  41. package/dist/tools/accessiblity-utils/scanner.js +43 -24
  42. package/dist/tools/appautomate-utils/appautomate.d.ts +42 -0
  43. package/dist/tools/appautomate-utils/appautomate.js +63 -38
  44. package/dist/tools/appautomate-utils/types.d.ts +5 -0
  45. package/dist/tools/appautomate.d.ts +3 -0
  46. package/dist/tools/appautomate.js +24 -21
  47. package/dist/tools/applive-utils/device-search.d.ts +6 -0
  48. package/dist/tools/applive-utils/start-session.d.ts +15 -0
  49. package/dist/tools/applive-utils/start-session.js +11 -4
  50. package/dist/tools/applive-utils/types.d.ts +7 -0
  51. package/dist/tools/applive-utils/upload-app.d.ts +5 -0
  52. package/dist/tools/applive-utils/upload-app.js +8 -12
  53. package/dist/tools/applive-utils/version-utils.d.ts +4 -0
  54. package/dist/tools/applive.d.ts +13 -0
  55. package/dist/tools/applive.js +9 -7
  56. package/dist/tools/automate-utils/fetch-screenshots.d.ts +6 -0
  57. package/dist/tools/automate-utils/fetch-screenshots.js +16 -12
  58. package/dist/tools/automate.d.ts +9 -0
  59. package/dist/tools/automate.js +9 -7
  60. package/dist/tools/bstack-sdk.d.ts +17 -0
  61. package/dist/tools/bstack-sdk.js +15 -8
  62. package/dist/tools/failurelogs-utils/app-automate.d.ts +7 -0
  63. package/dist/tools/failurelogs-utils/app-automate.js +29 -11
  64. package/dist/tools/failurelogs-utils/automate.d.ts +6 -0
  65. package/dist/tools/failurelogs-utils/automate.js +27 -12
  66. package/dist/tools/failurelogs-utils/utils.d.ts +30 -0
  67. package/dist/tools/getFailureLogs.d.ts +14 -0
  68. package/dist/tools/getFailureLogs.js +15 -16
  69. package/dist/tools/live-utils/desktop-filter.d.ts +2 -0
  70. package/dist/tools/live-utils/mobile-filter.d.ts +2 -0
  71. package/dist/tools/live-utils/start-session.d.ts +6 -0
  72. package/dist/tools/live-utils/start-session.js +18 -5
  73. package/dist/tools/live-utils/types.d.ts +33 -0
  74. package/dist/tools/live.d.ts +3 -0
  75. package/dist/tools/live.js +28 -16
  76. package/dist/tools/observability.d.ts +5 -0
  77. package/dist/tools/observability.js +14 -11
  78. package/dist/tools/sdk-utils/commands.d.ts +3 -0
  79. package/dist/tools/sdk-utils/commands.js +5 -4
  80. package/dist/tools/sdk-utils/constants.d.ts +2 -0
  81. package/dist/tools/sdk-utils/constants.js +82 -21
  82. package/dist/tools/sdk-utils/instructions.d.ts +6 -0
  83. package/dist/tools/sdk-utils/instructions.js +8 -6
  84. package/dist/tools/sdk-utils/percy/constants.d.ts +3 -0
  85. package/dist/tools/sdk-utils/percy/constants.js +1 -0
  86. package/dist/tools/sdk-utils/percy/instructions.d.ts +10 -0
  87. package/dist/tools/sdk-utils/percy/types.d.ts +5 -0
  88. package/dist/tools/sdk-utils/types.d.ts +39 -0
  89. package/dist/tools/sdk-utils/types.js +1 -0
  90. package/dist/tools/selfheal-utils/selfheal.d.ts +11 -0
  91. package/dist/tools/selfheal-utils/selfheal.js +10 -6
  92. package/dist/tools/selfheal.d.ts +7 -0
  93. package/dist/tools/selfheal.js +9 -7
  94. package/dist/tools/testmanagement-utils/TCG-utils/api.d.ts +34 -0
  95. package/dist/tools/testmanagement-utils/TCG-utils/api.js +57 -44
  96. package/dist/tools/testmanagement-utils/TCG-utils/config.d.ts +5 -0
  97. package/dist/tools/testmanagement-utils/TCG-utils/helpers.d.ts +13 -0
  98. package/dist/tools/testmanagement-utils/TCG-utils/helpers.js +2 -1
  99. package/dist/tools/testmanagement-utils/TCG-utils/types.d.ts +26 -0
  100. package/dist/tools/testmanagement-utils/add-test-result.d.ts +42 -0
  101. package/dist/tools/testmanagement-utils/add-test-result.js +23 -10
  102. package/dist/tools/testmanagement-utils/create-lca-steps.d.ts +100 -0
  103. package/dist/tools/testmanagement-utils/create-lca-steps.js +64 -55
  104. package/dist/tools/testmanagement-utils/create-project-folder.d.ts +31 -0
  105. package/dist/tools/testmanagement-utils/create-project-folder.js +31 -21
  106. package/dist/tools/testmanagement-utils/create-testcase.d.ts +122 -0
  107. package/dist/tools/testmanagement-utils/create-testcase.js +13 -10
  108. package/dist/tools/testmanagement-utils/create-testrun.d.ts +82 -0
  109. package/dist/tools/testmanagement-utils/create-testrun.js +11 -8
  110. package/dist/tools/testmanagement-utils/list-testcases.d.ts +30 -0
  111. package/dist/tools/testmanagement-utils/list-testcases.js +9 -7
  112. package/dist/tools/testmanagement-utils/list-testruns.d.ts +22 -0
  113. package/dist/tools/testmanagement-utils/list-testruns.js +9 -7
  114. package/dist/tools/testmanagement-utils/poll-lca-status.d.ts +11 -0
  115. package/dist/tools/testmanagement-utils/poll-lca-status.js +12 -8
  116. package/dist/tools/testmanagement-utils/testcase-from-file.d.ts +4 -0
  117. package/dist/tools/testmanagement-utils/testcase-from-file.js +6 -6
  118. package/dist/tools/testmanagement-utils/update-testrun.d.ts +40 -0
  119. package/dist/tools/testmanagement-utils/update-testrun.js +11 -7
  120. package/dist/tools/testmanagement-utils/upload-file.d.ts +20 -0
  121. package/dist/tools/testmanagement-utils/upload-file.js +8 -6
  122. package/dist/tools/testmanagement.d.ts +60 -0
  123. package/dist/tools/testmanagement.js +53 -53
  124. package/package.json +1 -1
@@ -1,14 +1,15 @@
1
- import axios from "axios";
1
+ import { apiClient } from "../../../lib/apiClient.js";
2
2
  import { TCG_TRIGGER_URL, TCG_POLL_URL, FETCH_DETAILS_URL, FORM_FIELDS_URL, BULK_CREATE_URL, } from "./config.js";
3
3
  import { createTestCasePayload } from "./helpers.js";
4
- import config from "../../../config.js";
4
+ import { getBrowserStackAuth } from "../../../lib/get-auth.js";
5
5
  /**
6
6
  * Fetch default and custom form fields for a project.
7
7
  */
8
- export async function fetchFormFields(projectId) {
9
- const res = await axios.get(FORM_FIELDS_URL(projectId), {
8
+ export async function fetchFormFields(projectId, config) {
9
+ const res = await apiClient.get({
10
+ url: FORM_FIELDS_URL(projectId),
10
11
  headers: {
11
- "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
12
+ "API-TOKEN": getBrowserStackAuth(config),
12
13
  },
13
14
  });
14
15
  return res.data;
@@ -16,44 +17,48 @@ export async function fetchFormFields(projectId) {
16
17
  /**
17
18
  * Trigger AI-based test case generation for a document.
18
19
  */
19
- export async function triggerTestCaseGeneration(document, documentId, folderId, projectId, source) {
20
- const res = await axios.post(TCG_TRIGGER_URL, {
21
- document,
22
- documentId,
23
- folderId,
24
- projectId,
25
- source,
26
- webhookUrl: `https://test-management.browserstack.com/api/v1/projects/${projectId}/folder/${folderId}/webhooks/tcg`,
27
- }, {
20
+ export async function triggerTestCaseGeneration(document, documentId, folderId, projectId, source, config) {
21
+ const res = await apiClient.post({
22
+ url: TCG_TRIGGER_URL,
28
23
  headers: {
29
- "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
24
+ "API-TOKEN": getBrowserStackAuth(config),
30
25
  "Content-Type": "application/json",
31
26
  "request-source": source,
32
27
  },
28
+ body: {
29
+ document,
30
+ documentId,
31
+ folderId,
32
+ projectId,
33
+ source,
34
+ webhookUrl: `https://test-management.browserstack.com/api/v1/projects/${projectId}/folder/${folderId}/webhooks/tcg`,
35
+ },
33
36
  });
34
37
  if (res.status !== 200) {
35
- throw new Error(`Trigger failed: ${res.statusText}`);
38
+ throw new Error(`Trigger failed: ${res.statusText || res.status}`);
36
39
  }
37
40
  return res.data["x-bstack-traceRequestId"];
38
41
  }
39
42
  /**
40
43
  * Initiate a fetch for test-case details; returns the traceRequestId for polling.
41
44
  */
42
- export async function fetchTestCaseDetails(documentId, folderId, projectId, testCaseIds, source) {
45
+ export async function fetchTestCaseDetails(documentId, folderId, projectId, testCaseIds, source, config) {
43
46
  if (testCaseIds.length === 0) {
44
47
  throw new Error("No testCaseIds provided to fetchTestCaseDetails");
45
48
  }
46
- const res = await axios.post(FETCH_DETAILS_URL, {
47
- document_id: documentId,
48
- folder_id: folderId,
49
- project_id: projectId,
50
- test_case_ids: testCaseIds,
51
- }, {
49
+ const res = await apiClient.post({
50
+ url: FETCH_DETAILS_URL,
52
51
  headers: {
53
- "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
52
+ "API-TOKEN": getBrowserStackAuth(config),
54
53
  "request-source": source,
55
54
  "Content-Type": "application/json",
56
55
  },
56
+ body: {
57
+ document_id: documentId,
58
+ folder_id: folderId,
59
+ project_id: projectId,
60
+ test_case_ids: testCaseIds,
61
+ },
57
62
  });
58
63
  if (res.data.data.success !== true) {
59
64
  throw new Error(`Fetch details failed: ${res.data.data.message}`);
@@ -63,16 +68,18 @@ export async function fetchTestCaseDetails(documentId, folderId, projectId, test
63
68
  /**
64
69
  * Poll for a given traceRequestId until all test-case details are returned.
65
70
  */
66
- export async function pollTestCaseDetails(traceRequestId) {
71
+ export async function pollTestCaseDetails(traceRequestId, config) {
67
72
  const detailMap = {};
68
73
  let done = false;
69
74
  while (!done) {
70
75
  // add a bit of jitter to avoid synchronized polling storms
71
76
  await new Promise((r) => setTimeout(r, 10000 + Math.random() * 5000));
72
- const poll = await axios.post(`${TCG_POLL_URL}?x-bstack-traceRequestId=${encodeURIComponent(traceRequestId)}`, {}, {
77
+ const poll = await apiClient.post({
78
+ url: `${TCG_POLL_URL}?x-bstack-traceRequestId=${encodeURIComponent(traceRequestId)}`,
73
79
  headers: {
74
- "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
80
+ "API-TOKEN": getBrowserStackAuth(config),
75
81
  },
82
+ body: {},
76
83
  });
77
84
  if (!poll.data.data.success) {
78
85
  throw new Error(`Polling failed: ${poll.data.data.message}`);
@@ -96,7 +103,7 @@ export async function pollTestCaseDetails(traceRequestId) {
96
103
  /**
97
104
  * Poll for scenarios & testcases, trigger detail fetches, then poll all details in parallel.
98
105
  */
99
- export async function pollScenariosTestDetails(args, traceId, context, documentId, source) {
106
+ export async function pollScenariosTestDetails(args, traceId, context, documentId, source, config) {
100
107
  const { folderId, projectReferenceId } = args;
101
108
  const scenariosMap = {};
102
109
  const detailPromises = [];
@@ -105,14 +112,16 @@ export async function pollScenariosTestDetails(args, traceId, context, documentI
105
112
  await new Promise((resolve, reject) => {
106
113
  const intervalId = setInterval(async () => {
107
114
  try {
108
- const poll = await axios.post(`${TCG_POLL_URL}?x-bstack-traceRequestId=${encodeURIComponent(traceId)}`, {}, {
115
+ const poll = await apiClient.post({
116
+ url: `${TCG_POLL_URL}?x-bstack-traceRequestId=${encodeURIComponent(traceId)}`,
109
117
  headers: {
110
- "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
118
+ "API-TOKEN": getBrowserStackAuth(config),
111
119
  },
120
+ body: {},
112
121
  });
113
122
  if (poll.status !== 200) {
114
123
  clearInterval(intervalId);
115
- reject(new Error(`Polling error: ${poll.statusText}`));
124
+ reject(new Error(`Polling error: ${poll.statusText || poll.status}`));
116
125
  return;
117
126
  }
118
127
  for (const msg of poll.data.data.message) {
@@ -140,8 +149,8 @@ export async function pollScenariosTestDetails(args, traceId, context, documentI
140
149
  ? [msg.data.testcases]
141
150
  : [];
142
151
  const ids = array.map((tc) => tc.id || tc.test_case_id);
143
- const reqId = await fetchTestCaseDetails(documentId, folderId, projectReferenceId, ids, source);
144
- detailPromises.push(pollTestCaseDetails(reqId));
152
+ const reqId = await fetchTestCaseDetails(documentId, folderId, projectReferenceId, ids, source, config);
153
+ detailPromises.push(pollTestCaseDetails(reqId, config));
145
154
  scenariosMap[sc.id] ||= {
146
155
  id: sc.id,
147
156
  name: sc.name,
@@ -189,7 +198,7 @@ export async function pollScenariosTestDetails(args, traceId, context, documentI
189
198
  /**
190
199
  * Bulk-create generated test cases in BrowserStack.
191
200
  */
192
- export async function bulkCreateTestCases(scenariosMap, projectId, folderId, fieldMaps, booleanFieldId, traceId, context, documentId) {
201
+ export async function bulkCreateTestCases(scenariosMap, projectId, folderId, fieldMaps, booleanFieldId, traceId, context, documentId, config) {
193
202
  const results = {};
194
203
  const total = Object.keys(scenariosMap).length;
195
204
  let doneCount = 0;
@@ -203,11 +212,13 @@ export async function bulkCreateTestCases(scenariosMap, projectId, folderId, fie
203
212
  test_cases: testcases.map((tc) => createTestCasePayload(tc, id, folderId, fieldMaps, documentId, booleanFieldId, traceId)),
204
213
  };
205
214
  try {
206
- const resp = await axios.post(BULK_CREATE_URL(projectId, folderId), payload, {
215
+ const resp = await apiClient.post({
216
+ url: BULK_CREATE_URL(projectId, folderId),
207
217
  headers: {
208
- "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
218
+ "API-TOKEN": getBrowserStackAuth(config),
209
219
  "Content-Type": "application/json",
210
220
  },
221
+ body: payload,
211
222
  });
212
223
  results[id] = resp.data;
213
224
  await context.sendNotification({
@@ -239,16 +250,17 @@ export async function bulkCreateTestCases(scenariosMap, projectId, folderId, fie
239
250
  const resultString = `Total of ${testCaseCount} test cases created in ${total} scenarios.`;
240
251
  return resultString;
241
252
  }
242
- export async function projectIdentifierToId(projectId) {
253
+ export async function projectIdentifierToId(projectId, config) {
243
254
  const url = `https://test-management.browserstack.com/api/v1/projects/?q=${projectId}`;
244
- const response = await axios.get(url, {
255
+ const response = await apiClient.get({
256
+ url,
245
257
  headers: {
246
- "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
258
+ "API-TOKEN": getBrowserStackAuth(config),
247
259
  accept: "application/json, text/plain, */*",
248
260
  },
249
261
  });
250
262
  if (response.data.success !== true) {
251
- throw new Error(`Failed to fetch project ID: ${response.statusText}`);
263
+ throw new Error(`Failed to fetch project ID: ${response.statusText || response.status}`);
252
264
  }
253
265
  for (const project of response.data.projects) {
254
266
  if (project.identifier === projectId) {
@@ -257,16 +269,17 @@ export async function projectIdentifierToId(projectId) {
257
269
  }
258
270
  throw new Error(`Project with identifier ${projectId} not found.`);
259
271
  }
260
- export async function testCaseIdentifierToDetails(projectId, testCaseIdentifier) {
272
+ export async function testCaseIdentifierToDetails(projectId, testCaseIdentifier, config) {
261
273
  const url = `https://test-management.browserstack.com/api/v1/projects/${projectId}/test-cases/search?q[query]=${testCaseIdentifier}`;
262
- const response = await axios.get(url, {
274
+ const response = await apiClient.get({
275
+ url,
263
276
  headers: {
264
- "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
277
+ "API-TOKEN": getBrowserStackAuth(config),
265
278
  accept: "application/json, text/plain, */*",
266
279
  },
267
280
  });
268
281
  if (response.data.success !== true) {
269
- throw new Error(`Failed to fetch test case details: ${response.statusText}`);
282
+ throw new Error(`Failed to fetch test case details: ${response.statusText || response.status}`);
270
283
  }
271
284
  // Check if test_cases array exists and has items
272
285
  if (!response.data.test_cases ||
@@ -0,0 +1,5 @@
1
+ export declare const TCG_TRIGGER_URL = "https://test-management.browserstack.com/api/v1/integration/tcg/test-generation/suggest-test-cases";
2
+ export declare const TCG_POLL_URL = "https://test-management.browserstack.com/api/v1/integration/tcg/test-generation/test-cases-polling";
3
+ export declare const FETCH_DETAILS_URL = "https://test-management.browserstack.com/api/v1/integration/tcg/test-generation/fetch-test-case-details";
4
+ export declare const FORM_FIELDS_URL: (projectId: string) => string;
5
+ export declare const BULK_CREATE_URL: (projectId: string, folderId: string) => string;
@@ -0,0 +1,13 @@
1
+ import { DefaultFieldMaps } from "./types.js";
2
+ /**
3
+ * Build mappings for default fields for priority, status, and case type.
4
+ */
5
+ export declare function buildDefaultFieldMaps(defaultFields: any): DefaultFieldMaps;
6
+ /**
7
+ * Find a boolean custom field ID if present.
8
+ */
9
+ export declare function findBooleanFieldId(customFields: any[]): number | undefined;
10
+ /**
11
+ * Construct payload for creating a single test case in bulk.
12
+ */
13
+ export declare function createTestCasePayload(tc: any, scenarioId: string, folderId: string, fieldMaps: DefaultFieldMaps, documentId: number, booleanFieldId?: number, traceId?: string): Record<string, any>;
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  /**
2
3
  * Build mappings for default fields for priority, status, and case type.
3
4
  */
@@ -41,7 +42,7 @@ export function createTestCasePayload(tc, scenarioId, folderId, fieldMaps, docum
41
42
  rich_text_id: null,
42
43
  scenario: scenarioId,
43
44
  test_case_count: tc.test_case_count || 1,
44
- uuid: tc.uuid || crypto.randomUUID?.() || "unknown-uuid",
45
+ uuid: tc.uuid || randomUUID() || "unknown-uuid",
45
46
  "x-bstack-traceRequestId": traceId,
46
47
  },
47
48
  }),
@@ -0,0 +1,26 @@
1
+ import { z } from "zod";
2
+ export declare const CreateTestCasesFromFileSchema: z.ZodObject<{
3
+ documentId: z.ZodString;
4
+ folderId: z.ZodString;
5
+ projectReferenceId: z.ZodString;
6
+ }, "strip", z.ZodTypeAny, {
7
+ documentId: string;
8
+ folderId: string;
9
+ projectReferenceId: string;
10
+ }, {
11
+ documentId: string;
12
+ folderId: string;
13
+ projectReferenceId: string;
14
+ }>;
15
+ export type CreateTestCasesFromFileArgs = z.infer<typeof CreateTestCasesFromFileSchema>;
16
+ export interface DefaultFieldMaps {
17
+ priority: Record<string, number>;
18
+ status: Record<string, number>;
19
+ caseType: Record<string, number>;
20
+ }
21
+ export interface Scenario {
22
+ id: string;
23
+ name: string;
24
+ testcases: any[];
25
+ traceId?: string;
26
+ }
@@ -0,0 +1,42 @@
1
+ import { z } from "zod";
2
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
+ import { BrowserStackConfig } from "../../lib/types.js";
4
+ /**
5
+ * Schema for adding a test result to a test run.
6
+ */
7
+ export declare const AddTestResultSchema: z.ZodObject<{
8
+ project_identifier: z.ZodString;
9
+ test_run_id: z.ZodString;
10
+ test_result: z.ZodObject<{
11
+ status: z.ZodString;
12
+ description: z.ZodOptional<z.ZodString>;
13
+ }, "strip", z.ZodTypeAny, {
14
+ status: string;
15
+ description?: string | undefined;
16
+ }, {
17
+ status: string;
18
+ description?: string | undefined;
19
+ }>;
20
+ test_case_id: z.ZodString;
21
+ }, "strip", z.ZodTypeAny, {
22
+ project_identifier: string;
23
+ test_run_id: string;
24
+ test_result: {
25
+ status: string;
26
+ description?: string | undefined;
27
+ };
28
+ test_case_id: string;
29
+ }, {
30
+ project_identifier: string;
31
+ test_run_id: string;
32
+ test_result: {
33
+ status: string;
34
+ description?: string | undefined;
35
+ };
36
+ test_case_id: string;
37
+ }>;
38
+ export type AddTestResultArgs = z.infer<typeof AddTestResultSchema>;
39
+ /**
40
+ * Adds a test result to a specific test run via BrowserStack Test Management API.
41
+ */
42
+ export declare function addTestResult(rawArgs: AddTestResultArgs, config: BrowserStackConfig): Promise<CallToolResult>;
@@ -1,7 +1,6 @@
1
- import axios from "axios";
2
- import config from "../../config.js";
1
+ import { apiClient } from "../../lib/apiClient.js";
2
+ import { getBrowserStackAuth } from "../../lib/get-auth.js";
3
3
  import { z } from "zod";
4
- import { formatAxiosError } from "../../lib/error.js";
5
4
  /**
6
5
  * Schema for adding a test result to a test run.
7
6
  */
@@ -26,7 +25,7 @@ export const AddTestResultSchema = z.object({
26
25
  /**
27
26
  * Adds a test result to a specific test run via BrowserStack Test Management API.
28
27
  */
29
- export async function addTestResult(rawArgs) {
28
+ export async function addTestResult(rawArgs, config) {
30
29
  try {
31
30
  const args = AddTestResultSchema.parse(rawArgs);
32
31
  const url = `https://test-management.browserstack.com/api/v2/projects/${encodeURIComponent(args.project_identifier)}/test-runs/${encodeURIComponent(args.test_run_id)}/results`;
@@ -34,12 +33,15 @@ export async function addTestResult(rawArgs) {
34
33
  test_result: args.test_result,
35
34
  test_case_id: args.test_case_id,
36
35
  };
37
- const response = await axios.post(url, body, {
38
- auth: {
39
- username: config.browserstackUsername,
40
- password: config.browserstackAccessKey,
36
+ const authString = getBrowserStackAuth(config);
37
+ const [username, password] = authString.split(":");
38
+ const response = await apiClient.post({
39
+ url,
40
+ headers: {
41
+ "Content-Type": "application/json",
42
+ Authorization: "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
41
43
  },
42
- headers: { "Content-Type": "application/json" },
44
+ body,
43
45
  });
44
46
  const data = response.data;
45
47
  if (!data.success) {
@@ -56,6 +58,17 @@ export async function addTestResult(rawArgs) {
56
58
  };
57
59
  }
58
60
  catch (err) {
59
- return formatAxiosError(err, "Failed to add test result to test run");
61
+ const msg = err?.response?.data?.message ||
62
+ err?.response?.data?.error ||
63
+ err?.message ||
64
+ String(err);
65
+ return {
66
+ content: [
67
+ {
68
+ type: "text",
69
+ text: `Failed to add test result to test run: ${msg}`,
70
+ },
71
+ ],
72
+ };
60
73
  }
61
74
  }
@@ -0,0 +1,100 @@
1
+ import { z } from "zod";
2
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
+ import { BrowserStackConfig } from "../../lib/types.js";
4
+ /**
5
+ * Schema for creating LCA steps for a test case
6
+ */
7
+ export declare const CreateLCAStepsSchema: z.ZodObject<{
8
+ project_identifier: z.ZodString;
9
+ test_case_identifier: z.ZodString;
10
+ base_url: z.ZodString;
11
+ credentials: z.ZodOptional<z.ZodObject<{
12
+ username: z.ZodString;
13
+ password: z.ZodString;
14
+ }, "strip", z.ZodTypeAny, {
15
+ username: string;
16
+ password: string;
17
+ }, {
18
+ username: string;
19
+ password: string;
20
+ }>>;
21
+ local_enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
22
+ test_name: z.ZodString;
23
+ test_case_details: z.ZodObject<{
24
+ name: z.ZodString;
25
+ description: z.ZodString;
26
+ preconditions: z.ZodString;
27
+ test_case_steps: z.ZodArray<z.ZodObject<{
28
+ step: z.ZodString;
29
+ result: z.ZodString;
30
+ }, "strip", z.ZodTypeAny, {
31
+ step: string;
32
+ result: string;
33
+ }, {
34
+ step: string;
35
+ result: string;
36
+ }>, "many">;
37
+ }, "strip", z.ZodTypeAny, {
38
+ name: string;
39
+ description: string;
40
+ test_case_steps: {
41
+ step: string;
42
+ result: string;
43
+ }[];
44
+ preconditions: string;
45
+ }, {
46
+ name: string;
47
+ description: string;
48
+ test_case_steps: {
49
+ step: string;
50
+ result: string;
51
+ }[];
52
+ preconditions: string;
53
+ }>;
54
+ wait_for_completion: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
55
+ }, "strip", z.ZodTypeAny, {
56
+ project_identifier: string;
57
+ test_case_identifier: string;
58
+ base_url: string;
59
+ local_enabled: boolean;
60
+ test_name: string;
61
+ test_case_details: {
62
+ name: string;
63
+ description: string;
64
+ test_case_steps: {
65
+ step: string;
66
+ result: string;
67
+ }[];
68
+ preconditions: string;
69
+ };
70
+ wait_for_completion: boolean;
71
+ credentials?: {
72
+ username: string;
73
+ password: string;
74
+ } | undefined;
75
+ }, {
76
+ project_identifier: string;
77
+ test_case_identifier: string;
78
+ base_url: string;
79
+ test_name: string;
80
+ test_case_details: {
81
+ name: string;
82
+ description: string;
83
+ test_case_steps: {
84
+ step: string;
85
+ result: string;
86
+ }[];
87
+ preconditions: string;
88
+ };
89
+ credentials?: {
90
+ username: string;
91
+ password: string;
92
+ } | undefined;
93
+ local_enabled?: boolean | undefined;
94
+ wait_for_completion?: boolean | undefined;
95
+ }>;
96
+ export type CreateLCAStepsArgs = z.infer<typeof CreateLCAStepsSchema>;
97
+ /**
98
+ * Creates LCA (Low Code Automation) steps for a test case in BrowserStack Test Management
99
+ */
100
+ export declare function createLCASteps(args: CreateLCAStepsArgs, context: any, config: BrowserStackConfig): Promise<CallToolResult>;
@@ -1,9 +1,8 @@
1
1
  import { z } from "zod";
2
- import axios from "axios";
3
- import config from "../../config.js";
4
- import { formatAxiosError } from "../../lib/error.js";
2
+ import { apiClient } from "../../lib/apiClient.js";
5
3
  import { projectIdentifierToId, testCaseIdentifierToDetails, } from "./TCG-utils/api.js";
6
4
  import { pollLCAStatus } from "./poll-lca-status.js";
5
+ import { getBrowserStackAuth } from "../../lib/get-auth.js";
7
6
  /**
8
7
  * Schema for creating LCA steps for a test case
9
8
  */
@@ -50,12 +49,12 @@ export const CreateLCAStepsSchema = z.object({
50
49
  /**
51
50
  * Creates LCA (Low Code Automation) steps for a test case in BrowserStack Test Management
52
51
  */
53
- export async function createLCASteps(args, context) {
52
+ export async function createLCASteps(args, context, config) {
54
53
  try {
55
54
  // Get the project ID from identifier
56
- const projectId = await projectIdentifierToId(args.project_identifier);
55
+ const projectId = await projectIdentifierToId(args.project_identifier, config);
57
56
  // Get the test case ID and folder ID from identifier
58
- const { testCaseId, folderId } = await testCaseIdentifierToDetails(projectId, args.test_case_identifier);
57
+ const { testCaseId, folderId } = await testCaseIdentifierToDetails(projectId, args.test_case_identifier, config);
59
58
  const url = `https://test-management.browserstack.com/api/v1/projects/${projectId}/test-cases/${testCaseId}/lcnc`;
60
59
  const payload = {
61
60
  base_url: args.base_url,
@@ -66,67 +65,53 @@ export async function createLCASteps(args, context) {
66
65
  version: "v2",
67
66
  webhook_path: `https://test-management.browserstack.com/api/v1/projects/${projectId}/test-cases/${testCaseId}/webhooks/lcnc`,
68
67
  };
69
- const response = await axios.post(url, payload, {
68
+ await apiClient.post({
69
+ url,
70
70
  headers: {
71
- "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
71
+ "API-TOKEN": getBrowserStackAuth(config),
72
72
  accept: "application/json, text/plain, */*",
73
73
  "Content-Type": "application/json",
74
74
  },
75
+ body: payload,
75
76
  });
76
- if (response.status >= 200 && response.status < 300) {
77
- // Check if user wants to wait for completion
78
- if (!args.wait_for_completion) {
77
+ // Check if user wants to wait for completion
78
+ if (!args.wait_for_completion) {
79
+ return {
80
+ content: [
81
+ {
82
+ type: "text",
83
+ text: `LCA steps creation initiated for test case ${args.test_case_identifier} (ID: ${testCaseId})`,
84
+ },
85
+ {
86
+ type: "text",
87
+ text: "LCA build started. Check the BrowserStack Lowcode Automation UI for completion status.",
88
+ },
89
+ ],
90
+ };
91
+ }
92
+ // Start polling for LCA build completion
93
+ try {
94
+ const max_wait_minutes = 10; // Maximum wait time in minutes
95
+ const maxWaitMs = max_wait_minutes * 60 * 1000;
96
+ const lcaResult = await pollLCAStatus(projectId, folderId, testCaseId, context, maxWaitMs, // max wait time
97
+ 2 * 60 * 1000, // 2 minutes initial wait
98
+ 10 * 1000, // 10 seconds interval
99
+ config);
100
+ if (lcaResult && lcaResult.status === "done") {
79
101
  return {
80
102
  content: [
81
103
  {
82
104
  type: "text",
83
- text: `LCA steps creation initiated for test case ${args.test_case_identifier} (ID: ${testCaseId})`,
105
+ text: `Successfully created LCA steps for test case ${args.test_case_identifier} (ID: ${testCaseId})`,
84
106
  },
85
107
  {
86
108
  type: "text",
87
- text: "LCA build started. Check the BrowserStack Lowcode Automation UI for completion status.",
109
+ text: `LCA build completed! Resource URL: ${lcaResult.resource_path}`,
88
110
  },
89
111
  ],
90
112
  };
91
113
  }
92
- // Start polling for LCA build completion
93
- try {
94
- const max_wait_minutes = 10; // Maximum wait time in minutes
95
- const maxWaitMs = max_wait_minutes * 60 * 1000;
96
- const lcaResult = await pollLCAStatus(projectId, folderId, testCaseId, context, maxWaitMs, // max wait time
97
- 2 * 60 * 1000, // 2 minutes initial wait
98
- 10 * 1000);
99
- if (lcaResult && lcaResult.status === "done") {
100
- return {
101
- content: [
102
- {
103
- type: "text",
104
- text: `Successfully created LCA steps for test case ${args.test_case_identifier} (ID: ${testCaseId})`,
105
- },
106
- {
107
- type: "text",
108
- text: `LCA build completed! Resource URL: ${lcaResult.resource_path}`,
109
- },
110
- ],
111
- };
112
- }
113
- else {
114
- return {
115
- content: [
116
- {
117
- type: "text",
118
- text: `LCA steps creation initiated for test case ${args.test_case_identifier} (ID: ${testCaseId})`,
119
- },
120
- {
121
- type: "text",
122
- text: `Warning: LCA build did not complete within ${max_wait_minutes} minutes. You can check the status later in the BrowserStack Test Management UI.`,
123
- },
124
- ],
125
- };
126
- }
127
- }
128
- catch (pollError) {
129
- console.error("Error during LCA polling:", pollError);
114
+ else {
130
115
  return {
131
116
  content: [
132
117
  {
@@ -135,14 +120,26 @@ export async function createLCASteps(args, context) {
135
120
  },
136
121
  {
137
122
  type: "text",
138
- text: "Warning: Error occurred while polling for LCA build completion. Check the BrowserStack Test Management UI for status.",
123
+ text: `Warning: LCA build did not complete within ${max_wait_minutes} minutes. You can check the status later in the BrowserStack Test Management UI.`,
139
124
  },
140
125
  ],
141
126
  };
142
127
  }
143
128
  }
144
- else {
145
- throw new Error(`Unexpected response: ${JSON.stringify(response.data)}`);
129
+ catch (pollError) {
130
+ console.error("Error during LCA polling:", pollError);
131
+ return {
132
+ content: [
133
+ {
134
+ type: "text",
135
+ text: `LCA steps creation initiated for test case ${args.test_case_identifier} (ID: ${testCaseId})`,
136
+ },
137
+ {
138
+ type: "text",
139
+ text: "Warning: Error occurred while polling for LCA build completion. Check the BrowserStack Test Management UI for status.",
140
+ },
141
+ ],
142
+ };
146
143
  }
147
144
  }
148
145
  catch (error) {
@@ -161,6 +158,18 @@ export async function createLCASteps(args, context) {
161
158
  };
162
159
  }
163
160
  }
164
- return formatAxiosError(error, "Failed to create LCA steps");
161
+ const msg = error?.response?.data?.message ||
162
+ error?.response?.data?.error ||
163
+ error?.message ||
164
+ String(error);
165
+ return {
166
+ content: [
167
+ {
168
+ type: "text",
169
+ text: `Failed to create LCA steps: ${msg}`,
170
+ },
171
+ ],
172
+ isError: true,
173
+ };
165
174
  }
166
175
  }