@browserstack/mcp-server 1.1.8 → 1.2.0

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 (125) hide show
  1. package/README.md +71 -35
  2. package/dist/config.d.ts +13 -0
  3. package/dist/config.js +10 -6
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.js +20 -30
  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 +12 -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 +3 -0
  31. package/dist/server-factory.js +37 -0
  32. package/dist/tools/accessibility.d.ts +3 -0
  33. package/dist/tools/accessibility.js +17 -10
  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 +46 -24
  42. package/dist/tools/appautomate-utils/appautomate.d.ts +42 -0
  43. package/dist/tools/appautomate-utils/appautomate.js +95 -9
  44. package/dist/tools/appautomate-utils/types.d.ts +5 -0
  45. package/dist/tools/appautomate-utils/types.js +6 -0
  46. package/dist/tools/appautomate.d.ts +3 -0
  47. package/dist/tools/appautomate.js +109 -13
  48. package/dist/tools/applive-utils/device-search.d.ts +6 -0
  49. package/dist/tools/applive-utils/start-session.d.ts +15 -0
  50. package/dist/tools/applive-utils/start-session.js +11 -4
  51. package/dist/tools/applive-utils/types.d.ts +7 -0
  52. package/dist/tools/applive-utils/upload-app.d.ts +5 -0
  53. package/dist/tools/applive-utils/upload-app.js +8 -12
  54. package/dist/tools/applive-utils/version-utils.d.ts +4 -0
  55. package/dist/tools/applive.d.ts +13 -0
  56. package/dist/tools/applive.js +6 -6
  57. package/dist/tools/automate-utils/fetch-screenshots.d.ts +6 -0
  58. package/dist/tools/automate-utils/fetch-screenshots.js +16 -12
  59. package/dist/tools/automate.d.ts +9 -0
  60. package/dist/tools/automate.js +6 -6
  61. package/dist/tools/bstack-sdk.d.ts +17 -0
  62. package/dist/tools/bstack-sdk.js +47 -20
  63. package/dist/tools/failurelogs-utils/app-automate.d.ts +7 -0
  64. package/dist/tools/failurelogs-utils/app-automate.js +29 -11
  65. package/dist/tools/failurelogs-utils/automate.d.ts +6 -0
  66. package/dist/tools/failurelogs-utils/automate.js +27 -12
  67. package/dist/tools/failurelogs-utils/utils.d.ts +30 -0
  68. package/dist/tools/getFailureLogs.d.ts +14 -0
  69. package/dist/tools/getFailureLogs.js +11 -11
  70. package/dist/tools/live-utils/desktop-filter.d.ts +2 -0
  71. package/dist/tools/live-utils/mobile-filter.d.ts +2 -0
  72. package/dist/tools/live-utils/start-session.d.ts +6 -0
  73. package/dist/tools/live-utils/start-session.js +18 -5
  74. package/dist/tools/live-utils/types.d.ts +33 -0
  75. package/dist/tools/live.d.ts +3 -0
  76. package/dist/tools/live.js +11 -11
  77. package/dist/tools/observability.d.ts +5 -0
  78. package/dist/tools/observability.js +14 -11
  79. package/dist/tools/sdk-utils/commands.d.ts +3 -0
  80. package/dist/tools/sdk-utils/commands.js +20 -5
  81. package/dist/tools/sdk-utils/constants.d.ts +2 -0
  82. package/dist/tools/sdk-utils/constants.js +284 -160
  83. package/dist/tools/sdk-utils/instructions.d.ts +6 -0
  84. package/dist/tools/sdk-utils/instructions.js +28 -6
  85. package/dist/tools/sdk-utils/percy/constants.d.ts +3 -0
  86. package/dist/tools/sdk-utils/percy/constants.js +36 -25
  87. package/dist/tools/sdk-utils/percy/instructions.d.ts +10 -0
  88. package/dist/tools/sdk-utils/percy/types.d.ts +5 -0
  89. package/dist/tools/sdk-utils/types.d.ts +39 -0
  90. package/dist/tools/sdk-utils/types.js +3 -0
  91. package/dist/tools/selfheal-utils/selfheal.d.ts +11 -0
  92. package/dist/tools/selfheal-utils/selfheal.js +10 -6
  93. package/dist/tools/selfheal.d.ts +7 -0
  94. package/dist/tools/selfheal.js +6 -6
  95. package/dist/tools/testmanagement-utils/TCG-utils/api.d.ts +34 -0
  96. package/dist/tools/testmanagement-utils/TCG-utils/api.js +57 -44
  97. package/dist/tools/testmanagement-utils/TCG-utils/config.d.ts +5 -0
  98. package/dist/tools/testmanagement-utils/TCG-utils/helpers.d.ts +13 -0
  99. package/dist/tools/testmanagement-utils/TCG-utils/helpers.js +2 -1
  100. package/dist/tools/testmanagement-utils/TCG-utils/types.d.ts +26 -0
  101. package/dist/tools/testmanagement-utils/add-test-result.d.ts +42 -0
  102. package/dist/tools/testmanagement-utils/add-test-result.js +23 -10
  103. package/dist/tools/testmanagement-utils/create-lca-steps.d.ts +100 -0
  104. package/dist/tools/testmanagement-utils/create-lca-steps.js +64 -55
  105. package/dist/tools/testmanagement-utils/create-project-folder.d.ts +31 -0
  106. package/dist/tools/testmanagement-utils/create-project-folder.js +31 -21
  107. package/dist/tools/testmanagement-utils/create-testcase.d.ts +122 -0
  108. package/dist/tools/testmanagement-utils/create-testcase.js +13 -10
  109. package/dist/tools/testmanagement-utils/create-testrun.d.ts +82 -0
  110. package/dist/tools/testmanagement-utils/create-testrun.js +11 -8
  111. package/dist/tools/testmanagement-utils/list-testcases.d.ts +30 -0
  112. package/dist/tools/testmanagement-utils/list-testcases.js +9 -7
  113. package/dist/tools/testmanagement-utils/list-testruns.d.ts +22 -0
  114. package/dist/tools/testmanagement-utils/list-testruns.js +9 -7
  115. package/dist/tools/testmanagement-utils/poll-lca-status.d.ts +11 -0
  116. package/dist/tools/testmanagement-utils/poll-lca-status.js +12 -8
  117. package/dist/tools/testmanagement-utils/testcase-from-file.d.ts +4 -0
  118. package/dist/tools/testmanagement-utils/testcase-from-file.js +6 -6
  119. package/dist/tools/testmanagement-utils/update-testrun.d.ts +40 -0
  120. package/dist/tools/testmanagement-utils/update-testrun.js +11 -7
  121. package/dist/tools/testmanagement-utils/upload-file.d.ts +20 -0
  122. package/dist/tools/testmanagement-utils/upload-file.js +8 -6
  123. package/dist/tools/testmanagement.d.ts +60 -0
  124. package/dist/tools/testmanagement.js +51 -53
  125. package/package.json +1 -1
@@ -1,8 +1,11 @@
1
1
  const javaSeleniumInstructions = `
2
- To manually capture screenshots, implement the following steps in your test script:
2
+ Import the BrowserStack Percy SDK in your test script:
3
+ Add the Percy import to your test file.
3
4
 
4
- 1. **Import the BrowserStack Percy SDK** in your test script.
5
- 2. Add the \`PercySDK.screenshot(driver, name)\` method at required points in your test script to capture the screenshots.
5
+ ---STEP---
6
+
7
+ Add screenshot capture method at required points:
8
+ Use the \`PercySDK.screenshot(driver, name)\` method at points in your test script where you want to capture screenshots.
6
9
 
7
10
  Here's an example:
8
11
 
@@ -26,26 +29,29 @@ public class YourTestClass extends YourBaseTest {
26
29
  \`\`\`
27
30
  `;
28
31
  export const nodejsSeleniumInstructions = `
29
- To manually capture screenshots, implement the following steps in your test script:
30
- - Import the BrowserStack Percy SDK in your test script.
31
- - Add the \`percy.snapshot(driver, name)\` method at required points in your test script to capture the screenshots you want.
32
-
33
- \`\`\`javascript
34
- const { percy } = require('browserstack-node-sdk');
35
- describe("sample Test", () => {
36
- // ... other imports and setup
37
-
38
- test("my test", async () => {
39
- // ....
40
- await percy.screenshot(driver, "My Snapshot")
41
- // ....
42
- });
43
- })
44
- \`\`\`
32
+ Import the BrowserStack Percy SDK in your test script:
33
+ Add the Percy import to your test file.
34
+
35
+ ---STEP---
36
+
37
+ Add screenshot capture method at required points:
38
+ Use the \`percy.snapshot(driver, name)\` method at points in your test script where you want to capture screenshots.
39
+
40
+ \`\`\`javascript
41
+ const { percy } = require('browserstack-node-sdk');
42
+ describe("sample Test", () => {
43
+ // ... other imports and setup
44
+
45
+ test("my test", async () => {
46
+ // ....
47
+ await percy.snapshot(driver, "My Snapshot")
48
+ // ....
49
+ });
50
+ })
51
+ \`\`\`
45
52
  `;
46
53
  const webdriverioPercyInstructions = `
47
- **1. Enable Percy in \`wdio.conf.js\`**
48
-
54
+ Enable Percy in \`wdio.conf.js\`:
49
55
  In your WebdriverIO configuration file, modify the 'browserstack' service options to enable Percy.
50
56
 
51
57
  - Set \`percy: true\`.
@@ -79,8 +85,9 @@ exports.config = {
79
85
  };
80
86
  \`\`\`
81
87
 
82
- **2. Manually Capturing Screenshots (Optional)**
88
+ ---STEP---
83
89
 
90
+ Manually Capturing Screenshots (Optional):
84
91
  If you set \`percyCaptureMode: 'manual'\` or want to take extra screenshots in \`auto\` mode, you need to add screenshot commands to your tests.
85
92
 
86
93
  First, install \`browserstack-node-sdk\`:
@@ -106,10 +113,13 @@ describe("My WebdriverIO Test", () => {
106
113
  \`\`\`
107
114
  `;
108
115
  const csharpSeleniumInstructions = `
109
- To manually capture screenshots alongside the auto mode, implement the following steps in your test script:
116
+ Import the BrowserStack Percy SDK in your test script:
117
+ Add the Percy import to your test file.
118
+
119
+ ---STEP---
110
120
 
111
- 1. **Import the BrowserStack Percy SDK** in your test script.
112
- 2. Add the \`PercySDK.Screenshot(driver, name)\` method at required points in your test script to get the screenshots you want.
121
+ Add screenshot capture method at required points:
122
+ Use the \`PercySDK.Screenshot(driver, name)\` method at points in your test script where you want to capture screenshots.
113
123
 
114
124
  Here's an example:
115
125
 
@@ -143,6 +153,7 @@ export const PERCY_INSTRUCTIONS = {
143
153
  cucumber: { script_updates: javaSeleniumInstructions },
144
154
  junit4: { script_updates: javaSeleniumInstructions },
145
155
  junit5: { script_updates: javaSeleniumInstructions },
156
+ serenity: { script_updates: javaSeleniumInstructions },
146
157
  },
147
158
  },
148
159
  csharp: {
@@ -0,0 +1,10 @@
1
+ import { SDKSupportedBrowserAutomationFramework, SDKSupportedLanguage, SDKSupportedTestingFramework } from "../types.js";
2
+ import { PercyInstructions } from "./types.js";
3
+ /**
4
+ * Retrieves Percy-specific instructions for a given language and framework.
5
+ */
6
+ export declare function getPercyInstructions(language: SDKSupportedLanguage, automationFramework: SDKSupportedBrowserAutomationFramework, testingFramework: SDKSupportedTestingFramework): PercyInstructions | null;
7
+ /**
8
+ * Formats the retrieved Percy instructions into a user-friendly string.
9
+ */
10
+ export declare function formatPercyInstructions(instructions: PercyInstructions): string;
@@ -0,0 +1,5 @@
1
+ import { SDKSupportedBrowserAutomationFramework, SDKSupportedLanguage, SDKSupportedTestingFramework } from "../types.js";
2
+ export interface PercyInstructions {
3
+ script_updates: string;
4
+ }
5
+ export type PercyConfigMapping = Partial<Record<SDKSupportedLanguage, Partial<Record<SDKSupportedBrowserAutomationFramework, Partial<Record<SDKSupportedTestingFramework, PercyInstructions>>>>>>;
@@ -0,0 +1,39 @@
1
+ export declare enum SDKSupportedLanguageEnum {
2
+ nodejs = "nodejs",
3
+ python = "python",
4
+ java = "java",
5
+ csharp = "csharp"
6
+ }
7
+ export type SDKSupportedLanguage = keyof typeof SDKSupportedLanguageEnum;
8
+ export declare enum SDKSupportedBrowserAutomationFrameworkEnum {
9
+ playwright = "playwright",
10
+ selenium = "selenium",
11
+ cypress = "cypress"
12
+ }
13
+ export type SDKSupportedBrowserAutomationFramework = keyof typeof SDKSupportedBrowserAutomationFrameworkEnum;
14
+ export declare enum SDKSupportedTestingFrameworkEnum {
15
+ jest = "jest",
16
+ codeceptjs = "codeceptjs",
17
+ playwright = "playwright",
18
+ pytest = "pytest",
19
+ robot = "robot",
20
+ behave = "behave",
21
+ cucumber = "cucumber",
22
+ nightwatch = "nightwatch",
23
+ webdriverio = "webdriverio",
24
+ mocha = "mocha",
25
+ junit4 = "junit4",
26
+ junit5 = "junit5",
27
+ testng = "testng",
28
+ serenity = "serenity",
29
+ cypress = "cypress",
30
+ nunit = "nunit",
31
+ mstest = "mstest",
32
+ xunit = "xunit",
33
+ specflow = "specflow",
34
+ reqnroll = "reqnroll"
35
+ }
36
+ export type SDKSupportedTestingFramework = keyof typeof SDKSupportedTestingFrameworkEnum;
37
+ export type ConfigMapping = Record<SDKSupportedLanguageEnum, Partial<Record<SDKSupportedBrowserAutomationFrameworkEnum, Partial<Record<SDKSupportedTestingFrameworkEnum, {
38
+ instructions: (username: string, accessKey: string) => string;
39
+ }>>>>>;
@@ -26,8 +26,11 @@ export var SDKSupportedTestingFrameworkEnum;
26
26
  SDKSupportedTestingFrameworkEnum["junit4"] = "junit4";
27
27
  SDKSupportedTestingFrameworkEnum["junit5"] = "junit5";
28
28
  SDKSupportedTestingFrameworkEnum["testng"] = "testng";
29
+ SDKSupportedTestingFrameworkEnum["serenity"] = "serenity";
29
30
  SDKSupportedTestingFrameworkEnum["cypress"] = "cypress";
30
31
  SDKSupportedTestingFrameworkEnum["nunit"] = "nunit";
31
32
  SDKSupportedTestingFrameworkEnum["mstest"] = "mstest";
32
33
  SDKSupportedTestingFrameworkEnum["xunit"] = "xunit";
34
+ SDKSupportedTestingFrameworkEnum["specflow"] = "specflow";
35
+ SDKSupportedTestingFrameworkEnum["reqnroll"] = "reqnroll";
33
36
  })(SDKSupportedTestingFrameworkEnum || (SDKSupportedTestingFrameworkEnum = {}));
@@ -0,0 +1,11 @@
1
+ interface SelectorMapping {
2
+ originalSelector: string;
3
+ healedSelector: string;
4
+ context: {
5
+ before: string;
6
+ after: string;
7
+ };
8
+ }
9
+ import { BrowserStackConfig } from "../../lib/types.js";
10
+ export declare function getSelfHealSelectors(sessionId: string, config: BrowserStackConfig): Promise<SelectorMapping[]>;
11
+ export {};
@@ -1,17 +1,21 @@
1
1
  import { assertOkResponse } from "../../lib/utils.js";
2
- import config from "../../config.js";
3
- export async function getSelfHealSelectors(sessionId) {
4
- const credentials = `${config.browserstackUsername}:${config.browserstackAccessKey}`;
5
- const auth = Buffer.from(credentials).toString("base64");
2
+ import { getBrowserStackAuth } from "../../lib/get-auth.js";
3
+ import { apiClient } from "../../lib/apiClient.js";
4
+ export async function getSelfHealSelectors(sessionId, config) {
5
+ const authString = getBrowserStackAuth(config);
6
+ const auth = Buffer.from(authString).toString("base64");
6
7
  const url = `https://api.browserstack.com/automate/sessions/${sessionId}/logs`;
7
- const response = await fetch(url, {
8
+ const response = await apiClient.get({
9
+ url,
8
10
  headers: {
9
11
  "Content-Type": "application/json",
10
12
  Authorization: `Basic ${auth}`,
11
13
  },
12
14
  });
13
15
  await assertOkResponse(response, "session logs");
14
- const logText = await response.text();
16
+ const logText = typeof response.data === "string"
17
+ ? response.data
18
+ : JSON.stringify(response.data);
15
19
  return extractHealedSelectors(logText);
16
20
  }
17
21
  function extractHealedSelectors(logText) {
@@ -0,0 +1,7 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
+ import { BrowserStackConfig } from "../lib/types.js";
4
+ export declare function fetchSelfHealSelectorTool(args: {
5
+ sessionId: string;
6
+ }, config: BrowserStackConfig): Promise<CallToolResult>;
7
+ export default function addSelfHealTools(server: McpServer, config: BrowserStackConfig): void;
@@ -3,9 +3,9 @@ import { getSelfHealSelectors } from "./selfheal-utils/selfheal.js";
3
3
  import logger from "../logger.js";
4
4
  import { trackMCP } from "../lib/instrumentation.js";
5
5
  // Tool function that fetches self-healing selectors
6
- export async function fetchSelfHealSelectorTool(args) {
6
+ export async function fetchSelfHealSelectorTool(args, config) {
7
7
  try {
8
- const selectors = await getSelfHealSelectors(args.sessionId);
8
+ const selectors = await getSelfHealSelectors(args.sessionId, config);
9
9
  return {
10
10
  content: [
11
11
  {
@@ -22,16 +22,16 @@ export async function fetchSelfHealSelectorTool(args) {
22
22
  }
23
23
  }
24
24
  // Registers the fetchSelfHealSelector tool with the MCP server
25
- export default function addSelfHealTools(server) {
25
+ export default function addSelfHealTools(server, config) {
26
26
  server.tool("fetchSelfHealedSelectors", "Retrieves AI-generated, self-healed selectors for a BrowserStack Automate session to resolve flaky tests caused by dynamic DOM changes.", {
27
27
  sessionId: z.string().describe("The session ID of the test run"),
28
28
  }, async (args) => {
29
29
  try {
30
- trackMCP("fetchSelfHealedSelectors", server.server.getClientVersion());
31
- return await fetchSelfHealSelectorTool(args);
30
+ trackMCP("fetchSelfHealedSelectors", server.server.getClientVersion(), undefined, config);
31
+ return await fetchSelfHealSelectorTool(args, config);
32
32
  }
33
33
  catch (error) {
34
- trackMCP("fetchSelfHealedSelectors", server.server.getClientVersion(), error);
34
+ trackMCP("fetchSelfHealedSelectors", server.server.getClientVersion(), error, config);
35
35
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
36
36
  return {
37
37
  content: [
@@ -0,0 +1,34 @@
1
+ import { DefaultFieldMaps, Scenario, CreateTestCasesFromFileArgs } from "./types.js";
2
+ import { BrowserStackConfig } from "../../../lib/types.js";
3
+ /**
4
+ * Fetch default and custom form fields for a project.
5
+ */
6
+ export declare function fetchFormFields(projectId: string, config: BrowserStackConfig): Promise<{
7
+ default_fields: any;
8
+ custom_fields: any;
9
+ }>;
10
+ /**
11
+ * Trigger AI-based test case generation for a document.
12
+ */
13
+ export declare function triggerTestCaseGeneration(document: string, documentId: number, folderId: string, projectId: string, source: string, config: BrowserStackConfig): Promise<string>;
14
+ /**
15
+ * Initiate a fetch for test-case details; returns the traceRequestId for polling.
16
+ */
17
+ export declare function fetchTestCaseDetails(documentId: number, folderId: string, projectId: string, testCaseIds: string[], source: string, config: BrowserStackConfig): Promise<string>;
18
+ /**
19
+ * Poll for a given traceRequestId until all test-case details are returned.
20
+ */
21
+ export declare function pollTestCaseDetails(traceRequestId: string, config: BrowserStackConfig): Promise<Record<string, any>>;
22
+ /**
23
+ * Poll for scenarios & testcases, trigger detail fetches, then poll all details in parallel.
24
+ */
25
+ export declare function pollScenariosTestDetails(args: CreateTestCasesFromFileArgs, traceId: string, context: any, documentId: number, source: string, config: BrowserStackConfig): Promise<Record<string, Scenario>>;
26
+ /**
27
+ * Bulk-create generated test cases in BrowserStack.
28
+ */
29
+ export declare function bulkCreateTestCases(scenariosMap: Record<string, Scenario>, projectId: string, folderId: string, fieldMaps: DefaultFieldMaps, booleanFieldId: number | undefined, traceId: string, context: any, documentId: number, config: BrowserStackConfig): Promise<string>;
30
+ export declare function projectIdentifierToId(projectId: string, config: BrowserStackConfig): Promise<string>;
31
+ export declare function testCaseIdentifierToDetails(projectId: string, testCaseIdentifier: string, config: BrowserStackConfig): Promise<{
32
+ testCaseId: string;
33
+ folderId: string;
34
+ }>;
@@ -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>;