Locators
Locators identify UI elements on the device. They are exposed as Playwright-style getBy* methods on Device and ElementHandle. Each method returns an ElementHandle — a lazy reference that resolves when an action or assertion runs against it.
See the Selectors Guide for a deeper discussion of when to use each one.
device.getByText(text: string, options?: { exact?: boolean }): ElementHandle
Section titled “device.getByText(text: string, options?: { exact?: boolean }): ElementHandle”Locate an element by its visible text. Substring match by default, like Playwright. Pass { exact: true } for an exact match.
device.getByText("Welcome") // substringdevice.getByText("Sign In", { exact: true }) // exactdevice.getByRole(role: string, options?): ElementHandle
Section titled “device.getByRole(role: string, options?): ElementHandle”Locate an element by its accessibility role, optionally filtered by accessible name or state.
device.getByRole("button", { name: "Submit" })device.getByRole("textfield", { name: "Email" })device.getByRole("checkbox")device.getByRole("switch", { name: "Dark Mode", checked: true })device.getByRole("button", { name: "Submit", disabled: true })device.getByRole("tab", { name: "Settings", selected: true })device.getByRole("button", { name: "Details", expanded: true })Options:
| Option | Type | Description |
|---|---|---|
name | string | Filter by accessible name |
checked | boolean | Filter by checked state (checkbox, switch, radio) |
disabled | boolean | Filter by disabled state |
selected | boolean | Filter by selected state (tab, option) |
expanded | boolean | Filter by expanded state (accordion, dropdown) |
device.getByDescription(text: string): ElementHandle
Section titled “device.getByDescription(text: string): ElementHandle”Locate an element by its accessibility description (Android contentDescription, iOS accessibilityLabel).
device.getByDescription("Close menu")device.getByDescription("Profile photo")device.getByPlaceholder(text: string): ElementHandle
Section titled “device.getByPlaceholder(text: string): ElementHandle”Locate an input by its placeholder / hint text.
device.getByPlaceholder("Enter your email")device.getByPlaceholder("Search")device.getByTestId(testId: string): ElementHandle
Section titled “device.getByTestId(testId: string): ElementHandle”Locate an element by its dedicated test identifier.
device.getByTestId("submit-button")device.getByLabel(text: string): ElementHandle
Section titled “device.getByLabel(text: string): ElementHandle”Locate an input element by its associated label text. Finds form controls (text fields, checkboxes, switches, etc.) whose accessible name matches the label.
- Android: matches inputs whose
contentDescriptionequals the text, or inputs linked vialabelFor/labeledBy. - iOS: matches input elements (text fields, switches, sliders, etc.) whose
accessibilityLabelequals the text.
device.getByLabel("Email") // finds the email text fielddevice.getByLabel("Dark Mode") // finds the Dark Mode switchdevice.getByLabel("Volume") // finds the Volume sliderdevice.locator(options: LocatorOptions): ElementHandle
Section titled “device.locator(options: LocatorOptions): ElementHandle”Escape hatch for native, non-accessible queries. Exactly one of id, xpath, or className must be set.
device.locator({ id: "com.myapp:id/email_input" })device.locator({ className: "com.myapp.widget.ColorPicker" })// XPath is Android-only. Always include a comment explaining why.device.locator({ xpath: "//android.widget.Button[@text='OK']" })LocatorOptions:
| Option | Type | Description |
|---|---|---|
id | string | Native resource id (Android R.id.foo or iOS accessibilityIdentifier). |
xpath | string | XPath expression. Android-only. |
className | string | Native widget class name. |
The
getBy*methods andlocator()are also available on everyElementHandle. Calling them on a parent locator scopes the search to its descendants. See ElementHandle Scoping.
iOS wrapper suppression. When traversing the iOS accessibility tree, Tapsmith drops a matching
XCUIElementTypeOthercontainer if a descendant also matches the same selector and shares the wrapper’saccessibilityIdentifier(or, when the wrapper’s identifier is empty, itsaccessibilityLabel). This collapses the redundant wrappers React Native (and SwiftUI in some configurations) emit around interactive elements, so agetByText("Submit")resolves to the actual control rather than the surrounding container. The visible text of the wrapper and descendant is not compared — identifier/label match alone is enough. If your native iOS app deliberately exposes an.othercontainer with the same identifier as a child you also want addressable, the outer wrapper will be silently suppressed in favour of the child — give the wrapper a uniqueaccessibilityIdentifier(or usedevice.locator({ id: ... })) to address it directly.