@browserstack/mcp-server 1.1.9 → 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.
- package/dist/config.d.ts +13 -0
- package/dist/config.js +4 -6
- package/dist/index.d.ts +4 -0
- package/dist/index.js +20 -30
- package/dist/lib/api.d.ts +2 -0
- package/dist/lib/api.js +10 -5
- package/dist/lib/apiClient.d.ts +31 -0
- package/dist/lib/apiClient.js +108 -0
- package/dist/lib/constants.d.ts +17 -0
- package/dist/lib/device-cache.d.ts +9 -0
- package/dist/lib/device-cache.js +3 -3
- package/dist/lib/error.d.ts +7 -0
- package/dist/lib/fuzzy.d.ts +1 -0
- package/dist/lib/get-auth.d.ts +2 -0
- package/dist/lib/get-auth.js +8 -0
- package/dist/lib/inmemory-store.d.ts +1 -0
- package/dist/lib/instrumentation.d.ts +4 -0
- package/dist/lib/instrumentation.js +8 -7
- package/dist/lib/local.d.ts +3 -0
- package/dist/lib/local.js +3 -3
- package/dist/lib/types.d.ts +4 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/utils.d.ts +4 -0
- package/dist/lib/version-resolver.d.ts +6 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.js +20 -4
- package/dist/oninitialized.d.ts +2 -0
- package/dist/oninitialized.js +2 -7
- package/dist/server-factory.d.ts +3 -0
- package/dist/server-factory.js +37 -0
- package/dist/tools/accessibility.d.ts +3 -0
- package/dist/tools/accessibility.js +17 -10
- package/dist/tools/accessiblity-utils/accessibility-rag.d.ts +12 -0
- package/dist/tools/accessiblity-utils/accessibility-rag.js +22 -12
- package/dist/tools/accessiblity-utils/report-fetcher.d.ts +8 -0
- package/dist/tools/accessiblity-utils/report-fetcher.js +26 -16
- package/dist/tools/accessiblity-utils/report-parser.d.ts +23 -0
- package/dist/tools/accessiblity-utils/report-parser.js +3 -3
- package/dist/tools/accessiblity-utils/scanner.d.ts +25 -0
- package/dist/tools/accessiblity-utils/scanner.js +43 -24
- package/dist/tools/appautomate-utils/appautomate.d.ts +42 -0
- package/dist/tools/appautomate-utils/appautomate.js +63 -38
- package/dist/tools/appautomate-utils/types.d.ts +5 -0
- package/dist/tools/appautomate.d.ts +3 -0
- package/dist/tools/appautomate.js +20 -19
- package/dist/tools/applive-utils/device-search.d.ts +6 -0
- package/dist/tools/applive-utils/start-session.d.ts +15 -0
- package/dist/tools/applive-utils/start-session.js +11 -4
- package/dist/tools/applive-utils/types.d.ts +7 -0
- package/dist/tools/applive-utils/upload-app.d.ts +5 -0
- package/dist/tools/applive-utils/upload-app.js +8 -12
- package/dist/tools/applive-utils/version-utils.d.ts +4 -0
- package/dist/tools/applive.d.ts +13 -0
- package/dist/tools/applive.js +6 -6
- package/dist/tools/automate-utils/fetch-screenshots.d.ts +6 -0
- package/dist/tools/automate-utils/fetch-screenshots.js +16 -12
- package/dist/tools/automate.d.ts +9 -0
- package/dist/tools/automate.js +6 -6
- package/dist/tools/bstack-sdk.d.ts +17 -0
- package/dist/tools/bstack-sdk.js +12 -7
- package/dist/tools/failurelogs-utils/app-automate.d.ts +7 -0
- package/dist/tools/failurelogs-utils/app-automate.js +29 -11
- package/dist/tools/failurelogs-utils/automate.d.ts +6 -0
- package/dist/tools/failurelogs-utils/automate.js +27 -12
- package/dist/tools/failurelogs-utils/utils.d.ts +30 -0
- package/dist/tools/getFailureLogs.d.ts +14 -0
- package/dist/tools/getFailureLogs.js +11 -11
- package/dist/tools/live-utils/desktop-filter.d.ts +2 -0
- package/dist/tools/live-utils/mobile-filter.d.ts +2 -0
- package/dist/tools/live-utils/start-session.d.ts +6 -0
- package/dist/tools/live-utils/start-session.js +18 -5
- package/dist/tools/live-utils/types.d.ts +33 -0
- package/dist/tools/live.d.ts +3 -0
- package/dist/tools/live.js +11 -11
- package/dist/tools/observability.d.ts +5 -0
- package/dist/tools/observability.js +14 -11
- package/dist/tools/sdk-utils/commands.d.ts +3 -0
- package/dist/tools/sdk-utils/commands.js +5 -4
- package/dist/tools/sdk-utils/constants.d.ts +2 -0
- package/dist/tools/sdk-utils/constants.js +82 -21
- package/dist/tools/sdk-utils/instructions.d.ts +6 -0
- package/dist/tools/sdk-utils/instructions.js +8 -6
- package/dist/tools/sdk-utils/percy/constants.d.ts +3 -0
- package/dist/tools/sdk-utils/percy/constants.js +1 -0
- package/dist/tools/sdk-utils/percy/instructions.d.ts +10 -0
- package/dist/tools/sdk-utils/percy/types.d.ts +5 -0
- package/dist/tools/sdk-utils/types.d.ts +39 -0
- package/dist/tools/sdk-utils/types.js +1 -0
- package/dist/tools/selfheal-utils/selfheal.d.ts +11 -0
- package/dist/tools/selfheal-utils/selfheal.js +10 -6
- package/dist/tools/selfheal.d.ts +7 -0
- package/dist/tools/selfheal.js +6 -6
- package/dist/tools/testmanagement-utils/TCG-utils/api.d.ts +34 -0
- package/dist/tools/testmanagement-utils/TCG-utils/api.js +57 -44
- package/dist/tools/testmanagement-utils/TCG-utils/config.d.ts +5 -0
- package/dist/tools/testmanagement-utils/TCG-utils/helpers.d.ts +13 -0
- package/dist/tools/testmanagement-utils/TCG-utils/helpers.js +2 -1
- package/dist/tools/testmanagement-utils/TCG-utils/types.d.ts +26 -0
- package/dist/tools/testmanagement-utils/add-test-result.d.ts +42 -0
- package/dist/tools/testmanagement-utils/add-test-result.js +23 -10
- package/dist/tools/testmanagement-utils/create-lca-steps.d.ts +100 -0
- package/dist/tools/testmanagement-utils/create-lca-steps.js +64 -55
- package/dist/tools/testmanagement-utils/create-project-folder.d.ts +31 -0
- package/dist/tools/testmanagement-utils/create-project-folder.js +31 -21
- package/dist/tools/testmanagement-utils/create-testcase.d.ts +122 -0
- package/dist/tools/testmanagement-utils/create-testcase.js +13 -10
- package/dist/tools/testmanagement-utils/create-testrun.d.ts +82 -0
- package/dist/tools/testmanagement-utils/create-testrun.js +11 -8
- package/dist/tools/testmanagement-utils/list-testcases.d.ts +30 -0
- package/dist/tools/testmanagement-utils/list-testcases.js +9 -7
- package/dist/tools/testmanagement-utils/list-testruns.d.ts +22 -0
- package/dist/tools/testmanagement-utils/list-testruns.js +9 -7
- package/dist/tools/testmanagement-utils/poll-lca-status.d.ts +11 -0
- package/dist/tools/testmanagement-utils/poll-lca-status.js +12 -8
- package/dist/tools/testmanagement-utils/testcase-from-file.d.ts +4 -0
- package/dist/tools/testmanagement-utils/testcase-from-file.js +6 -6
- package/dist/tools/testmanagement-utils/update-testrun.d.ts +40 -0
- package/dist/tools/testmanagement-utils/update-testrun.js +11 -7
- package/dist/tools/testmanagement-utils/upload-file.d.ts +20 -0
- package/dist/tools/testmanagement-utils/upload-file.js +8 -6
- package/dist/tools/testmanagement.d.ts +60 -0
- package/dist/tools/testmanagement.js +51 -53
- package/package.json +1 -1
|
@@ -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,6 +26,7 @@ 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";
|
|
@@ -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
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
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
|
|
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 =
|
|
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;
|
package/dist/tools/selfheal.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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":
|
|
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
|
|
21
|
-
|
|
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":
|
|
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
|
|
47
|
-
|
|
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":
|
|
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
|
|
77
|
+
const poll = await apiClient.post({
|
|
78
|
+
url: `${TCG_POLL_URL}?x-bstack-traceRequestId=${encodeURIComponent(traceRequestId)}`,
|
|
73
79
|
headers: {
|
|
74
|
-
"API-TOKEN":
|
|
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
|
|
115
|
+
const poll = await apiClient.post({
|
|
116
|
+
url: `${TCG_POLL_URL}?x-bstack-traceRequestId=${encodeURIComponent(traceId)}`,
|
|
109
117
|
headers: {
|
|
110
|
-
"API-TOKEN":
|
|
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
|
|
215
|
+
const resp = await apiClient.post({
|
|
216
|
+
url: BULK_CREATE_URL(projectId, folderId),
|
|
207
217
|
headers: {
|
|
208
|
-
"API-TOKEN":
|
|
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
|
|
255
|
+
const response = await apiClient.get({
|
|
256
|
+
url,
|
|
245
257
|
headers: {
|
|
246
|
-
"API-TOKEN":
|
|
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
|
|
274
|
+
const response = await apiClient.get({
|
|
275
|
+
url,
|
|
263
276
|
headers: {
|
|
264
|
-
"API-TOKEN":
|
|
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 ||
|
|
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
|
|
2
|
-
import
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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>;
|