Skip to content

Commit 4cebd1a

Browse files
committed
feat: add automate self-heal tools integration
1 parent 301f4b0 commit 4cebd1a

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import addTestManagementTools from "./tools/testmanagement.js";
1515
import addAppAutomationTools from "./tools/appautomate.js";
1616
import addFailureLogsTools from "./tools/getFailureLogs.js";
1717
import addAutomateTools from "./tools/automate.js";
18+
import addSelfHealTools from "./tools/selfheal.js";
1819
import { setupOnInitialized } from "./oninitialized.js";
1920

2021
function registerTools(server: McpServer) {
@@ -26,6 +27,7 @@ function registerTools(server: McpServer) {
2627
addAppAutomationTools(server);
2728
addFailureLogsTools(server);
2829
addAutomateTools(server);
30+
addSelfHealTools(server);
2931
}
3032

3133
// Create an MCP server
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { assertOkResponse } from "../../lib/utils.js";
2+
import config from "../../config.js";
3+
4+
interface SelectorMapping {
5+
originalSelector: string;
6+
healedSelector: string;
7+
}
8+
9+
export async function getSelfHealSelectors(sessionId: string) {
10+
const credentials = `${config.browserstackUsername}:${config.browserstackAccessKey}`;
11+
const auth = Buffer.from(credentials).toString("base64");
12+
const url = `https://api.browserstack.com/automate/sessions/${sessionId}/logs`;
13+
14+
const response = await fetch(url, {
15+
headers: {
16+
"Content-Type": "application/json",
17+
Authorization: `Basic ${auth}`,
18+
},
19+
});
20+
21+
await assertOkResponse(response, "session logs");
22+
const logText = await response.text();
23+
return extractHealedSelectors(logText);
24+
}
25+
26+
function extractHealedSelectors(logText: string): SelectorMapping[] {
27+
// Pattern to match SELFHEAL entries with healed selectors
28+
const selfhealPattern =
29+
/SELFHEAL\s*{\s*"status":"true",\s*"data":\s*{\s*"using":"css selector",\s*"value":"(.*?)"}/g;
30+
31+
// Pattern to match preceding selector requests
32+
const requestPattern =
33+
/POST \/session\/[^/]+\/element.*?"using":"css selector","value":"(.*?)"/g;
34+
35+
// Find all healed selectors
36+
const healedSelectors: string[] = [];
37+
let healedMatch;
38+
while ((healedMatch = selfhealPattern.exec(logText)) !== null) {
39+
healedSelectors.push(healedMatch[1]);
40+
}
41+
42+
// Find all selector requests
43+
const selectorRequests: string[] = [];
44+
let requestMatch;
45+
while ((requestMatch = requestPattern.exec(logText)) !== null) {
46+
selectorRequests.push(requestMatch[1]);
47+
}
48+
49+
// Pair each healed selector with its corresponding original selector
50+
const healedMappings: SelectorMapping[] = [];
51+
const minLength = Math.min(selectorRequests.length, healedSelectors.length);
52+
53+
for (let i = 0; i < minLength; i++) {
54+
healedMappings.push({
55+
originalSelector: selectorRequests[i],
56+
healedSelector: healedSelectors[i],
57+
});
58+
}
59+
60+
return healedMappings;
61+
}

src/tools/selfheal.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2+
import { z } from "zod";
3+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
4+
import { getSelfHealSelectors } from "./selfheal-utils/selfheal.js";
5+
import logger from "../logger.js";
6+
7+
// Tool function that fetches self-healing selectors
8+
export async function fetchSelfHealSelectorTool(args: {
9+
sessionId: string;
10+
}): Promise<CallToolResult> {
11+
try {
12+
const selectors = await getSelfHealSelectors(args.sessionId);
13+
return {
14+
content: [
15+
{
16+
type: "text",
17+
text:
18+
"Self-heal selectors fetched successfully" +
19+
JSON.stringify(selectors),
20+
},
21+
],
22+
};
23+
} catch (error) {
24+
logger.error("Error fetching self-heal selector suggestions", error);
25+
throw error;
26+
}
27+
}
28+
29+
// Registers the fetchSelfHealSelector tool with the MCP server
30+
export default function addSelfHealTools(server: McpServer) {
31+
server.tool(
32+
"fetchSelfHealSelector",
33+
"Fetch self-healing selector suggestions for a broken selector",
34+
{
35+
sessionId: z.string().describe("The session ID of the test run"),
36+
},
37+
async (args) => {
38+
try {
39+
return await fetchSelfHealSelectorTool(args);
40+
} catch (error) {
41+
const errorMessage =
42+
error instanceof Error ? error.message : "Unknown error";
43+
return {
44+
content: [
45+
{
46+
type: "text",
47+
text: `Error during fetching self-heal suggestions: ${errorMessage}`,
48+
},
49+
],
50+
};
51+
}
52+
},
53+
);
54+
}

0 commit comments

Comments
 (0)