UNPKG

robotframework-mcp

Version:

Model Context Protocol implementation for Robot Framework

697 lines (620 loc) 17.4 kB
/** * Element Tools * * MCP element interaction tools that map to Robot Framework SeleniumLibrary keywords. */ const { executeRobotCommand, generateTempRobotScript } = require('../robot-runner'); const fs = require('fs'); /** * Maps MCP locator strategies to Robot Framework locator strategies * @param {string} by - The MCP locator strategy * @param {string} value - The locator value * @returns {string} - The Robot Framework locator */ function mapLocator(by, value) { switch (by.toLowerCase()) { case 'id': return `id:${value}`; case 'css': return `css:${value}`; case 'xpath': return `xpath:${value}`; case 'name': return `name:${value}`; case 'tag': return `tag:${value}`; case 'class': return `class:${value}`; default: throw new Error(`Unsupported locator strategy: ${by}`); } } /** * Find an element * @param {object} params - The tool parameters * @param {Map} sessions - The sessions map * @returns {Promise<object>} - The tool execution result */ async function findElement(params, sessions) { const { by, value, timeout = 10000 } = params; // Validate parameters if (!by || !value) { return { status: 'error', error: 'Both "by" and "value" parameters are required' }; } try { const locator = mapLocator(by, value); // Generate Robot Framework script const scriptContent = ` *** Settings *** Library SeleniumLibrary *** Test Cases *** Find Element Wait Until Element Is Visible ${locator} ${timeout / 1000} `; const scriptPath = generateTempRobotScript(scriptContent); try { const result = await executeRobotCommand('', scriptPath); if (result.status === 'success') { return { status: 'success', result: { message: `Element found: ${locator}`, found: true } }; } else { return { status: 'error', error: result.error || `Element not found: ${locator}`, found: false }; } } catch (error) { return { status: 'error', error: `Failed to find element: ${error.message}`, found: false }; } finally { // Clean up temp script file try { fs.unlinkSync(scriptPath); } catch (error) { console.error(`Error removing temp script file: ${error.message}`); } } } catch (error) { return { status: 'error', error: error.message, found: false }; } } /** * Click an element * @param {object} params - The tool parameters * @param {Map} sessions - The sessions map * @returns {Promise<object>} - The tool execution result */ async function clickElement(params, sessions) { const { by, value, timeout = 10000 } = params; // Validate parameters if (!by || !value) { return { status: 'error', error: 'Both "by" and "value" parameters are required' }; } try { const locator = mapLocator(by, value); // Generate Robot Framework script const scriptContent = ` *** Settings *** Library SeleniumLibrary *** Test Cases *** Click Element Wait Until Element Is Visible ${locator} ${timeout / 1000} Click Element ${locator} `; const scriptPath = generateTempRobotScript(scriptContent); try { const result = await executeRobotCommand('', scriptPath); if (result.status === 'success') { return { status: 'success', result: { message: `Element clicked: ${locator}` } }; } else { return { status: 'error', error: result.error || `Failed to click element: ${locator}` }; } } catch (error) { return { status: 'error', error: `Failed to click element: ${error.message}` }; } finally { // Clean up temp script file try { fs.unlinkSync(scriptPath); } catch (error) { console.error(`Error removing temp script file: ${error.message}`); } } } catch (error) { return { status: 'error', error: error.message }; } } /** * Send keys to an element * @param {object} params - The tool parameters * @param {Map} sessions - The sessions map * @returns {Promise<object>} - The tool execution result */ async function sendKeys(params, sessions) { const { by, value, text, timeout = 10000 } = params; // Validate parameters if (!by || !value || text === undefined) { return { status: 'error', error: 'Parameters "by", "value", and "text" are all required' }; } try { const locator = mapLocator(by, value); // Generate Robot Framework script const scriptContent = ` *** Settings *** Library SeleniumLibrary *** Test Cases *** Send Keys To Element Wait Until Element Is Visible ${locator} ${timeout / 1000} Input Text ${locator} ${text} `; const scriptPath = generateTempRobotScript(scriptContent); try { const result = await executeRobotCommand('', scriptPath); if (result.status === 'success') { return { status: 'success', result: { message: `Text entered into element: ${locator}` } }; } else { return { status: 'error', error: result.error || `Failed to enter text into element: ${locator}` }; } } catch (error) { return { status: 'error', error: `Failed to enter text into element: ${error.message}` }; } finally { // Clean up temp script file try { fs.unlinkSync(scriptPath); } catch (error) { console.error(`Error removing temp script file: ${error.message}`); } } } catch (error) { return { status: 'error', error: error.message }; } } /** * Get element text * @param {object} params - The tool parameters * @param {Map} sessions - The sessions map * @returns {Promise<object>} - The tool execution result */ async function getElementText(params, sessions) { const { by, value, timeout = 10000 } = params; // Validate parameters if (!by || !value) { return { status: 'error', error: 'Both "by" and "value" parameters are required' }; } try { const locator = mapLocator(by, value); // Generate Robot Framework script // Note: This is simplified. In a real implementation, you'd need to capture // the text output from Robot Framework, which might require additional setup const scriptContent = ` *** Settings *** Library SeleniumLibrary *** Test Cases *** Get Element Text Wait Until Element Is Visible ${locator} ${timeout / 1000} ${text}= Get Text ${locator} Log Element text: \${text} `; const scriptPath = generateTempRobotScript(scriptContent); try { const result = await executeRobotCommand('', scriptPath); if (result.status === 'success') { // In a real implementation, you would extract the actual text from the Robot Framework output // This is simplified and would need to be enhanced const textMatch = result.output.match(/Element text: (.*)/); const text = textMatch ? textMatch[1] : 'Text extraction not implemented'; return { status: 'success', result: { text: text, message: `Got text from element: ${locator}` } }; } else { return { status: 'error', error: result.error || `Failed to get text from element: ${locator}` }; } } catch (error) { return { status: 'error', error: `Failed to get text from element: ${error.message}` }; } finally { // Clean up temp script file try { fs.unlinkSync(scriptPath); } catch (error) { console.error(`Error removing temp script file: ${error.message}`); } } } catch (error) { return { status: 'error', error: error.message }; } } /** * Hover over an element * @param {object} params - The tool parameters * @param {Map} sessions - The sessions map * @returns {Promise<object>} - The tool execution result */ async function hover(params, sessions) { const { by, value, timeout = 10000 } = params; // Validate parameters if (!by || !value) { return { status: 'error', error: 'Both "by" and "value" parameters are required' }; } try { const locator = mapLocator(by, value); // Generate Robot Framework script const scriptContent = ` *** Settings *** Library SeleniumLibrary *** Test Cases *** Hover Over Element Wait Until Element Is Visible ${locator} ${timeout / 1000} Mouse Over ${locator} `; const scriptPath = generateTempRobotScript(scriptContent); try { const result = await executeRobotCommand('', scriptPath); if (result.status === 'success') { return { status: 'success', result: { message: `Hovered over element: ${locator}` } }; } else { return { status: 'error', error: result.error || `Failed to hover over element: ${locator}` }; } } catch (error) { return { status: 'error', error: `Failed to hover over element: ${error.message}` }; } finally { // Clean up temp script file try { fs.unlinkSync(scriptPath); } catch (error) { console.error(`Error removing temp script file: ${error.message}`); } } } catch (error) { return { status: 'error', error: error.message }; } } /** * Drag and drop an element * @param {object} params - The tool parameters * @param {Map} sessions - The sessions map * @returns {Promise<object>} - The tool execution result */ async function dragAndDrop(params, sessions) { const { by, value, targetBy, targetValue, timeout = 10000 } = params; // Validate parameters if (!by || !value || !targetBy || !targetValue) { return { status: 'error', error: 'Parameters "by", "value", "targetBy", and "targetValue" are all required' }; } try { const sourceLocator = mapLocator(by, value); const targetLocator = mapLocator(targetBy, targetValue); // Generate Robot Framework script const scriptContent = ` *** Settings *** Library SeleniumLibrary *** Test Cases *** Drag And Drop Element Wait Until Element Is Visible ${sourceLocator} ${timeout / 1000} Wait Until Element Is Visible ${targetLocator} ${timeout / 1000} Drag And Drop ${sourceLocator} ${targetLocator} `; const scriptPath = generateTempRobotScript(scriptContent); try { const result = await executeRobotCommand('', scriptPath); if (result.status === 'success') { return { status: 'success', result: { message: `Dragged element ${sourceLocator} to ${targetLocator}` } }; } else { return { status: 'error', error: result.error || `Failed to drag and drop element` }; } } catch (error) { return { status: 'error', error: `Failed to drag and drop element: ${error.message}` }; } finally { // Clean up temp script file try { fs.unlinkSync(scriptPath); } catch (error) { console.error(`Error removing temp script file: ${error.message}`); } } } catch (error) { return { status: 'error', error: error.message }; } } /** * Double click an element * @param {object} params - The tool parameters * @param {Map} sessions - The sessions map * @returns {Promise<object>} - The tool execution result */ async function doubleClick(params, sessions) { const { by, value, timeout = 10000 } = params; // Validate parameters if (!by || !value) { return { status: 'error', error: 'Both "by" and "value" parameters are required' }; } try { const locator = mapLocator(by, value); // Generate Robot Framework script const scriptContent = ` *** Settings *** Library SeleniumLibrary *** Test Cases *** Double Click Element Wait Until Element Is Visible ${locator} ${timeout / 1000} Double Click Element ${locator} `; const scriptPath = generateTempRobotScript(scriptContent); try { const result = await executeRobotCommand('', scriptPath); if (result.status === 'success') { return { status: 'success', result: { message: `Double-clicked element: ${locator}` } }; } else { return { status: 'error', error: result.error || `Failed to double-click element: ${locator}` }; } } catch (error) { return { status: 'error', error: `Failed to double-click element: ${error.message}` }; } finally { // Clean up temp script file try { fs.unlinkSync(scriptPath); } catch (error) { console.error(`Error removing temp script file: ${error.message}`); } } } catch (error) { return { status: 'error', error: error.message }; } } /** * Right click an element * @param {object} params - The tool parameters * @param {Map} sessions - The sessions map * @returns {Promise<object>} - The tool execution result */ async function rightClick(params, sessions) { const { by, value, timeout = 10000 } = params; // Validate parameters if (!by || !value) { return { status: 'error', error: 'Both "by" and "value" parameters are required' }; } try { const locator = mapLocator(by, value); // Generate Robot Framework script const scriptContent = ` *** Settings *** Library SeleniumLibrary *** Test Cases *** Right Click Element Wait Until Element Is Visible ${locator} ${timeout / 1000} Open Context Menu ${locator} `; const scriptPath = generateTempRobotScript(scriptContent); try { const result = await executeRobotCommand('', scriptPath); if (result.status === 'success') { return { status: 'success', result: { message: `Right-clicked element: ${locator}` } }; } else { return { status: 'error', error: result.error || `Failed to right-click element: ${locator}` }; } } catch (error) { return { status: 'error', error: `Failed to right-click element: ${error.message}` }; } finally { // Clean up temp script file try { fs.unlinkSync(scriptPath); } catch (error) { console.error(`Error removing temp script file: ${error.message}`); } } } catch (error) { return { status: 'error', error: error.message }; } } /** * Upload a file * @param {object} params - The tool parameters * @param {Map} sessions - The sessions map * @returns {Promise<object>} - The tool execution result */ async function uploadFile(params, sessions) { const { by, value, filePath, timeout = 10000 } = params; // Validate parameters if (!by || !value || !filePath) { return { status: 'error', error: 'Parameters "by", "value", and "filePath" are all required' }; } try { const locator = mapLocator(by, value); // Check if file exists if (!fs.existsSync(filePath)) { return { status: 'error', error: `File not found: ${filePath}` }; } // Generate Robot Framework script const scriptContent = ` *** Settings *** Library SeleniumLibrary *** Test Cases *** Upload File Wait Until Element Is Visible ${locator} ${timeout / 1000} Choose File ${locator} ${filePath} `; const scriptPath = generateTempRobotScript(scriptContent); try { const result = await executeRobotCommand('', scriptPath); if (result.status === 'success') { return { status: 'success', result: { message: `File uploaded successfully: ${filePath}` } }; } else { return { status: 'error', error: result.error || `Failed to upload file: ${filePath}` }; } } catch (error) { return { status: 'error', error: `Failed to upload file: ${error.message}` }; } finally { // Clean up temp script file try { fs.unlinkSync(scriptPath); } catch (error) { console.error(`Error removing temp script file: ${error.message}`); } } } catch (error) { return { status: 'error', error: error.message }; } } module.exports = { findElement, clickElement, sendKeys, getElementText, hover, dragAndDrop, doubleClick, rightClick, uploadFile };