Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions src/tools/testmanagement-utils/get-testcase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { apiClient } from "../../lib/apiClient.js";
import { z } from "zod";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { formatAxiosError } from "../../lib/error.js";
import { getBrowserStackAuth } from "../../lib/get-auth.js";
import { BrowserStackConfig } from "../../lib/types.js";
import { getTMBaseURL } from "../../lib/tm-base-url.js";

/**
* Schema for getting a specific test case by ID.
*/
export const GetTestCaseSchema = z.object({
project_identifier: z
.string()
.describe(
"Identifier of the project to fetch the test case from. This id starts with a PR- and is followed by a number.",
),
test_case_id: z
.string()
.describe(
"Identifier of the test case to fetch (e.g., TC-16667 or 2). Multiple IDs can be provided separated by commas.",
),
});

export type GetTestCaseArgs = z.infer<typeof GetTestCaseSchema>;

/**
* Calls BrowserStack Test Management to get a specific test case by ID.
*/
export async function getTestCase(
args: GetTestCaseArgs,
config: BrowserStackConfig,
): Promise<CallToolResult> {
try {
const tmBaseUrl = await getTMBaseURL(config);
const url = `${tmBaseUrl}/api/v2/projects/${encodeURIComponent(
args.project_identifier,
)}/test-cases?id=${encodeURIComponent(args.test_case_id)}`;

const authString = getBrowserStackAuth(config);
const [username, password] = authString.split(":");
const resp = await apiClient.get({
url,
headers: {
Authorization:
"Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
},
});

const resp_data = resp.data;
if (!resp_data.success) {
return {
content: [
{
type: "text",
text: `Failed to get test case: ${JSON.stringify(resp_data)}`,
},
],
isError: true,
};
}

const { test_cases } = resp_data;

if (!test_cases || test_cases.length === 0) {
return {
content: [
{
type: "text",
text: `No test case found with ID: ${args.test_case_id}`,
},
],
isError: true,
};
}

const tc = test_cases[0];

// Format steps if present
let stepsText = "";
if (tc.steps && tc.steps.length > 0) {
stepsText = tc.steps
.map(
(step: any, index: number) =>
`${index + 1}. ${step.step}\n Result: ${step.result}`,
)
.join("\n\n");
}

const summary = `Test Case: ${tc.identifier}
Title: ${tc.title}
Type: ${tc.case_type}
Priority: ${tc.priority}
Status: ${tc.status}
Automation: ${tc.automation_status}
Owner: ${tc.owner || "Unassigned"}

Description:
${tc.description || "N/A"}

Preconditions:
${tc.preconditions || "N/A"}

Steps:${stepsText ? "\n" + stepsText : " None"}

URL: https://test-management.browserstack.com/projects/${args.project_identifier}/folders/${tc.folder_id}/test-cases/${tc.identifier}`;

return {
content: [
{
type: "text",
text: summary,
},
{
type: "text",
text: JSON.stringify(tc, null, 2),
},
],
};
} catch (err) {
return formatAxiosError(err, "Failed to get test case");
}
}
2 changes: 1 addition & 1 deletion src/tools/testmanagement-utils/update-testcase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export async function updateTestCase(
}

if (params.test_case_steps !== undefined) {
testCaseBody.steps = params.test_case_steps;
testCaseBody.test_case_steps = params.test_case_steps;
}

const body = { test_case: testCaseBody };
Expand Down
44 changes: 44 additions & 0 deletions src/tools/testmanagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ import {
ListTestCasesSchema,
} from "./testmanagement-utils/list-testcases.js";

import {
getTestCase,
GetTestCaseSchema,
} from "./testmanagement-utils/get-testcase.js";

import {
CreateTestRunSchema,
createTestRun,
Expand Down Expand Up @@ -200,6 +205,38 @@ export async function listTestCasesTool(
}
}

/**
* Gets a specific test case by ID from BrowserStack Test Management.
*/
export async function getTestCaseTool(
args: z.infer<typeof GetTestCaseSchema>,
config: BrowserStackConfig,
server: McpServer,
): Promise<CallToolResult> {
try {
trackMCP(
"getTestCase",
server.server.getClientVersion()!,
undefined,
config,
);
return await getTestCase(args, config);
} catch (err) {
trackMCP("getTestCase", server.server.getClientVersion()!, err, config);
return {
content: [
{
type: "text",
text: `Failed to get test case: ${
err instanceof Error ? err.message : "Unknown error"
}. Please open an issue on GitHub if the problem persists`,
},
],
isError: true,
};
}
}

/**
* Creates a test run in BrowserStack Test Management.
*/
Expand Down Expand Up @@ -469,6 +506,13 @@ export default function addTestManagementTools(
(args) => listTestCasesTool(args, config, server),
);

tools.getTestCase = server.tool(
"getTestCase",
"Get a specific test case by ID from BrowserStack Test Management. Returns full details including steps, description, preconditions, and metadata.",
GetTestCaseSchema.shape,
(args) => getTestCaseTool(args, config, server),
);

tools.createTestRun = server.tool(
"createTestRun",
"Create a test run in BrowserStack Test Management.",
Expand Down