@browserstack/mcp-server 1.0.13 → 1.0.15

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 (55) hide show
  1. package/README.md +22 -0
  2. package/dist/config.js +2 -6
  3. package/dist/index.js +29 -32
  4. package/dist/lib/api.js +3 -57
  5. package/dist/lib/constants.js +14 -0
  6. package/dist/lib/device-cache.js +32 -33
  7. package/dist/lib/error.js +3 -6
  8. package/dist/lib/fuzzy.js +1 -4
  9. package/dist/lib/inmemory-store.js +1 -0
  10. package/dist/lib/instrumentation.js +12 -18
  11. package/dist/lib/local.js +27 -35
  12. package/dist/lib/utils.js +29 -4
  13. package/dist/logger.js +4 -9
  14. package/dist/tools/accessibility.js +51 -21
  15. package/dist/tools/accessiblity-utils/report-fetcher.js +28 -0
  16. package/dist/tools/accessiblity-utils/report-parser.js +51 -0
  17. package/dist/tools/accessiblity-utils/scanner.js +80 -0
  18. package/dist/tools/appautomate-utils/appautomate.js +95 -0
  19. package/dist/tools/appautomate.js +116 -0
  20. package/dist/tools/applive-utils/fuzzy-search.js +3 -6
  21. package/dist/tools/applive-utils/start-session.js +14 -20
  22. package/dist/tools/applive-utils/upload-app.js +12 -51
  23. package/dist/tools/applive.js +18 -25
  24. package/dist/tools/automate-utils/fetch-screenshots.js +59 -0
  25. package/dist/tools/automate.js +44 -37
  26. package/dist/tools/bstack-sdk.js +14 -18
  27. package/dist/tools/failurelogs-utils/app-automate.js +88 -0
  28. package/dist/tools/failurelogs-utils/automate.js +97 -0
  29. package/dist/tools/getFailureLogs.js +173 -0
  30. package/dist/tools/live-utils/desktop-filter.js +8 -11
  31. package/dist/tools/live-utils/mobile-filter.js +7 -10
  32. package/dist/tools/live-utils/start-session.js +17 -23
  33. package/dist/tools/live-utils/types.js +2 -5
  34. package/dist/tools/live-utils/version-resolver.js +1 -4
  35. package/dist/tools/live.js +23 -29
  36. package/dist/tools/observability.js +12 -19
  37. package/dist/tools/sdk-utils/constants.js +3 -9
  38. package/dist/tools/sdk-utils/instructions.js +4 -9
  39. package/dist/tools/sdk-utils/types.js +1 -2
  40. package/dist/tools/testmanagement-utils/TCG-utils/api.js +259 -0
  41. package/dist/tools/testmanagement-utils/TCG-utils/config.js +5 -0
  42. package/dist/tools/testmanagement-utils/TCG-utils/helpers.js +53 -0
  43. package/dist/tools/testmanagement-utils/TCG-utils/types.js +8 -0
  44. package/dist/tools/testmanagement-utils/add-test-result.js +18 -25
  45. package/dist/tools/testmanagement-utils/create-project-folder.js +21 -28
  46. package/dist/tools/testmanagement-utils/create-testcase.js +30 -38
  47. package/dist/tools/testmanagement-utils/create-testrun.js +23 -30
  48. package/dist/tools/testmanagement-utils/list-testcases.js +16 -23
  49. package/dist/tools/testmanagement-utils/list-testruns.js +13 -20
  50. package/dist/tools/testmanagement-utils/testcase-from-file.js +42 -0
  51. package/dist/tools/testmanagement-utils/update-testrun.js +16 -23
  52. package/dist/tools/testmanagement-utils/upload-file.js +101 -0
  53. package/dist/tools/testmanagement.js +98 -61
  54. package/package.json +13 -7
  55. package/dist/tools/accessiblity-utils/accessibility.js +0 -82
@@ -0,0 +1,259 @@
1
+ import axios from "axios";
2
+ import { TCG_TRIGGER_URL, TCG_POLL_URL, FETCH_DETAILS_URL, FORM_FIELDS_URL, BULK_CREATE_URL, } from "./config.js";
3
+ import { createTestCasePayload } from "./helpers.js";
4
+ import config from "../../../config.js";
5
+ /**
6
+ * Fetch default and custom form fields for a project.
7
+ */
8
+ export async function fetchFormFields(projectId) {
9
+ const res = await axios.get(FORM_FIELDS_URL(projectId), {
10
+ headers: {
11
+ "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
12
+ },
13
+ });
14
+ return res.data;
15
+ }
16
+ /**
17
+ * Trigger AI-based test case generation for a document.
18
+ */
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
+ }, {
28
+ headers: {
29
+ "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
30
+ "Content-Type": "application/json",
31
+ "request-source": source,
32
+ },
33
+ });
34
+ if (res.status !== 200) {
35
+ throw new Error(`Trigger failed: ${res.statusText}`);
36
+ }
37
+ return res.data["x-bstack-traceRequestId"];
38
+ }
39
+ /**
40
+ * Initiate a fetch for test-case details; returns the traceRequestId for polling.
41
+ */
42
+ export async function fetchTestCaseDetails(documentId, folderId, projectId, testCaseIds, source) {
43
+ if (testCaseIds.length === 0) {
44
+ throw new Error("No testCaseIds provided to fetchTestCaseDetails");
45
+ }
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
+ }, {
52
+ headers: {
53
+ "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
54
+ "request-source": source,
55
+ "Content-Type": "application/json",
56
+ },
57
+ });
58
+ if (res.data.data.success !== true) {
59
+ throw new Error(`Fetch details failed: ${res.data.data.message}`);
60
+ }
61
+ return res.data.request_trace_id;
62
+ }
63
+ /**
64
+ * Poll for a given traceRequestId until all test-case details are returned.
65
+ */
66
+ export async function pollTestCaseDetails(traceRequestId) {
67
+ const detailMap = {};
68
+ let done = false;
69
+ while (!done) {
70
+ // add a bit of jitter to avoid synchronized polling storms
71
+ await new Promise((r) => setTimeout(r, 10000 + Math.random() * 5000));
72
+ const poll = await axios.post(`${TCG_POLL_URL}?x-bstack-traceRequestId=${encodeURIComponent(traceRequestId)}`, {}, {
73
+ headers: {
74
+ "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
75
+ },
76
+ });
77
+ if (!poll.data.data.success) {
78
+ throw new Error(`Polling failed: ${poll.data.data.message}`);
79
+ }
80
+ for (const msg of poll.data.data.message) {
81
+ if (msg.type === "termination") {
82
+ done = true;
83
+ }
84
+ if (msg.type === "testcase_details") {
85
+ for (const test of msg.data.testcase_details) {
86
+ detailMap[test.id] = {
87
+ steps: test.steps,
88
+ preconditions: test.preconditions,
89
+ };
90
+ }
91
+ }
92
+ }
93
+ }
94
+ return detailMap;
95
+ }
96
+ /**
97
+ * Poll for scenarios & testcases, trigger detail fetches, then poll all details in parallel.
98
+ */
99
+ export async function pollScenariosTestDetails(args, traceId, context, documentId, source) {
100
+ const { folderId, projectReferenceId } = args;
101
+ const scenariosMap = {};
102
+ const detailPromises = [];
103
+ let iteratorCount = 0;
104
+ // Promisify interval-style polling using a wrapper
105
+ await new Promise((resolve, reject) => {
106
+ const intervalId = setInterval(async () => {
107
+ try {
108
+ const poll = await axios.post(`${TCG_POLL_URL}?x-bstack-traceRequestId=${encodeURIComponent(traceId)}`, {}, {
109
+ headers: {
110
+ "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
111
+ },
112
+ });
113
+ if (poll.status !== 200) {
114
+ clearInterval(intervalId);
115
+ reject(new Error(`Polling error: ${poll.statusText}`));
116
+ return;
117
+ }
118
+ for (const msg of poll.data.data.message) {
119
+ if (msg.type === "scenario") {
120
+ msg.data.scenarios.forEach((sc) => {
121
+ scenariosMap[sc.id] = { id: sc.id, name: sc.name, testcases: [] };
122
+ });
123
+ const count = Object.keys(scenariosMap).length;
124
+ await context.sendNotification({
125
+ method: "notifications/progress",
126
+ params: {
127
+ progressToken: context._meta?.progressToken ?? traceId,
128
+ progress: count,
129
+ total: count,
130
+ message: `Fetched ${count} scenarios`,
131
+ },
132
+ });
133
+ }
134
+ if (msg.type === "testcase") {
135
+ const sc = msg.data.scenario;
136
+ if (sc) {
137
+ const array = Array.isArray(msg.data.testcases)
138
+ ? msg.data.testcases
139
+ : msg.data.testcases
140
+ ? [msg.data.testcases]
141
+ : [];
142
+ 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));
145
+ scenariosMap[sc.id] ||= {
146
+ id: sc.id,
147
+ name: sc.name,
148
+ testcases: [],
149
+ traceId,
150
+ };
151
+ scenariosMap[sc.id].testcases.push(...array);
152
+ iteratorCount++;
153
+ const total = Object.keys(scenariosMap).length;
154
+ await context.sendNotification({
155
+ method: "notifications/progress",
156
+ params: {
157
+ progressToken: context._meta?.progressToken ?? traceId,
158
+ progress: iteratorCount,
159
+ total,
160
+ message: `Fetched ${array.length} test cases for scenario ${iteratorCount} out of ${total}`,
161
+ },
162
+ });
163
+ }
164
+ }
165
+ if (msg.type === "termination") {
166
+ clearInterval(intervalId);
167
+ resolve();
168
+ }
169
+ }
170
+ }
171
+ catch (err) {
172
+ clearInterval(intervalId);
173
+ reject(err);
174
+ }
175
+ }, 10000); // 10 second interval
176
+ });
177
+ // once all detail fetches are triggered, wait for them to complete
178
+ const detailsList = await Promise.all(detailPromises);
179
+ const allDetails = detailsList.reduce((acc, cur) => ({ ...acc, ...cur }), {});
180
+ // attach the fetched detail objects back to each testcase
181
+ for (const scenario of Object.values(scenariosMap)) {
182
+ scenario.testcases = scenario.testcases.map((tc) => ({
183
+ ...tc,
184
+ ...(allDetails[tc.id || tc.test_case_id] ?? {}),
185
+ }));
186
+ }
187
+ return scenariosMap;
188
+ }
189
+ /**
190
+ * Bulk-create generated test cases in BrowserStack.
191
+ */
192
+ export async function bulkCreateTestCases(scenariosMap, projectId, folderId, fieldMaps, booleanFieldId, traceId, context, documentId) {
193
+ const results = {};
194
+ const total = Object.keys(scenariosMap).length;
195
+ let doneCount = 0;
196
+ let testCaseCount = 0;
197
+ for (const { id, testcases } of Object.values(scenariosMap)) {
198
+ const testCaseLength = testcases.length;
199
+ testCaseCount += testCaseLength;
200
+ if (testCaseLength === 0)
201
+ continue;
202
+ const payload = {
203
+ test_cases: testcases.map((tc) => createTestCasePayload(tc, id, folderId, fieldMaps, documentId, booleanFieldId, traceId)),
204
+ };
205
+ try {
206
+ const resp = await axios.post(BULK_CREATE_URL(projectId, folderId), payload, {
207
+ headers: {
208
+ "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
209
+ "Content-Type": "application/json",
210
+ },
211
+ });
212
+ results[id] = resp.data;
213
+ await context.sendNotification({
214
+ method: "notifications/progress",
215
+ params: {
216
+ progressToken: context._meta?.progressToken ?? "bulk-create",
217
+ message: `Bulk create done for scenario ${doneCount} of ${total}`,
218
+ total,
219
+ progress: doneCount,
220
+ },
221
+ });
222
+ }
223
+ catch (error) {
224
+ //send notification
225
+ await context.sendNotification({
226
+ method: "notifications/progress",
227
+ params: {
228
+ progressToken: context._meta?.progressToken ?? traceId,
229
+ message: `Bulk create failed for scenario ${id}: ${error instanceof Error ? error.message : "Unknown error"}`,
230
+ total,
231
+ progress: doneCount,
232
+ },
233
+ });
234
+ //continue to next scenario
235
+ continue;
236
+ }
237
+ doneCount++;
238
+ }
239
+ const resultString = `Total of ${testCaseCount} test cases created in ${total} scenarios.`;
240
+ return resultString;
241
+ }
242
+ export async function projectIdentifierToId(projectId) {
243
+ const url = `https://test-management.browserstack.com/api/v1/projects/?q=${projectId}`;
244
+ const response = await axios.get(url, {
245
+ headers: {
246
+ "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
247
+ accept: "application/json, text/plain, */*",
248
+ },
249
+ });
250
+ if (response.data.success !== true) {
251
+ throw new Error(`Failed to fetch project ID: ${response.statusText}`);
252
+ }
253
+ for (const project of response.data.projects) {
254
+ if (project.identifier === projectId) {
255
+ return project.id;
256
+ }
257
+ }
258
+ throw new Error(`Project with identifier ${projectId} not found.`);
259
+ }
@@ -0,0 +1,5 @@
1
+ export const TCG_TRIGGER_URL = "https://test-management.browserstack.com/api/v1/integration/tcg/test-generation/suggest-test-cases";
2
+ export const TCG_POLL_URL = "https://test-management.browserstack.com/api/v1/integration/tcg/test-generation/test-cases-polling";
3
+ export const FETCH_DETAILS_URL = "https://test-management.browserstack.com/api/v1/integration/tcg/test-generation/fetch-test-case-details";
4
+ export const FORM_FIELDS_URL = (projectId) => `https://test-management.browserstack.com/api/v1/projects/${projectId}/form-fields-v2`;
5
+ export const BULK_CREATE_URL = (projectId, folderId) => `https://test-management.browserstack.com/api/v1/projects/${projectId}/folder/${folderId}/bulk-test-cases`;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Build mappings for default fields for priority, status, and case type.
3
+ */
4
+ export function buildDefaultFieldMaps(defaultFields) {
5
+ const priority = Object.fromEntries(defaultFields.priority.values.map((v) => [
6
+ v.name.toLowerCase(),
7
+ v.value,
8
+ ]));
9
+ const status = Object.fromEntries(defaultFields.status.values.map((v) => [v.internal_name, v.value]));
10
+ const caseType = Object.fromEntries(defaultFields.case_type.values.map((v) => [v.internal_name, v.value]));
11
+ return { priority, status, caseType };
12
+ }
13
+ /**
14
+ * Find a boolean custom field ID if present.
15
+ */
16
+ export function findBooleanFieldId(customFields) {
17
+ const boolField = customFields.find((f) => f.field_type === "field_boolean");
18
+ return boolField?.id;
19
+ }
20
+ /**
21
+ * Construct payload for creating a single test case in bulk.
22
+ */
23
+ export function createTestCasePayload(tc, scenarioId, folderId, fieldMaps, documentId, booleanFieldId, traceId) {
24
+ const pri = tc.priority ?? "Medium";
25
+ const stat = fieldMaps.status["active"];
26
+ const ct = fieldMaps.caseType["functional"];
27
+ return {
28
+ attachments: [documentId],
29
+ name: tc.name,
30
+ description: tc.description,
31
+ test_case_folder_id: folderId,
32
+ priority: pri,
33
+ status: stat,
34
+ case_type: ct,
35
+ automation_status: "not_automated",
36
+ fetch_ai_test_case_details: true,
37
+ template: "test_case_steps",
38
+ metadata: JSON.stringify({
39
+ ai_prompt: {
40
+ attachment_id: documentId,
41
+ rich_text_id: null,
42
+ scenario: scenarioId,
43
+ test_case_count: tc.test_case_count || 1,
44
+ uuid: tc.uuid || crypto.randomUUID?.() || "unknown-uuid",
45
+ "x-bstack-traceRequestId": traceId,
46
+ },
47
+ }),
48
+ tags: ["AI Generated", "MCP Generated"],
49
+ custom_fields: booleanFieldId ? { [booleanFieldId]: false } : undefined,
50
+ test_case_steps: tc.steps,
51
+ preconditions: tc.preconditions,
52
+ };
53
+ }
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ export const CreateTestCasesFromFileSchema = z.object({
3
+ documentId: z.string().describe("Internal document identifier"),
4
+ folderId: z.string().describe("BrowserStack folder ID"),
5
+ projectReferenceId: z
6
+ .string()
7
+ .describe("The BrowserStack project reference ID is a unique identifier found in the project URL within the BrowserStack Test Management Platform. This ID is also returned by the Upload Document tool."),
8
+ });
@@ -1,50 +1,43 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.AddTestResultSchema = void 0;
7
- exports.addTestResult = addTestResult;
8
- const axios_1 = __importDefault(require("axios"));
9
- const config_1 = __importDefault(require("../../config"));
10
- const zod_1 = require("zod");
11
- const error_1 = require("../../lib/error");
1
+ import axios from "axios";
2
+ import config from "../../config.js";
3
+ import { z } from "zod";
4
+ import { formatAxiosError } from "../../lib/error.js";
12
5
  /**
13
6
  * Schema for adding a test result to a test run.
14
7
  */
15
- exports.AddTestResultSchema = zod_1.z.object({
16
- project_identifier: zod_1.z
8
+ export const AddTestResultSchema = z.object({
9
+ project_identifier: z
17
10
  .string()
18
- .describe("Identifier of the project (e.g., PR-12345)"),
19
- test_run_id: zod_1.z.string().describe("Identifier of the test run (e.g., TR-678)"),
20
- test_result: zod_1.z.object({
21
- status: zod_1.z
11
+ .describe("Identifier of the project (Starts with 'PR-')"),
12
+ test_run_id: z.string().describe("Identifier of the test run (e.g., TR-678)"),
13
+ test_result: z.object({
14
+ status: z
22
15
  .string()
23
16
  .describe("Status of the test result, e.g., 'passed', 'failed'."),
24
- description: zod_1.z
17
+ description: z
25
18
  .string()
26
19
  .optional()
27
20
  .describe("Optional description of the test result."),
28
21
  }),
29
- test_case_id: zod_1.z
22
+ test_case_id: z
30
23
  .string()
31
24
  .describe("Identifier of the test case, e.g., 'TC-13'."),
32
25
  });
33
26
  /**
34
27
  * Adds a test result to a specific test run via BrowserStack Test Management API.
35
28
  */
36
- async function addTestResult(rawArgs) {
29
+ export async function addTestResult(rawArgs) {
37
30
  try {
38
- const args = exports.AddTestResultSchema.parse(rawArgs);
31
+ const args = AddTestResultSchema.parse(rawArgs);
39
32
  const url = `https://test-management.browserstack.com/api/v2/projects/${encodeURIComponent(args.project_identifier)}/test-runs/${encodeURIComponent(args.test_run_id)}/results`;
40
33
  const body = {
41
34
  test_result: args.test_result,
42
35
  test_case_id: args.test_case_id,
43
36
  };
44
- const response = await axios_1.default.post(url, body, {
37
+ const response = await axios.post(url, body, {
45
38
  auth: {
46
- username: config_1.default.browserstackUsername,
47
- password: config_1.default.browserstackAccessKey,
39
+ username: config.browserstackUsername,
40
+ password: config.browserstackAccessKey,
48
41
  },
49
42
  headers: { "Content-Type": "application/json" },
50
43
  });
@@ -63,6 +56,6 @@ async function addTestResult(rawArgs) {
63
56
  };
64
57
  }
65
58
  catch (err) {
66
- return (0, error_1.formatAxiosError)(err, "Failed to add test result to test run");
59
+ return formatAxiosError(err, "Failed to add test result to test run");
67
60
  }
68
61
  }
@@ -1,34 +1,27 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.CreateProjFoldSchema = void 0;
7
- exports.createProjectOrFolder = createProjectOrFolder;
8
- const axios_1 = __importDefault(require("axios"));
9
- const config_1 = __importDefault(require("../../config"));
10
- const zod_1 = require("zod");
11
- const error_1 = require("../../lib/error"); // or correct path
1
+ import axios from "axios";
2
+ import config from "../../config.js";
3
+ import { z } from "zod";
4
+ import { formatAxiosError } from "../../lib/error.js"; // or correct path
12
5
  // Schema for combined project/folder creation
13
- exports.CreateProjFoldSchema = zod_1.z.object({
14
- project_name: zod_1.z
6
+ export const CreateProjFoldSchema = z.object({
7
+ project_name: z
15
8
  .string()
16
9
  .optional()
17
10
  .describe("Name of the project to create."),
18
- project_description: zod_1.z
11
+ project_description: z
19
12
  .string()
20
13
  .optional()
21
14
  .describe("Description for the new project."),
22
- project_identifier: zod_1.z
15
+ project_identifier: z
23
16
  .string()
24
17
  .optional()
25
18
  .describe("Existing project identifier to use for folder creation."),
26
- folder_name: zod_1.z.string().optional().describe("Name of the folder to create."),
27
- folder_description: zod_1.z
19
+ folder_name: z.string().optional().describe("Name of the folder to create."),
20
+ folder_description: z
28
21
  .string()
29
22
  .optional()
30
23
  .describe("Description for the new folder."),
31
- parent_id: zod_1.z
24
+ parent_id: z
32
25
  .number()
33
26
  .optional()
34
27
  .describe("Parent folder ID; if omitted, folder is created at root."),
@@ -36,8 +29,8 @@ exports.CreateProjFoldSchema = zod_1.z.object({
36
29
  /**
37
30
  * Creates a project and/or folder in BrowserStack Test Management.
38
31
  */
39
- async function createProjectOrFolder(args) {
40
- const { project_name, project_description, project_identifier, folder_name, folder_description, parent_id, } = exports.CreateProjFoldSchema.parse(args);
32
+ export async function createProjectOrFolder(args) {
33
+ const { project_name, project_description, project_identifier, folder_name, folder_description, parent_id, } = CreateProjFoldSchema.parse(args);
41
34
  if (!project_name && !project_identifier && !folder_name) {
42
35
  throw new Error("Provide project_name (to create project), or project_identifier and folder_name (to create folder).");
43
36
  }
@@ -45,10 +38,10 @@ async function createProjectOrFolder(args) {
45
38
  // Step 1: Create project if project_name provided
46
39
  if (project_name) {
47
40
  try {
48
- const res = await axios_1.default.post("https://test-management.browserstack.com/api/v2/projects", { project: { name: project_name, description: project_description } }, {
41
+ const res = await axios.post("https://test-management.browserstack.com/api/v2/projects", { project: { name: project_name, description: project_description } }, {
49
42
  auth: {
50
- username: config_1.default.browserstackUsername,
51
- password: config_1.default.browserstackAccessKey,
43
+ username: config.browserstackUsername,
44
+ password: config.browserstackAccessKey,
52
45
  },
53
46
  headers: { "Content-Type": "application/json" },
54
47
  });
@@ -59,7 +52,7 @@ async function createProjectOrFolder(args) {
59
52
  projId = res.data.project.identifier;
60
53
  }
61
54
  catch (err) {
62
- return (0, error_1.formatAxiosError)(err, "Failed to create project..");
55
+ return formatAxiosError(err, "Failed to create project..");
63
56
  }
64
57
  }
65
58
  // Step 2: Create folder if folder_name provided
@@ -67,7 +60,7 @@ async function createProjectOrFolder(args) {
67
60
  if (!projId)
68
61
  throw new Error("Cannot create folder without project_identifier.");
69
62
  try {
70
- const res = await axios_1.default.post(`https://test-management.browserstack.com/api/v2/projects/${encodeURIComponent(projId)}/folders`, {
63
+ const res = await axios.post(`https://test-management.browserstack.com/api/v2/projects/${encodeURIComponent(projId)}/folders`, {
71
64
  folder: {
72
65
  name: folder_name,
73
66
  description: folder_description,
@@ -75,8 +68,8 @@ async function createProjectOrFolder(args) {
75
68
  },
76
69
  }, {
77
70
  auth: {
78
- username: config_1.default.browserstackUsername,
79
- password: config_1.default.browserstackAccessKey,
71
+ username: config.browserstackUsername,
72
+ password: config.browserstackAccessKey,
80
73
  },
81
74
  headers: { "Content-Type": "application/json" },
82
75
  });
@@ -95,7 +88,7 @@ async function createProjectOrFolder(args) {
95
88
  };
96
89
  }
97
90
  catch (err) {
98
- return (0, error_1.formatAxiosError)(err, "Failed to create folder.");
91
+ return formatAxiosError(err, "Failed to create folder.");
99
92
  }
100
93
  }
101
94
  // Only project was created
@@ -1,64 +1,56 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.CreateTestCaseSchema = void 0;
7
- exports.sanitizeArgs = sanitizeArgs;
8
- exports.createTestCase = createTestCase;
9
- const axios_1 = __importDefault(require("axios"));
10
- const config_1 = __importDefault(require("../../config"));
11
- const zod_1 = require("zod");
12
- const error_1 = require("../../lib/error"); // or correct
13
- exports.CreateTestCaseSchema = zod_1.z.object({
14
- project_identifier: zod_1.z
1
+ import axios from "axios";
2
+ import config from "../../config.js";
3
+ import { z } from "zod";
4
+ import { formatAxiosError } from "../../lib/error.js"; // or correct
5
+ export const CreateTestCaseSchema = z.object({
6
+ project_identifier: z
15
7
  .string()
16
8
  .describe("The ID of the BrowserStack project where the test case should be created. If no project identifier is provided, ask the user if they would like to create a new project using the createProjectOrFolder tool."),
17
- folder_id: zod_1.z
9
+ folder_id: z
18
10
  .string()
19
11
  .describe("The ID of the folder within the project where the test case should be created. If not provided, ask the user if they would like to create a new folder using the createProjectOrFolder tool."),
20
- name: zod_1.z.string().describe("Name of the test case."),
21
- description: zod_1.z
12
+ name: z.string().describe("Name of the test case."),
13
+ description: z
22
14
  .string()
23
15
  .optional()
24
16
  .describe("Brief description of the test case."),
25
- owner: zod_1.z
17
+ owner: z
26
18
  .string()
27
19
  .email()
28
20
  .describe("Email of the test case owner.")
29
21
  .optional(),
30
- preconditions: zod_1.z
22
+ preconditions: z
31
23
  .string()
32
24
  .optional()
33
25
  .describe("Any preconditions (HTML allowed)."),
34
- test_case_steps: zod_1.z
35
- .array(zod_1.z.object({
36
- step: zod_1.z.string().describe("Action to perform in this step."),
37
- result: zod_1.z.string().describe("Expected result of this step."),
26
+ test_case_steps: z
27
+ .array(z.object({
28
+ step: z.string().describe("Action to perform in this step."),
29
+ result: z.string().describe("Expected result of this step."),
38
30
  }))
39
31
  .describe("List of steps and expected results."),
40
- issues: zod_1.z
41
- .array(zod_1.z.string())
32
+ issues: z
33
+ .array(z.string())
42
34
  .optional()
43
35
  .describe("List of the linked Jira, Asana or Azure issues ID's. This should be strictly in array format not the string of json."),
44
- issue_tracker: zod_1.z
36
+ issue_tracker: z
45
37
  .object({
46
- name: zod_1.z
38
+ name: z
47
39
  .string()
48
40
  .describe("Issue tracker name, For example, use jira for Jira, azure for Azure DevOps, or asana for Asana."),
49
- host: zod_1.z.string().url().describe("Base URL of the issue tracker."),
41
+ host: z.string().url().describe("Base URL of the issue tracker."),
50
42
  })
51
43
  .optional(),
52
- tags: zod_1.z
53
- .array(zod_1.z.string())
44
+ tags: z
45
+ .array(z.string())
54
46
  .optional()
55
47
  .describe("Tags to attach to the test case. This should be strictly in array format not the string of json"),
56
- custom_fields: zod_1.z
57
- .record(zod_1.z.string(), zod_1.z.string())
48
+ custom_fields: z
49
+ .record(z.string(), z.string())
58
50
  .optional()
59
51
  .describe("Map of custom field names to values."),
60
52
  });
61
- function sanitizeArgs(args) {
53
+ export function sanitizeArgs(args) {
62
54
  const cleaned = { ...args };
63
55
  if (cleaned.description === null)
64
56
  delete cleaned.description;
@@ -74,13 +66,13 @@ function sanitizeArgs(args) {
74
66
  }
75
67
  return cleaned;
76
68
  }
77
- async function createTestCase(params) {
69
+ export async function createTestCase(params) {
78
70
  const body = { test_case: params };
79
71
  try {
80
- const response = await axios_1.default.post(`https://test-management.browserstack.com/api/v2/projects/${encodeURIComponent(params.project_identifier)}/folders/${encodeURIComponent(params.folder_id)}/test-cases`, body, {
72
+ const response = await axios.post(`https://test-management.browserstack.com/api/v2/projects/${encodeURIComponent(params.project_identifier)}/folders/${encodeURIComponent(params.folder_id)}/test-cases`, body, {
81
73
  auth: {
82
- username: config_1.default.browserstackUsername,
83
- password: config_1.default.browserstackAccessKey,
74
+ username: config.browserstackUsername,
75
+ password: config.browserstackAccessKey,
84
76
  },
85
77
  headers: { "Content-Type": "application/json" },
86
78
  });
@@ -110,6 +102,6 @@ async function createTestCase(params) {
110
102
  }
111
103
  catch (err) {
112
104
  // Delegate to our centralized Axios error formatter
113
- return (0, error_1.formatAxiosError)(err, "Failed to create test case");
105
+ return formatAxiosError(err, "Failed to create test case");
114
106
  }
115
107
  }