@democratize-quality/mcp-server
Version:
MCP Server for democratizing quality through browser automation and comprehensive API testing capabilities
168 lines (149 loc) • 6.84 kB
JavaScript
const ToolBase = require('../base/ToolBase');
const browserService = require('../../services/browserService');
/**
* Browser Click Tool
* Simulates a click on a specific element in the browser with Playwright-style locators
*/
class BrowserClickTool extends ToolBase {
static definition = {
name: "browser_click",
description: "Simulates a click on a specific element in the browser.",
input_schema: {
type: "object",
properties: {
browserId: {
type: "string",
description: "The ID of the browser instance."
},
locatorType: {
type: "string",
enum: ["css", "xpath", "text", "role", "label", "placeholder", "testId", "altText"],
default: "css",
description: "The type of locator to use"
},
locatorValue: {
type: "string",
description: "The locator value (CSS selector, XPath, text content, etc.)"
},
// Keep backward compatibility
selector: {
type: "string",
description: "The CSS selector of the element to click (deprecated, use locatorType and locatorValue instead)."
},
options: {
type: "object",
properties: {
timeout: {
type: "number",
default: 30000,
description: "Timeout in milliseconds"
},
force: {
type: "boolean",
default: false,
description: "Force click even if element is not actionable"
},
noWaitAfter: {
type: "boolean",
default: false,
description: "Do not wait for navigation after click"
},
button: {
type: "string",
enum: ["left", "right", "middle"],
default: "left",
description: "Mouse button to click"
}
},
description: "Additional click options"
}
},
anyOf: [
{ required: ["browserId", "locatorType", "locatorValue"] },
{ required: ["browserId", "selector"] }
]
},
output_schema: {
type: "object",
properties: {
success: { type: "boolean", description: "Indicates if the click was successful." },
locatorType: { type: "string", description: "The locator type that was used." },
locatorValue: { type: "string", description: "The locator value that was used." },
browserId: { type: "string", description: "The browser instance ID that was used." },
elementFound: { type: "boolean", description: "Whether the element was found." },
message: { type: "string", description: "Success or error message." }
},
required: ["success", "browserId"]
}
};
async execute(parameters) {
const { browserId, locatorType, locatorValue, selector, options = {} } = parameters;
// Handle backward compatibility
let finalLocatorType, finalLocatorValue;
if (selector) {
finalLocatorType = "css";
finalLocatorValue = selector;
} else {
finalLocatorType = locatorType || "css";
finalLocatorValue = locatorValue;
}
console.error(`[BrowserClickTool] Simulating click in browser ${browserId} with locator: ${finalLocatorType}="${finalLocatorValue}"`);
try {
// Convert Playwright-style locator to appropriate format for browser service
const elementSelector = this.convertLocatorToSelector(finalLocatorType, finalLocatorValue);
await browserService.clickElement(browserId, elementSelector, options);
console.error(`[BrowserClickTool] Successfully clicked element in browser: ${browserId}`);
return {
success: true,
browserId: browserId,
locatorType: finalLocatorType,
locatorValue: finalLocatorValue,
elementFound: true,
message: `Successfully clicked element with ${finalLocatorType} locator: ${finalLocatorValue}`
};
} catch (error) {
console.error(`[BrowserClickTool] Failed to click element:`, error.message);
return {
success: false,
browserId: browserId,
locatorType: finalLocatorType,
locatorValue: finalLocatorValue,
elementFound: false,
message: `Failed to click element: ${error.message}`
};
}
}
/**
* Convert Playwright-style locator to CSS selector or XPath
*/
convertLocatorToSelector(locatorType, locatorValue) {
switch (locatorType) {
case "css":
return locatorValue;
case "xpath":
return locatorValue;
case "text":
// Convert text locator to XPath
return `//*[contains(text(), "${locatorValue}")]`;
case "role":
// Convert role locator to CSS attribute selector
return `[role="${locatorValue}"]`;
case "label":
// Convert label locator to XPath for label association
return `//input[@aria-label="${locatorValue}"] | //input[@id=//label[contains(text(), "${locatorValue}")]/@for]`;
case "placeholder":
// Convert placeholder locator to CSS attribute selector
return `[placeholder="${locatorValue}"]`;
case "testId":
// Convert test ID to CSS attribute selector (assuming data-testid)
return `[data-testid="${locatorValue}"]`;
case "altText":
// Convert alt text to CSS attribute selector
return `[alt="${locatorValue}"]`;
default:
// Default to CSS selector
return locatorValue;
}
}
}
module.exports = BrowserClickTool;