1"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7247],{22002:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>d});var s=i(7378);const t={},l=s.createContext(t);function r(e){const n=s.useContext(l);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function d(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:r(e.components),s.createElement(l.Provider,{value:n},e.children)}},46412:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>d,default:()=>a,frontMatter:()=>r,metadata:()=>s,toc:()=>o});const s=JSON.parse('{"id":"mcp/selectors","title":"Selectors","description":"The WebdriverIO MCP server supports multiple selector strategies for locating elements on web pages and mobile apps.","source":"@site/docs/mcp/selectors.md","sourceDirName":"mcp","slug":"/mcp/selectors","permalink":"/docs/mcp/selectors","draft":false,"unlisted":false,"editUrl":"https://github.com/webdriverio/webdriverio/edit/main/website/docs/mcp/selectors.md","tags":[],"version":"current","frontMatter":{"id":"selectors","title":"Selectors"},"sidebar":"docs","previous":{"title":"Cloud Providers","permalink":"/docs/mcp/cloud-providers"},"next":{"title":"Tools","permalink":"/docs/mcp/tools"}}');var t=i(86106),l=i(22002);const r={id:"selectors",title:"Selectors"},d=void 0,c={},o=[{value:"Web Selectors",id:"web-selectors",level:2},{value:"Mobile Selectors",id:"mobile-selectors",level:2},{value:"Accessibility ID (Recommended)",id:"accessibility-id-recommended",level:3},{value:"Android Selectors",id:"android-selectors",level:3},{value:"UiAutomator",id:"uiautomator",level:4},{value:"Resource ID",id:"resource-id",level:4},{value:"XPath (Android)",id:"xpath-android",level:4},{value:"iOS Selectors",id:"ios-selectors",level:3},{value:"Predicate String",id:"predicate-string",level:4},{value:"Class Chain",id:"class-chain",level:4},{value:"XPath (iOS)",id:"xpath-ios",level:4},{value:"Cross-Platform Selector Strategy",id:"cross-platform-selector-strategy",level:2},{value:"1. Accessibility ID (Best)",id:"1-accessibility-id-best",level:3},{value:"2. Platform-Specific with Conditional Logic",id:"2-platform-specific-with-conditional-logic",level:3},{value:"3. XPath (Last Resort)",id:"3-xpath-last-resort",level:3},{value:"Element Types Reference",id:"element-types-reference",level:2},{value:"Android Element Types",id:"android-element-types",level:3},{value:"iOS Element Types",id:"ios-element-types",level:3},{value:"Best Practices",id:"best-practices",level:2},{value:"Do",id:"do",level:3},{value:"Don't",id:"dont",level:3},{value:"Examples of Good vs Bad Selectors",id:"examples-of-good-vs-bad-selectors",level:3},{value:"Debugging Selectors",id:"debugging-selectors",level:2},{value:"Web (Chrome DevTools)",id:"web-chrome-devtools",level:3},{value:"Mobile (Appium Inspector)",id:"mobile-appium-inspector",level:3},{value:"Using <code>get_elements</code>",id:"using-get_elements",level:3},{value:"Advanced Options",id:"advanced-options",level:4},{value:"Using <code>get_accessibility</code> (Browser Only)",id:"using-get_accessibility-browser-only",level:3}];function h(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",h3:"h3",h4:"h4",hr:"hr",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,l.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.p,{children:"The WebdriverIO MCP server supports multiple selector strategies for locating elements on web pages and mobile apps."}),"\n",(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["For comprehensive selector documentation including all WebdriverIO selector strategies, see the main ",(0,t.jsx)(n.a,{href:"/docs/selectors",children:"Selectors"})," guide. This page focuses on selectors commonly used with the MCP server."]})}),"\n",(0,t.jsx)(n.h2,{id:"web-selectors",children:"Web Selectors"}),"\n",(0,t.jsx)(n.p,{children:"For browser automation, the MCP server supports all standard WebdriverIO selectors. The most commonly used include:"}),"\n",(0,t.jsxs)(n.table,{children:[(0,t.jsx)(n.thead,{children:(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.th,{children:"Selector"}),(0,t.jsx)(n.th,{children:"Example"}),(0,t.jsx)(n.th,{children:"Description"})]})}),(0,t.jsxs)(n.tbody,{children:[(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"CSS"}),(0,t.jsxs)(n.td,{children:[(0,t.jsx)(n.code,{children:"#login-button"}),", ",(0,t.jsx)(n.code,{children:".submit-btn"})]}),(0,t.jsx)(n.td,{children:"Standard CSS selectors"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"XPath"}),(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"//button[@id='submit']"})}),(0,t.jsx)(n.td,{children:"XPath expressions"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"Text"}),(0,t.jsxs)(n.td,{children:[(0,t.jsx)(n.code,{children:"button=Submit"}),", ",(0,t.jsx)(n.code,{children:"a*=Click"})]}),(0,t.jsx)(n.td,{children:"WebdriverIO text selectors"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"ARIA"}),(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"aria/Submit Button"})}),(0,t.jsx)(n.td,{children:"Accessibility name selectors"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"Test ID"}),(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:'[data-testid="submit"]'})}),(0,t.jsx)(n.td,{children:"Recommended for testing"})]})]})]}),"\n",(0,t.jsxs)(n.p,{children:["For detailed examples and best practices, see the ",(0,t.jsx)(n.a,{href:"/docs/selectors",children:"Selectors"})," documentation."]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"mobile-selectors",children:"Mobile Selectors"}),"\n",(0,t.jsx)(n.p,{children:"Mobile selectors work with both iOS and Android platforms through Appium."}),"\n",(0,t.jsx)(n.h3,{id:"accessibility-id-recommended",children:"Accessibility ID (Recommended)"}),"\n",(0,t.jsxs)(n.p,{children:["Accessibility IDs are the ",(0,t.jsx)(n.strong,{children:"most reliable cross-platform selector"}
1),". They work on both iOS and Android and are stable across app updates."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"# Syntax\n~accessibilityId\n\n# Examples\n~loginButton\n~submitForm\n~usernameField\n"})}),"\n",(0,t.jsxs)(n.admonition,{title:"Best Practice",type:"tip",children:[(0,t.jsx)(n.p,{children:"Always prefer accessibility IDs when available. They provide:"}),(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"Cross-platform compatibility (iOS + Android)"}),"\n",(0,t.jsx)(n.li,{children:"Stability across UI changes"}),"\n",(0,t.jsx)(n.li,{children:"Better test maintainability"}),"\n",(0,t.jsx)(n.li,{children:"Improved accessibility of your app"}),"\n"]})]}),"\n",(0,t.jsx)(n.h3,{id:"android-selectors",children:"Android Selectors"}),"\n",(0,t.jsx)(n.h4,{id:"uiautomator",children:"UiAutomator"}),"\n",(0,t.jsx)(n.p,{children:"UiAutomator selectors are powerful and fast for Android."}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'# By Text\nandroid=new UiSelector().text("Login")\n\n# By Partial Text\nandroid=new UiSelector().textContains("Log")\n\n# By Resource ID\nandroid=new UiSelector().resourceId("com.example:id/login_button")\n\n# By Class Name\nandroid=new UiSelector().className("android.widget.Button")\n\n# By Description (Accessibility)\nandroid=new UiSelector().description("Login button")\n\n# Combined Conditions\nandroid=new UiSelector().className("android.widget.Button").text("Login")\n\n# Scrollable Container\nandroid=new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().text("Item"))\n'})}),"\n",(0,t.jsx)(n.h4,{id:"resource-id",children:"Resource ID"}),"\n",(0,t.jsx)(n.p,{children:"Resource IDs provide stable element identification on Android."}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"# Full Resource ID\nid=com.example.app:id/login_button\n\n# Partial ID (app package inferred)\nid=login_button\n"})}
1),"\n",(0,t.jsx)(n.h4,{id:"xpath-android",children:"XPath (Android)"}),"\n",(0,t.jsx)(n.p,{children:"XPath works on Android but is slower than UiAutomator."}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"# By Class and Text\n//android.widget.Button[@text='Login']\n\n# By Resource ID\n//android.widget.EditText[@resource-id='com.example:id/username']\n\n# By Content Description\n//android.widget.ImageButton[@content-desc='Menu']\n\n# Hierarchical\n//android.widget.LinearLayout/android.widget.Button[1]\n"})}),"\n",(0,t.jsx)(n.h3,{id:"ios-selectors",children:"iOS Selectors"}),"\n",(0,t.jsx)(n.h4,{id:"predicate-string",children:"Predicate String"}),"\n",(0,t.jsx)(n.p,{children:"iOS Predicate Strings are fast and powerful for iOS automation."}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'# By Label\n-ios predicate string:label == "Login"\n\n# By Partial Label\n-ios predicate string:label CONTAINS "Log"\n\n# By Name\n-ios predicate string:name == "loginButton"\n\n# By Type\n-ios predicate string:type == "XCUIElementTypeButton"\n\n# By Value\n-ios predicate string:value == "ON"\n\n# Combined Conditions\n-ios predicate string:type == "XCUIElementTypeButton" AND label == "Login"\n\n# Visibility\n-ios predicate string:label == "Login" AND visible == 1\n\n# Case Insensitive\n-ios predicate string:label ==[c] "login"\n'})}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.strong,{children:"Predicate Operators:"})}),"\n",(0,t.jsxs)(n.table,{children:[(0,t.jsx)(n.thead,{children:(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.th,{children:"Operator"}),(0,t.jsx)(n.th,{children:"Description"})]})}),(0,t.jsxs)(n.tbody,{children:[(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"=="})}),(0,t.jsx)(n.td,{children:"Equals"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"!="})}),(0,t.jsx)(n.td,{children:"Not equals"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"CONTAINS"})}),(0,t.jsx)(n.td,{children:"Contains substring"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"BEGINSWITH"})}),(0,t.jsx)(n.td,{children:"Starts with"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"ENDSWITH"})}),(0,t.jsx)(n.td,{children:"Ends with"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"LIKE"})}),(0,t.jsx)(n.td,{children:"Wildcard match"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"MATCHES"})}),(0,t.jsx)(n.td,{children:"Regex match"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"AND"})}),(0,t.jsx)(n.td,{children:"Logical AND"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"OR"})}),(0,t.jsx)(n.td,{children:"Logical OR"})]})]})]}),"\n",(0,t.jsx)(n.h4,{id:"class-chain",children:"Class Chain"}),"\n",(0,t.jsx)(n.p,{children:"iOS Class Chains provide hierarchical element location with good performance."}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'# Direct Child\n-ios class chain:**/XCUIElementTypeButton[`label == "Login"`]\n\n# Any Descendant\n-ios class chain:**/XCUIElementTypeButton\n\n# By Index\n-ios class chain:**/XCUIElementTypeCell[3]\n\n# Combined with Predicate\n-ios class chain:**/XCUIElementTypeButton[`name == "submit" AND visible == 1`]\n\n# Hierarchical\n-ios class chain:**/XCUIElementTypeTable/XCUIElementTypeCell[`label == "Settings"`]\n\n# Last Element\n-ios class chain:**/XCUIElementTypeButton[-1]\n'})}
1),"\n",(0,t.jsx)(n.h4,{id:"xpath-ios",children:"XPath (iOS)"}),"\n",(0,t.jsx)(n.p,{children:"XPath works on iOS but is slower than predicate strings."}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"# By Type and Label\n//XCUIElementTypeButton[@label='Login']\n\n# By Name\n//XCUIElementTypeTextField[@name='username']\n\n# By Value\n//XCUIElementTypeSwitch[@value='1']\n\n# Hierarchical\n//XCUIElementTypeTable/XCUIElementTypeCell[1]\n"})}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"cross-platform-selector-strategy",children:"Cross-Platform Selector Strategy"}),"\n",(0,t.jsx)(n.p,{children:"When writing tests that need to work on both iOS and Android, use this priority order:"}),"\n",(0,t.jsx)(n.h3,{id:"1-accessibility-id-best",children:"1. Accessibility ID (Best)"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"# Works on both platforms\n~loginButton\n"})}),"\n",(0,t.jsx)(n.h3,{id:"2-platform-specific-with-conditional-logic",children:"2. Platform-Specific with Conditional Logic"}),"\n",(0,t.jsx)(n.p,{children:"When accessibility IDs aren't available, use platform-specific selectors:"}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.strong,{children:"Android:"})}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'android=new UiSelector().text("Login")\n'})}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.strong,{children:"iOS:"})}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'-ios predicate string:label == "Login"\n'})}),"\n",(0,t.jsx)(n.h3,{id:"3-xpath-last-resort",children:"3. XPath (Last Resort)"}),"\n",(0,t.jsx)(n.p,{children:"XPath works on both platforms but with different element types:"}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.strong,{children:"Android:"})}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"//android.widget.Button[@text='Login']\n"})}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.strong,{children:"iOS:"})}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"//XCUIElementTypeButton[@label='Login']\n"})}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"element-types-reference",children:"Element Types Reference"}),"\n",(0,t.jsx)(n.h3,{id:"android-element-types",children:"Android Element Types"}),"\n",(0,t.jsxs)(n.table,{children:[(0,t.jsx)(n.thead,{children:(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.th,{children:"Type"}),(0,t.jsx)(n.th,{children:"Description"})]})}),(0,t.jsxs)(n.tbody,{children:[(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"android.widget.Button"})}),(0,t.jsx)(n.td,{children:"Button"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"android.widget.EditText"})}),(0,t.jsx)(n.td,{children:"Text input"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"android.widget.TextView"})}),(0,t.jsx)(n.td,{children:"Text label"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"android.widget.ImageView"})}),(0,t.jsx)(n.td,{children:"Image"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"android.widget.ImageButton"})}),(0,t.jsx)(n.td,{children:"Image button"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"android.widget.CheckBox"})}),(0,t.jsx)(n.td,{children:"Checkbox"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"android.widget.RadioButton"})}),(0,t.jsx)(n.td,{children:"Radio button"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"android.widget.Switch"})}),(0,t.jsx)(n.td,{children:"Toggle switch"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"android.widget.Spinner"})}),(0,t.jsx)(n.td,{children:"Dropdown"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"android.widget.ListView"})}),(0,t.jsx)(n.td,{children:"List view"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"android.widget.RecyclerView"})}),(0,t.jsx)(n.td,{children:"Recycler view"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"android.widget.ScrollView"})}),(0,t.jsx)(n.td,{children:"Scroll container"})]})]})]}),"\n",(0,t.jsx)(n.h3,{id:"ios-element-types",children:"iOS Element Types"}),"\n",(0,t.jsxs)(n.table,{children:[(0,t.jsx)(n.thead,{children:(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.th,{children:"Type"}),(0,t.jsx)(n.th,{children:"Description"})]})}),(0,t.jsxs)(n.tbody,{children:[(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"XCUIElementTypeButton"})}),(0,t.jsx)(n.td,{children:"Button"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"XCUIElementTypeTextField"})}),(0,t.jsx)(n.td,{children:"Text input"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"XCUIElementTypeSecureTextField"})}),(0,t.jsx)(n.td,{children:"Password input"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"XCUIElementTypeStaticText"})}),(0,t.jsx)(n.td,{children:"Text label"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"XCUIElementTypeImage"})}),(0,t.jsx)(n.td,{children:"Image"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"XCUIElementTypeSwitch"})}),(0,t.jsx)(n.td,{children:"Toggle switch"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"XCUIElementTypeSlider"})}),(0,t.jsx)(n.td,{children:"Slider"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"XCUIElementTypePicker"})}),(0,t.jsx)(n.td,{children:"Picker wheel"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"XCUIElementTypeTable"})}),(0,t.jsx)(n.td,{children:"Table view"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"XCUIElementTypeCell"})}),(0,t.jsx)(n.td,{children:"Table cell"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"XCUIElementTypeCollectionView"})}),(0,t.jsx)(n.td,{children:"Collection view"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"XCUIElementTypeScrollView"})}),(0,t.jsx)(n.td,{children:"Scroll view"})]})]})]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"best-practices",children:"Best Practices"}),"\n",(0,t.jsx)(n.h3,{id:"do",children:"Do"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Use accessibility IDs"})," for stable, cross-platform selectors"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Add data-testid attributes"})," to web elements for testing"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Use resource IDs"})," on Android when accessibility IDs aren't available"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Prefer predicate strings"})," over XPath on iOS"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Keep selectors simple"})," and specific"]}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"dont",children:"Don't"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Avoid long XPath expressions"})," - they're slow and fragile"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Don't rely on indices"})," for dynamic lists"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Avoid text-based selectors"})," for localized apps"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Don't use absolute XPath"})," (starting from root)"]}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"examples-of-good-vs-bad-selectors",children:"Examples of Good vs Bad Selectors"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'# Good - Stable accessibility ID\n~loginButton\n\n# Bad - Fragile XPath with indices\n//div[3]/form/button[2]\n\n# Good - Specific CSS with test ID\n[data-testid="submit-button"]\n\n# Bad - Class that might change\n.btn-primary-lg-v2\n\n# Good - UiAutomator with resource ID\nandroid=new UiSelector().resourceId("com.app:id/submit")\n\n# Bad - Text that might be localized\nandroid=new UiSelector().text("Submit")\n'})}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"debugging-selectors",children:"Debugging Selectors"}),"\n",(0,t.jsx)(n.h3,{id:"web-chrome-devtools",children:"Web (Chrome DevTools)"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsx)(n.li,{children:"Open Chrome DevTools (F12)"}),"\n",(0,t.jsx)(n.li,{children:"Use the Elements panel to inspect elements"}),"\n",(0,t.jsx)(n.li,{children:"Right-click an element \u2192 Copy \u2192 Copy selector"}),"\n",(0,t.jsxs)(n.li,{children:["Test selectors in Console: ",(0,t.jsx)(n.code,{children:"document.querySelector('your-selector')"})]}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"mobile-appium-inspector",children:"Mobile (Appium Inspector)"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsx)(n.li,{children:"Start Appium Inspector"}),"\n",(0,t.jsx)(n.li,{children:"Connect to your running session"}),"\n",(0,t.jsx)(n.li,{children:"Click on elements to see all available attributes"}),"\n",(0,t.jsx)(n.li,{children:'Use the "Search for element" feature to test selectors'}),"\n"]}),"\n",(0,t.jsxs)(n.h3,{id:"using-get_elements",children:["Using ",(0,t.jsx)(n.code,{children:"get_elements"})]}),"\n",(0,t.jsxs)(n.p,{children:["The MCP server's ",(0,t.jsx)(n.code,{children:"get_elements"})," tool returns multiple selector strategies for each element:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'Ask: "Get all visible elements on the screen"\n'})}),"\n",(0,t.jsx)(n.p,{children:"This returns elements with pre-generated selectors you can use directly."}),"\n",(0,t.jsx)(n.h4,{id:"advanced-options",children:"Advanced Options"}),"\n",(0,t.jsx)(n.p,{children:"For more control over element discovery:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'# Get only images and visual elements\nGet visible elements with elementType "visual"\n\n# Get elements with their coordinates for layout debugging\nGet visible elements with includeBounds enabled\n\n# Get the next 20 elements (pagination)\nGet visible elements with limit 20 and offset 20\n\n# Include layout containers for debugging\nGet visible elements with includeContainers enabled\n'})}),"\n",(0,t.jsx)(n.p,{children:"The tool returns a paginated response:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-json",children:'{\n "total": 42,\n "showing": 20,\n "hasMore": true,\n "elements": [...]\n}\n'})}),"\n",(0,t.jsxs)(n.h3,{id:"using-get_accessibility-browser-only",children:["Using ",(0,t.jsx)(n.code,{children:"get_accessibility"})," (Browser Only)"]}),"\n",(0,t.jsxs)(n.p,{children:["For browser automation, the ",(0,t.jsx)(n.code,{children:"get_accessibility"})," tool provides semantic information about page elements:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"# Get all named accessibility nodes\nGet accessibility tree\n\n# Filter to only buttons and links\nGet accessibility tree filtered to button and link roles\n\n# Get next page of results\nGet accessibility tree with limit 50 and offset 50\n"})}),"\n",(0,t.jsxs)(n.p,{children:["This is useful when ",(0,t.jsx)(n.code,{children:"get_elements"})," doesn't return expected elements, as it queries the browser's native accessibility API."]})]})}function a(e={}){const{wrapper:n}={...(0,l.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.