A simple, easy-to-use, and stable Android automation library
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.
Access XPath functionality through the device xpath property.
class Device:
@cached_property
def xpath(self) -> xpath.XPathEntry:
"""XPath-based element selection entry point"""
class XPathEntry:
def __call__(self, xpath_expression: str) -> XPathSelector:
"""
Create XPath selector.
Parameters:
- xpath_expression: XPath query string
Returns:
XPathSelector for element interaction
"""
def dump_hierarchy(self) -> str:
"""Get UI hierarchy XML for XPath debugging"""Usage examples:
d = u2.connect()
# Basic XPath selection
element = d.xpath('//*[@text="Settings"]')
button = d.xpath('//android.widget.Button[@text="OK"]')
# Complex XPath queries
first_item = d.xpath('//android.widget.ListView/android.widget.LinearLayout[1]')
checked_box = d.xpath('//android.widget.CheckBox[@checked="true"]')
# Debug hierarchy
hierarchy = d.xpath.dump_hierarchy()
print(hierarchy) # XML representation of UISelect elements using XPath expressions with support for attributes, hierarchy, and logical operators.
class XPathSelector:
def click(self, timeout: Optional[float] = None):
"""Click the element found by XPath"""
def long_click(self, duration: float = 0.5):
"""Long click the element"""
def exists(self, timeout: Optional[float] = None) -> bool:
"""Check if element exists"""
def wait(self, timeout: Optional[float] = None) -> bool:
"""Wait for element to appear"""
def wait_gone(self, timeout: Optional[float] = None) -> bool:
"""Wait for element to disappear"""
def get_text(self) -> str:
"""Get element text content"""
def get(self, attribute: str) -> str:
"""Get element attribute value"""
def set_text(self, text: str):
"""Set element text content"""
def screenshot(self) -> Image.Image:
"""Take screenshot of element"""Frequently used XPath expressions for Android UI automation.
d = u2.connect()
# Text-based selection
d.xpath('//*[@text="Login"]').click()
d.xpath('//*[contains(@text, "Welcome")]').click()
d.xpath('//*[starts-with(@text, "User")]').click()
# Resource ID selection
d.xpath('//*[@resource-id="com.example:id/button"]').click()
d.xpath('//*[contains(@resource-id, "submit")]').click()
# Class-based selection
d.xpath('//android.widget.Button').click()
d.xpath('//android.widget.EditText[@enabled="true"]').set_text("input")
# Hierarchy-based selection
d.xpath('//android.widget.ListView/android.widget.LinearLayout[1]').click()
d.xpath('//android.widget.ScrollView//android.widget.Button').click()
# Attribute combinations
d.xpath('//*[@clickable="true" and @enabled="true"]').click()
d.xpath('//*[@text="Submit" or @text="Send"]').click()
# Index-based selection
d.xpath('(//android.widget.Button)[1]').click() # First button
d.xpath('(//android.widget.Button)[last()]').click() # Last button
# Parent/child relationships
d.xpath('//android.widget.TextView[@text="Label"]/../android.widget.EditText').set_text("value")
d.xpath('//android.widget.LinearLayout[android.widget.TextView[@text="Title"]]').click()Access element properties and attributes through XPath selections.
d = u2.connect()
# Get element text
title = d.xpath('//*[@resource-id="title"]').get_text()
print(f"Title: {title}")
# Get element attributes
element = d.xpath('//android.widget.Button[@text="Submit"]')
bounds = element.get('bounds')
class_name = element.get('class')
enabled = element.get('enabled')
print(f"Bounds: {bounds}")
print(f"Class: {class_name}")
print(f"Enabled: {enabled}")
# Check element state
if d.xpath('//*[@checked="true"]').exists():
print("Checkbox is checked")
# Wait for elements
success = d.xpath('//*[@text="Success"]').wait(timeout=10)
if success:
print("Success message appeared")Text input and advanced interaction through XPath-selected elements.
d = u2.connect()
# Text input
username_field = d.xpath('//*[@resource-id="username"]')
username_field.set_text("john_doe")
password_field = d.xpath('//*[@resource-id="password"]')
password_field.set_text("secret123")
# Clear and set text
search_box = d.xpath('//*[@class="android.widget.EditText"]')
search_box.set_text("") # Clear
search_box.set_text("new search term")
# Click variations
d.xpath('//*[@text="Login"]').click()
d.xpath('//*[@text="Options"]').long_click(duration=2.0)
# Element screenshots
element_img = d.xpath('//*[@resource-id="logo"]').screenshot()
element_img.save("logo.png")Tools and techniques for debugging XPath expressions and UI hierarchy issues.
d = u2.connect()
# Dump UI hierarchy for analysis
hierarchy_xml = d.xpath.dump_hierarchy()
with open("hierarchy.xml", "w", encoding="utf-8") as f:
f.write(hierarchy_xml)
# Check if XPath matches any elements
xpath_expr = '//*[@text="Submit"]'
if d.xpath(xpath_expr).exists():
print(f"XPath '{xpath_expr}' matches elements")
else:
print(f"XPath '{xpath_expr}' matches no elements")
# Get all matching elements count
buttons = d.xpath('//android.widget.Button')
print(f"Found {len(buttons)} buttons")
# Test element visibility and properties
element = d.xpath('//*[@resource-id="target"]')
if element.exists():
print(f"Element text: {element.get_text()}")
print(f"Element bounds: {element.get('bounds')}")
print(f"Element enabled: {element.get('enabled')}")Advanced XPath functionality for complex UI automation scenarios.
d = u2.connect()
# Position-based selection
d.xpath('//android.widget.Button[position()=1]').click() # First button
d.xpath('//android.widget.Button[position()=last()]').click() # Last button
d.xpath('//android.widget.Button[position()>2]').click() # Buttons after second
# Text content functions
d.xpath('//*[text()="Exact Match"]').click()
d.xpath('//*[contains(text(), "Partial")]').click()
d.xpath('//*[starts-with(text(), "Prefix")]').click()
d.xpath('//*[string-length(text())>10]').click() # Text longer than 10 chars
# Multiple attribute conditions
d.xpath('//*[@enabled="true" and @clickable="true" and contains(@text, "Submit")]').click()
# Sibling navigation
d.xpath('//android.widget.TextView[@text="Label"]/following-sibling::android.widget.EditText').set_text("value")
d.xpath('//android.widget.Button[@text="Cancel"]/preceding-sibling::android.widget.Button').click()
# Ancestor/descendant navigation
d.xpath('//android.widget.ScrollView//android.widget.Button[@text="Deep Button"]').click()
d.xpath('//android.widget.LinearLayout[.//android.widget.TextView[@text="Container"]]').click()All XPath methods and capabilities for comprehensive element selection.
class XPathEntry:
def __call__(self, xpath: str, timeout: float = 10) -> XPathSelector:
"""
Select elements using XPath expression.
Parameters:
- xpath: XPath expression string
- timeout: Maximum wait time for element
Returns:
XPathSelector for element interaction
"""
def exists(self, xpath: str) -> bool:
"""Check if XPath element exists"""
def get(self, xpath: str, timeout: float = 10) -> XMLElement:
"""Get single element by XPath"""
def all(self, xpath: str) -> List[XMLElement]:
"""Get all matching elements by XPath"""
def wait(self, xpath: str, timeout: float = 10) -> XMLElement:
"""Wait for XPath element to appear"""
class XPathSelector:
@property
def exists(self) -> bool:
"""Check if element exists"""
def get(self, timeout: float = 10) -> XMLElement:
"""Get single element with timeout"""
def get_last_match(self) -> XMLElement:
"""Get last matched element from cache"""
def get_text(self) -> str:
"""Get element text content"""
def set_text(self, text: str):
"""Set element text content"""
def click(self):
"""Click the element"""
def all(self) -> List[XMLElement]:
"""Get all matching elements"""
def wait(self, timeout: float = 10) -> XMLElement:
"""Wait for element to appear"""
class XMLElement:
@property
def tag(self) -> str:
"""Element tag name"""
@property
def text(self) -> str:
"""Element text content"""
@property
def attrib(self) -> Dict[str, str]:
"""Element attributes dictionary"""
@property
def bounds(self) -> Tuple[int, int, int, int]:
"""Element bounds (left, top, right, bottom)"""
@property
def center(self) -> Tuple[int, int]:
"""Element center coordinates (x, y)"""
def get(self, key: str, default: Any = None) -> Any:
"""Get attribute value by key"""
def click(self):
"""Click the element"""
def screenshot(self) -> Image.Image:
"""Take screenshot of element"""Usage examples for complete XPath API:
d = u2.connect()
# Direct XPath operations
xpath_entry = d.xpath
elements = xpath_entry.all('//android.widget.Button')
if xpath_entry.exists('//*[@text="Login"]'):
login_element = xpath_entry.get('//*[@text="Login"]')
login_element.click()
# XPath selector operations
selector = d.xpath('//android.widget.EditText[@resource-id="username"]')
if selector.exists:
element = selector.get(timeout=5)
element.click()
selector.set_text("user123")
# XMLElement operations
button = d.xpath('//*[@text="Submit"]').get()
print(f"Button tag: {button.tag}")
print(f"Button text: {button.text}")
print(f"Button bounds: {button.bounds}")
print(f"Button center: {button.center}")
print(f"Button enabled: {button.get('enabled', 'unknown')}")
# Take element screenshot
element_image = button.screenshot()
element_image.save("button_screenshot.png")
# Wait operations with timeout
try:
loading_element = d.xpath('//*[@text="Loading..."]').wait(timeout=30)
print("Loading element appeared")
except Exception:
print("Loading element did not appear within 30 seconds")Install with Tessl CLI
npx tessl i tessl/pypi-uiautomator2