Skip to main content
Glama

MCP Appium Server

by Rahulec08
appium-test.ts25.5 kB
import { AppiumHelper, AppiumCapabilities, AppiumError, } from "../src/lib/appium/appiumHelper.js"; import { Browser, RemoteOptions, remote } from "webdriverio"; /** * Comprehensive test suite for AppiumHelper * This tests all W3C compliant methods and mobile-specific extensions */ async function testAppiumHelperComprehensive() { const appium = new AppiumHelper("./test-screenshots"); let testResults: { [key: string]: boolean } = {}; try { console.log("🚀 Starting comprehensive AppiumHelper test suite...\n"); // Test 1: Driver Initialization console.log("📱 Test 1: Driver Initialization"); try { const capabilities: AppiumCapabilities = AppiumHelper.createW3CCapabilities("Android", { deviceName: "Pixel_4", automationName: "UiAutomator2", appPackage: "com.android.settings", appActivity: ".Settings", noReset: true, newCommandTimeout: 300, }); console.log( "Capabilities created:", JSON.stringify(capabilities, null, 2) ); // Use environment variable or default URL const appiumUrl = process.env.APPIUM_SERVER_URL || "http://0.0.0.0:4723/wd/hub"; await appium.initializeDriver(capabilities, appiumUrl); testResults["driver_initialization"] = true; console.log("✅ Driver initialization: PASSED\n"); } catch (error) { testResults["driver_initialization"] = false; console.log("❌ Driver initialization: FAILED -", error); throw error; // Can't continue without driver } // Test 2: Session Management console.log("📱 Test 2: Session Management"); try { const driver = appium.getDriver(); console.log("Driver instance retrieved:", !!driver); const sessionValid = await appium.validateSession(); console.log("Session validation:", sessionValid); testResults["session_management"] = true; console.log("✅ Session management: PASSED\n"); } catch (error) { testResults["session_management"] = false; console.log("❌ Session management: FAILED -", error); } // Test 3: Device Information console.log("📱 Test 3: Device Information"); try { const currentPackage = await appium.getCurrentPackage(); console.log("Current package:", currentPackage); const currentActivity = await appium.getCurrentActivity(); console.log("Current activity:", currentActivity); const deviceTime = await appium.getDeviceTime(); console.log("Device time:", deviceTime); const orientation = await appium.getOrientation(); console.log("Device orientation:", orientation); const windowSize = await appium.getWindowSize(); console.log("Window size:", windowSize); testResults["device_information"] = true; console.log("✅ Device information: PASSED\n"); } catch (error) { testResults["device_information"] = false; console.log("❌ Device information: FAILED -", error); } // Test 4: Screenshots console.log("📱 Test 4: Screenshots"); try { const screenshotPath1 = await appium.takeScreenshot("initial_screen"); console.log("Full screenshot saved:", screenshotPath1); testResults["screenshots"] = true; console.log("✅ Screenshots: PASSED\n"); } catch (error) { testResults["screenshots"] = false; console.log("❌ Screenshots: FAILED -", error); } // Test 5: Page Source console.log("📱 Test 5: Page Source"); try { const pageSource = await appium.getPageSource(); console.log("Page source length:", pageSource.length); console.log("Contains settings?", pageSource.includes("Settings")); testResults["page_source"] = true; console.log("✅ Page source: PASSED\n"); } catch (error) { testResults["page_source"] = false; console.log("❌ Page source: FAILED -", error); } // Test 6: Element Location Strategies console.log("📱 Test 6: Element Location Strategies"); try { // Test different location strategies const strategies = [ { strategy: "xpath", selector: '//android.widget.TextView[@text="About phone"]', }, { strategy: "id", selector: "android:id/title" }, { strategy: "class name", selector: "android.widget.TextView" }, { strategy: "accessibility id", selector: "About phone" }, ]; let strategyResults = 0; for (const test of strategies) { try { const exists = await appium.elementExists( test.selector, test.strategy ); console.log( `${test.strategy} strategy (${test.selector}):`, exists ? "Found" : "Not found" ); if (exists) strategyResults++; } catch (error) { console.log( `${test.strategy} strategy failed:`, error instanceof Error ? error.message : error ); } } testResults["element_location"] = strategyResults > 0; console.log( `✅ Element location strategies: ${strategyResults}/4 working\n` ); } catch (error) { testResults["element_location"] = false; console.log("❌ Element location strategies: FAILED -", error); } // Test 7: Element Finding console.log("📱 Test 7: Element Finding"); try { // Find single element const element = await appium.findElement( "//android.widget.TextView", "xpath" ); console.log("Single element found:", !!element); // Find multiple elements const elements = await appium.findElements( "//android.widget.TextView", "xpath" ); console.log("Multiple elements found:", elements.length); testResults["element_finding"] = true; console.log("✅ Element finding: PASSED\n"); } catch (error) { testResults["element_finding"] = false; console.log("❌ Element finding: FAILED -", error); } // Test 8: Element State Checks console.log("📱 Test 8: Element State Checks"); try { const testSelector = "//android.widget.TextView"; const isDisplayed = await appium.isDisplayed(testSelector); console.log("Element displayed:", isDisplayed); const isEnabled = await appium.isEnabled(testSelector); console.log("Element enabled:", isEnabled); const isSelected = await appium.isSelected(testSelector); console.log("Element selected:", isSelected); testResults["element_state"] = true; console.log("✅ Element state checks: PASSED\n"); } catch (error) { testResults["element_state"] = false; console.log("❌ Element state checks: FAILED -", error); } // Test 9: Element Text and Attributes console.log("📱 Test 9: Element Text and Attributes"); try { const textSelector = "//android.widget.TextView[1]"; const elementText = await appium.getText(textSelector); console.log("Element text:", elementText); const className = await appium.getAttribute(textSelector, "class"); console.log("Element class:", className); const resourceId = await appium.getAttribute(textSelector, "resource-id"); console.log("Element resource-id:", resourceId); testResults["element_text_attributes"] = true; console.log("✅ Element text and attributes: PASSED\n"); } catch (error) { testResults["element_text_attributes"] = false; console.log("❌ Element text and attributes: FAILED -", error); } // Test 10: Touch Actions console.log("📱 Test 10: Touch Actions"); try { // Test scroll console.log("Testing scroll down..."); await appium.scroll("down", 0.3); await new Promise((resolve) => setTimeout(resolve, 1000)); console.log("Testing scroll up..."); await appium.scroll("up", 0.3); await new Promise((resolve) => setTimeout(resolve, 1000)); // Test swipe const windowSize = await appium.getWindowSize(); const centerX = windowSize.width / 2; const startY = windowSize.height * 0.7; const endY = windowSize.height * 0.3; console.log("Testing swipe gesture..."); await appium.swipe(centerX, startY, centerX, endY, 500); await new Promise((resolve) => setTimeout(resolve, 1000)); testResults["touch_actions"] = true; console.log("✅ Touch actions: PASSED\n"); } catch (error) { testResults["touch_actions"] = false; console.log("❌ Touch actions: FAILED -", error); } // Test 11: Element Interaction console.log("📱 Test 11: Element Interaction"); try { // Look for About phone and tap it const aboutPhoneExists = await appium.elementExists( '//android.widget.TextView[@text="About phone"]' ); if (aboutPhoneExists) { console.log("Found 'About phone', testing tap..."); await appium.tapElement( '//android.widget.TextView[@text="About phone"]' ); await new Promise((resolve) => setTimeout(resolve, 2000)); const aboutScreenshot = await appium.takeScreenshot("about_phone"); console.log("About phone screenshot:", aboutScreenshot); // Navigate back await appium.navigateBack(); await new Promise((resolve) => setTimeout(resolve, 1000)); } // Test search functionality if available const searchExists = await appium.elementExists( '//android.widget.TextView[@text="Search settings"]' ); if (searchExists) { console.log("Testing search functionality..."); await appium.tapElement( '//android.widget.TextView[@text="Search settings"]' ); await new Promise((resolve) => setTimeout(resolve, 1000)); // Try to find search input field const searchInputExists = await appium.elementExists( "//android.widget.EditText" ); if (searchInputExists) { await appium.sendKeys("//android.widget.EditText", "wifi"); await new Promise((resolve) => setTimeout(resolve, 2000)); await appium.takeScreenshot("search_results"); // Clear search await appium.clearElement("//android.widget.EditText"); // Navigate back await appium.navigateBack(); await new Promise((resolve) => setTimeout(resolve, 1000)); } } testResults["element_interaction"] = true; console.log("✅ Element interaction: PASSED\n"); } catch (error) { testResults["element_interaction"] = false; console.log("❌ Element interaction: FAILED -", error); } // Test 12: Advanced Gestures console.log("📱 Test 12: Advanced Gestures"); try { const windowSize = await appium.getWindowSize(); const centerX = windowSize.width / 2; const centerY = windowSize.height / 2; // Test long press (if we can find a suitable element) const testElements = await appium.findElements( "//android.widget.TextView" ); if (testElements.length > 0) { try { console.log("Testing long press..."); await appium.longPress("//android.widget.TextView[1]", 1000); await new Promise((resolve) => setTimeout(resolve, 1000)); } catch (longPressError) { console.log( "Long press test skipped:", longPressError instanceof Error ? longPressError.message : longPressError ); } } // Test pinch gesture try { console.log("Testing pinch gesture..."); await appium.pinch(centerX, centerY, 0.8, 1000); await new Promise((resolve) => setTimeout(resolve, 1000)); } catch (pinchError) { console.log( "Pinch test skipped:", pinchError instanceof Error ? pinchError.message : pinchError ); } // Test zoom gesture try { console.log("Testing zoom gesture..."); await appium.zoom(centerX, centerY, 1.2, 1000); await new Promise((resolve) => setTimeout(resolve, 1000)); } catch (zoomError) { console.log( "Zoom test skipped:", zoomError instanceof Error ? zoomError.message : zoomError ); } testResults["advanced_gestures"] = true; console.log("✅ Advanced gestures: PASSED\n"); } catch (error) { testResults["advanced_gestures"] = false; console.log("❌ Advanced gestures: FAILED -", error); } // Test 13: Context Management console.log("📱 Test 13: Context Management"); try { const contexts = await appium.getContexts(); console.log("Available contexts:", contexts); const currentContext = await appium.getCurrentContext(); console.log("Current context:", currentContext); testResults["context_management"] = true; console.log("✅ Context management: PASSED\n"); } catch (error) { testResults["context_management"] = false; console.log("❌ Context management: FAILED -", error); } // Test 14: App Management console.log("📱 Test 14: App Management"); try { // Test if calculator app is installed const calcInstalled = await appium.isAppInstalled( "com.google.android.calculator" ); console.log("Calculator app installed:", calcInstalled); // Test current app info const currentPackage = await appium.getCurrentPackage(); console.log("Current package:", currentPackage); testResults["app_management"] = true; console.log("✅ App management: PASSED\n"); } catch (error) { testResults["app_management"] = false; console.log("❌ App management: FAILED -", error); } // Test 15: Device Hardware Keys console.log("📱 Test 15: Device Hardware Keys"); try { // Test home key (key code 3) console.log("Testing home key..."); await appium.pressKeyCode(3); await new Promise((resolve) => setTimeout(resolve, 2000)); // Return to settings await appium.activateApp("com.android.settings"); await new Promise((resolve) => setTimeout(resolve, 2000)); testResults["hardware_keys"] = true; console.log("✅ Hardware keys: PASSED\n"); } catch (error) { testResults["hardware_keys"] = false; console.log("❌ Hardware keys: FAILED -", error); } // Test 16: tapElement Method console.log("📱 Test 16: tapElement Method"); try { // Test basic tap functionality on different element types console.log("Testing tapElement on various UI elements..."); // Test 1: Tap on first visible TextView const textElements = await appium.findElements( "//android.widget.TextView" ); if (textElements.length > 0) { console.log(`Found ${textElements.length} text elements for tapping`); const firstElement = "//android.widget.TextView[1]"; const isDisplayed = await appium.isDisplayed(firstElement); if (isDisplayed) { console.log("Testing tapElement on first TextView..."); await appium.tapElement(firstElement); await new Promise((resolve) => setTimeout(resolve, 1000)); await appium.takeScreenshot("after_tap_first_element"); console.log("✅ tapElement on TextView successful"); } } // Test 2: Try tapping on specific Settings menu items const settingsMenuItems = [ '//android.widget.TextView[@text="Network & internet"]', '//android.widget.TextView[@text="Connected devices"]', '//android.widget.TextView[@text="Apps"]', '//android.widget.TextView[@text="Battery"]', '//android.widget.TextView[@text="Display"]', '//android.widget.TextView[@text="Sound"]', '//android.widget.TextView[@text="Storage"]', '//android.widget.TextView[@text="Privacy"]', '//android.widget.TextView[@text="Security"]', '//android.widget.TextView[@text="System"]', ]; let successfulTaps = 0; for (const menuItem of settingsMenuItems) { try { const exists = await appium.elementExists(menuItem); if (exists) { const menuText = menuItem.split('"')[1]; console.log(`Testing tap on: "${menuText}"`); await appium.tapElement(menuItem); await new Promise((resolve) => setTimeout(resolve, 1500)); // Take screenshot to verify navigation await appium.takeScreenshot( `tap_${menuText.toLowerCase().replace(/\s+/g, "_")}` ); // Navigate back to Settings main screen await appium.navigateBack(); await new Promise((resolve) => setTimeout(resolve, 1000)); successfulTaps++; console.log(`✅ Successfully tapped "${menuText}"`); // Only test 2-3 items to avoid excessive navigation if (successfulTaps >= 2) break; } } catch (error) { console.log( `Tap failed for ${menuItem}:`, error instanceof Error ? error.message : error ); } } // Test 3: Test tapping by coordinates (fallback test) console.log("Testing coordinate-based tap..."); const windowSize = await appium.getWindowSize(); const centerX = windowSize.width / 2; const centerY = windowSize.height / 2; await appium.tapByCoordinates(centerX, centerY); await new Promise((resolve) => setTimeout(resolve, 1000)); console.log(`✅ Coordinate tap at (${centerX}, ${centerY}) successful`); // Test 4: Test tapElement with different selector strategies const selectorTests = [ { strategy: "xpath", selector: "//android.widget.TextView[contains(@text,'About')]", }, { strategy: "xpath", selector: "//android.widget.LinearLayout[1]/android.widget.TextView[1]", }, { strategy: "class name", selector: "android.widget.TextView" }, ]; for (const test of selectorTests) { try { const exists = await appium.elementExists( test.selector, test.strategy ); if (exists) { console.log( `Testing tapElement with ${test.strategy}: ${test.selector}` ); await appium.tapElement(test.selector, test.strategy); await new Promise((resolve) => setTimeout(resolve, 800)); console.log(`✅ tapElement with ${test.strategy} successful`); break; // Only test one successful strategy } } catch (error) { console.log( `tapElement failed with ${test.strategy}:`, error instanceof Error ? error.message : error ); } } console.log( `✅ tapElement tests completed: ${successfulTaps} menu items successfully tapped` ); testResults["tap_element_method"] = true; console.log("✅ tapElement method: PASSED\n"); } catch (error) { testResults["tap_element_method"] = false; console.log("❌ tapElement method: FAILED -", error); } // Test 17: Script Execution console.log("📱 Test 17: Script Execution"); try { // Execute mobile command const result = await appium.executeScript("mobile: deviceInfo", []); console.log("Device info result:", result); testResults["script_execution"] = true; console.log("✅ Script execution: PASSED\n"); } catch (error) { testResults["script_execution"] = false; console.log("❌ Script execution: FAILED -", error); } // Test 18: Orientation console.log("📱 Test 18: Orientation Control"); try { const currentOrientation = await appium.getOrientation(); console.log("Current orientation:", currentOrientation); // Try to change orientation const newOrientation = currentOrientation === "PORTRAIT" ? "LANDSCAPE" : "PORTRAIT"; await appium.setOrientation(newOrientation); await new Promise((resolve) => setTimeout(resolve, 3000)); const changedOrientation = await appium.getOrientation(); console.log("Changed orientation:", changedOrientation); // Restore original orientation await appium.setOrientation(currentOrientation); await new Promise((resolve) => setTimeout(resolve, 3000)); testResults["orientation"] = true; console.log("✅ Orientation control: PASSED\n"); } catch (error) { testResults["orientation"] = false; console.log("❌ Orientation control: FAILED -", error); } // Test 19: Wait Functions console.log("📱 Test 19: Wait Functions"); try { const element = await appium.waitForElement( "//android.widget.TextView", "xpath", 5000 ); console.log("Wait for element successful:", !!element); const isClickable = await appium.waitUntilElementClickable( "//android.widget.TextView", "xpath", 5000 ); console.log("Wait until clickable successful:", isClickable); testResults["wait_functions"] = true; console.log("✅ Wait functions: PASSED\n"); } catch (error) { testResults["wait_functions"] = false; console.log("❌ Wait functions: FAILED -", error); } // Test 20: Error Handling console.log("📱 Test 20: Error Handling"); try { // Try to find non-existent element try { await appium.findElement( '//android.widget.NonExistentElement[@text="NonExistent"]', "xpath", 2000 ); testResults["error_handling"] = false; } catch (error) { if (error instanceof AppiumError) { console.log("AppiumError correctly thrown:", error.message); testResults["error_handling"] = true; } else { testResults["error_handling"] = false; } } console.log("✅ Error handling: PASSED\n"); } catch (error) { testResults["error_handling"] = false; console.log("❌ Error handling: FAILED -", error); } } catch (error) { console.error("❌ Critical test failure:", error); } finally { // Cleanup console.log("🧹 Cleaning up..."); try { await appium.closeDriver(); console.log("✅ Driver closed successfully"); } catch (error) { console.log("⚠️ Error closing driver:", error); } } // Print test results summary console.log("\n" + "=".repeat(60)); console.log("📊 TEST RESULTS SUMMARY"); console.log("=".repeat(60)); let passed = 0; let total = 0; for (const [testName, result] of Object.entries(testResults)) { total++; if (result) passed++; console.log( `${result ? "✅" : "❌"} ${testName.replace(/_/g, " ")}: ${ result ? "PASSED" : "FAILED" }` ); } console.log("=".repeat(60)); console.log( `📈 TOTAL: ${passed}/${total} tests passed (${Math.round( (passed / total) * 100 )}%)` ); console.log("=".repeat(60)); if (passed === total) { console.log("🎉 All tests passed! AppiumHelper is working correctly."); } else { console.log("⚠️ Some tests failed. Check the logs above for details."); } return testResults; } /** * Quick smoke test for basic functionality */ async function quickSmokeTest() { const appium = new AppiumHelper("./test-screenshots"); try { console.log("🔥 Running quick smoke test...\n"); const capabilities: AppiumCapabilities = { platformName: "Android", "appium:deviceName": "Pixel_4", "appium:automationName": "UiAutomator2", "appium:appPackage": "com.android.settings", "appium:appActivity": ".Settings", "appium:noReset": true, }; // Use environment variable or default const appiumUrl = process.env.APPIUM_SERVER_URL || "http://localhost:4723"; await appium.initializeDriver(capabilities, appiumUrl); console.log("✅ Driver initialized"); const screenshot = await appium.takeScreenshot("smoke_test"); console.log("✅ Screenshot taken:", screenshot); const pageSource = await appium.getPageSource(); console.log("✅ Page source retrieved, length:", pageSource.length); const elements = await appium.findElements("//android.widget.TextView"); console.log("✅ Found elements:", elements.length); await appium.scroll("down"); console.log("✅ Scroll performed"); await appium.closeDriver(); console.log("✅ Driver closed"); console.log("\n🎉 Smoke test completed successfully!"); } catch (error) { console.error("❌ Smoke test failed:", error); try { await appium.closeDriver(); } catch {} } } // Export test functions export { testAppiumHelperComprehensive, quickSmokeTest }; // Run tests if this file is executed directly if (import.meta.url === `file://${process.argv[1]}`) { const testType = process.argv[2] || "comprehensive"; if (testType === "smoke") { quickSmokeTest().catch(console.error); } else { testAppiumHelperComprehensive().catch(console.error); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Rahulec08/appium-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server