0
# XPath-based Element Selection
1
2
Advanced XPath-based element selection providing powerful querying capabilities for complex UI hierarchies and dynamic content. XPath selection offers more flexible element targeting than traditional UiAutomator selectors.
3
4
## Capabilities
5
6
### XPath Entry Point
7
8
Access XPath functionality through the device xpath property.
9
10
```python { .api }
11
class Device:
12
@cached_property
13
def xpath(self) -> xpath.XPathEntry:
14
"""XPath-based element selection entry point"""
15
16
class XPathEntry:
17
def __call__(self, xpath_expression: str) -> XPathSelector:
18
"""
19
Create XPath selector.
20
21
Parameters:
22
- xpath_expression: XPath query string
23
24
Returns:
25
XPathSelector for element interaction
26
"""
27
28
def dump_hierarchy(self) -> str:
29
"""Get UI hierarchy XML for XPath debugging"""
30
```
31
32
Usage examples:
33
34
```python
35
d = u2.connect()
36
37
# Basic XPath selection
38
element = d.xpath('//*[@text="Settings"]')
39
button = d.xpath('//android.widget.Button[@text="OK"]')
40
41
# Complex XPath queries
42
first_item = d.xpath('//android.widget.ListView/android.widget.LinearLayout[1]')
43
checked_box = d.xpath('//android.widget.CheckBox[@checked="true"]')
44
45
# Debug hierarchy
46
hierarchy = d.xpath.dump_hierarchy()
47
print(hierarchy) # XML representation of UI
48
```
49
50
### XPath Element Selection
51
52
Select elements using XPath expressions with support for attributes, hierarchy, and logical operators.
53
54
```python { .api }
55
class XPathSelector:
56
def click(self, timeout: Optional[float] = None):
57
"""Click the element found by XPath"""
58
59
def long_click(self, duration: float = 0.5):
60
"""Long click the element"""
61
62
def exists(self, timeout: Optional[float] = None) -> bool:
63
"""Check if element exists"""
64
65
def wait(self, timeout: Optional[float] = None) -> bool:
66
"""Wait for element to appear"""
67
68
def wait_gone(self, timeout: Optional[float] = None) -> bool:
69
"""Wait for element to disappear"""
70
71
def get_text(self) -> str:
72
"""Get element text content"""
73
74
def get(self, attribute: str) -> str:
75
"""Get element attribute value"""
76
77
def set_text(self, text: str):
78
"""Set element text content"""
79
80
def screenshot(self) -> Image.Image:
81
"""Take screenshot of element"""
82
```
83
84
### Common XPath Patterns
85
86
Frequently used XPath expressions for Android UI automation.
87
88
```python
89
d = u2.connect()
90
91
# Text-based selection
92
d.xpath('//*[@text="Login"]').click()
93
d.xpath('//*[contains(@text, "Welcome")]').click()
94
d.xpath('//*[starts-with(@text, "User")]').click()
95
96
# Resource ID selection
97
d.xpath('//*[@resource-id="com.example:id/button"]').click()
98
d.xpath('//*[contains(@resource-id, "submit")]').click()
99
100
# Class-based selection
101
d.xpath('//android.widget.Button').click()
102
d.xpath('//android.widget.EditText[@enabled="true"]').set_text("input")
103
104
# Hierarchy-based selection
105
d.xpath('//android.widget.ListView/android.widget.LinearLayout[1]').click()
106
d.xpath('//android.widget.ScrollView//android.widget.Button').click()
107
108
# Attribute combinations
109
d.xpath('//*[@clickable="true" and @enabled="true"]').click()
110
d.xpath('//*[@text="Submit" or @text="Send"]').click()
111
112
# Index-based selection
113
d.xpath('(//android.widget.Button)[1]').click() # First button
114
d.xpath('(//android.widget.Button)[last()]').click() # Last button
115
116
# Parent/child relationships
117
d.xpath('//android.widget.TextView[@text="Label"]/../android.widget.EditText').set_text("value")
118
d.xpath('//android.widget.LinearLayout[android.widget.TextView[@text="Title"]]').click()
119
```
120
121
### XPath Element Information
122
123
Access element properties and attributes through XPath selections.
124
125
```python
126
d = u2.connect()
127
128
# Get element text
129
title = d.xpath('//*[@resource-id="title"]').get_text()
130
print(f"Title: {title}")
131
132
# Get element attributes
133
element = d.xpath('//android.widget.Button[@text="Submit"]')
134
bounds = element.get('bounds')
135
class_name = element.get('class')
136
enabled = element.get('enabled')
137
138
print(f"Bounds: {bounds}")
139
print(f"Class: {class_name}")
140
print(f"Enabled: {enabled}")
141
142
# Check element state
143
if d.xpath('//*[@checked="true"]').exists():
144
print("Checkbox is checked")
145
146
# Wait for elements
147
success = d.xpath('//*[@text="Success"]').wait(timeout=10)
148
if success:
149
print("Success message appeared")
150
```
151
152
### XPath Text Input and Interaction
153
154
Text input and advanced interaction through XPath-selected elements.
155
156
```python
157
d = u2.connect()
158
159
# Text input
160
username_field = d.xpath('//*[@resource-id="username"]')
161
username_field.set_text("john_doe")
162
163
password_field = d.xpath('//*[@resource-id="password"]')
164
password_field.set_text("secret123")
165
166
# Clear and set text
167
search_box = d.xpath('//*[@class="android.widget.EditText"]')
168
search_box.set_text("") # Clear
169
search_box.set_text("new search term")
170
171
# Click variations
172
d.xpath('//*[@text="Login"]').click()
173
d.xpath('//*[@text="Options"]').long_click(duration=2.0)
174
175
# Element screenshots
176
element_img = d.xpath('//*[@resource-id="logo"]').screenshot()
177
element_img.save("logo.png")
178
```
179
180
### XPath Debugging and Troubleshooting
181
182
Tools and techniques for debugging XPath expressions and UI hierarchy issues.
183
184
```python
185
d = u2.connect()
186
187
# Dump UI hierarchy for analysis
188
hierarchy_xml = d.xpath.dump_hierarchy()
189
with open("hierarchy.xml", "w", encoding="utf-8") as f:
190
f.write(hierarchy_xml)
191
192
# Check if XPath matches any elements
193
xpath_expr = '//*[@text="Submit"]'
194
if d.xpath(xpath_expr).exists():
195
print(f"XPath '{xpath_expr}' matches elements")
196
else:
197
print(f"XPath '{xpath_expr}' matches no elements")
198
199
# Get all matching elements count
200
buttons = d.xpath('//android.widget.Button')
201
print(f"Found {len(buttons)} buttons")
202
203
# Test element visibility and properties
204
element = d.xpath('//*[@resource-id="target"]')
205
if element.exists():
206
print(f"Element text: {element.get_text()}")
207
print(f"Element bounds: {element.get('bounds')}")
208
print(f"Element enabled: {element.get('enabled')}")
209
```
210
211
### Advanced XPath Features
212
213
Advanced XPath functionality for complex UI automation scenarios.
214
215
```python
216
d = u2.connect()
217
218
# Position-based selection
219
d.xpath('//android.widget.Button[position()=1]').click() # First button
220
d.xpath('//android.widget.Button[position()=last()]').click() # Last button
221
d.xpath('//android.widget.Button[position()>2]').click() # Buttons after second
222
223
# Text content functions
224
d.xpath('//*[text()="Exact Match"]').click()
225
d.xpath('//*[contains(text(), "Partial")]').click()
226
d.xpath('//*[starts-with(text(), "Prefix")]').click()
227
d.xpath('//*[string-length(text())>10]').click() # Text longer than 10 chars
228
229
# Multiple attribute conditions
230
d.xpath('//*[@enabled="true" and @clickable="true" and contains(@text, "Submit")]').click()
231
232
# Sibling navigation
233
d.xpath('//android.widget.TextView[@text="Label"]/following-sibling::android.widget.EditText').set_text("value")
234
d.xpath('//android.widget.Button[@text="Cancel"]/preceding-sibling::android.widget.Button').click()
235
236
# Ancestor/descendant navigation
237
d.xpath('//android.widget.ScrollView//android.widget.Button[@text="Deep Button"]').click()
238
d.xpath('//android.widget.LinearLayout[.//android.widget.TextView[@text="Container"]]').click()
239
```
240
241
### Complete XPath API
242
243
All XPath methods and capabilities for comprehensive element selection.
244
245
```python { .api }
246
class XPathEntry:
247
def __call__(self, xpath: str, timeout: float = 10) -> XPathSelector:
248
"""
249
Select elements using XPath expression.
250
251
Parameters:
252
- xpath: XPath expression string
253
- timeout: Maximum wait time for element
254
255
Returns:
256
XPathSelector for element interaction
257
"""
258
259
def exists(self, xpath: str) -> bool:
260
"""Check if XPath element exists"""
261
262
def get(self, xpath: str, timeout: float = 10) -> XMLElement:
263
"""Get single element by XPath"""
264
265
def all(self, xpath: str) -> List[XMLElement]:
266
"""Get all matching elements by XPath"""
267
268
def wait(self, xpath: str, timeout: float = 10) -> XMLElement:
269
"""Wait for XPath element to appear"""
270
271
class XPathSelector:
272
@property
273
def exists(self) -> bool:
274
"""Check if element exists"""
275
276
def get(self, timeout: float = 10) -> XMLElement:
277
"""Get single element with timeout"""
278
279
def get_last_match(self) -> XMLElement:
280
"""Get last matched element from cache"""
281
282
def get_text(self) -> str:
283
"""Get element text content"""
284
285
def set_text(self, text: str):
286
"""Set element text content"""
287
288
def click(self):
289
"""Click the element"""
290
291
def all(self) -> List[XMLElement]:
292
"""Get all matching elements"""
293
294
def wait(self, timeout: float = 10) -> XMLElement:
295
"""Wait for element to appear"""
296
297
class XMLElement:
298
@property
299
def tag(self) -> str:
300
"""Element tag name"""
301
302
@property
303
def text(self) -> str:
304
"""Element text content"""
305
306
@property
307
def attrib(self) -> Dict[str, str]:
308
"""Element attributes dictionary"""
309
310
@property
311
def bounds(self) -> Tuple[int, int, int, int]:
312
"""Element bounds (left, top, right, bottom)"""
313
314
@property
315
def center(self) -> Tuple[int, int]:
316
"""Element center coordinates (x, y)"""
317
318
def get(self, key: str, default: Any = None) -> Any:
319
"""Get attribute value by key"""
320
321
def click(self):
322
"""Click the element"""
323
324
def screenshot(self) -> Image.Image:
325
"""Take screenshot of element"""
326
```
327
328
Usage examples for complete XPath API:
329
330
```python
331
d = u2.connect()
332
333
# Direct XPath operations
334
xpath_entry = d.xpath
335
elements = xpath_entry.all('//android.widget.Button')
336
if xpath_entry.exists('//*[@text="Login"]'):
337
login_element = xpath_entry.get('//*[@text="Login"]')
338
login_element.click()
339
340
# XPath selector operations
341
selector = d.xpath('//android.widget.EditText[@resource-id="username"]')
342
if selector.exists:
343
element = selector.get(timeout=5)
344
element.click()
345
selector.set_text("user123")
346
347
# XMLElement operations
348
button = d.xpath('//*[@text="Submit"]').get()
349
print(f"Button tag: {button.tag}")
350
print(f"Button text: {button.text}")
351
print(f"Button bounds: {button.bounds}")
352
print(f"Button center: {button.center}")
353
print(f"Button enabled: {button.get('enabled', 'unknown')}")
354
355
# Take element screenshot
356
element_image = button.screenshot()
357
element_image.save("button_screenshot.png")
358
359
# Wait operations with timeout
360
try:
361
loading_element = d.xpath('//*[@text="Loading..."]').wait(timeout=30)
362
print("Loading element appeared")
363
except Exception:
364
print("Loading element did not appear within 30 seconds")
365
```