Skip to content

Elements and text

Examples are in Python

Kotlin has the same methods in camelCase (set_text → setText, long_tap → longTap), called inside tapTest { }. Kotlin + JUnit 5 shows this page's examples in Kotlin.

An Element is a selector plus a device: app.element(selector) for one app's nodes, device.screen.element(selector) for the whole screen (App or screen). Building one does nothing on the device; every call on it finds the node again. The examples use app = device.app("com.shop").

Every call takes an optional timeout in seconds. Actions default to the device's action timeout (10 s), waits to its wait timeout (10 s), app lifecycle calls to 30 s. The timeout is the deadline of the whole command on the device, including the lookup; nothing else sleeps.

Reading an element

Method Returns
exists() whether at least one node matches right now
count() how many nodes match
snapshot() text, content description, class, bounds, enabled / checked / focused / … of the single match
text() the node's text as Android reports it, or None when it has none. An empty field reports its hint here; snapshot().showing_hint says so
is_enabled(), is_checked() shortcuts on snapshot()

exists() and count() accept any number of matches; snapshot() and every action below need exactly one (Selectors).

Taps

Method What happens on the device
tap() a click at the centre of the node's visible bounds, whether or not the node is enabled (wait for enabled() first if it matters)
long_tap() a long click, likewise
double_tap() two taps inside Android's double-tap window, as one gesture

A tap whose point another window covers fails with OBSCURED before any input (Covered elements). For swipes, scrolls, drags and pinches, see Gestures.

Text

Method What happens on the device
set_text(value) accessibility set-text on the node; ACTION_REJECTED only if the node refuses it
type_text(value) three client-side steps: tap(), then wait().focused() (skip it with await_focus=False), then device.type_text(value)
clear_text() set_text("")
device.type_text(value) types character by character with real key events (no IME) into whatever has input focus now: no target, no click, no settling. Characters outside Android's virtual key map, such as emoji, are rejected before input with INVALID_REQUEST / UNSUPPORTED_CHARACTERS
ime_action() runs the field's keyboard action key (Search, Go, Send, Done, …: whatever the app configured), as the keyboard's own key does. The field must have input focus, so tap() it first; a node that does not offer the action fails with ACTION_REJECTED before input. Android 11+ (UNSUPPORTED below). Pressing Enter is not the same: apps waiting for "Search" ignore it

Text actions report only what Android said about the input, never what the app did with it: apps reformat, truncate, reject or copy text elsewhere, and Tap assumes none of that. The field is not read back. Assert the outcome with a selector that still identifies the field after the edit (its resource id, not its old text):

email = app.element(res("email"))
email.set_text("user@example.com")
email.wait().text_equals("user@example.com")

Accessibility actions and sliders

Method What happens on the device
perform_action(action) runs one of the node's accessibility actions as a screen reader does, with no touch: a StandardAction (EXPAND, COLLAPSE, DISMISS, SCROLL_FORWARD, PAGE_DOWN, SELECT, COPY, …)
perform_custom_action(label) an app-defined custom action by its label ("Archive", "Mark unread")
set_progress(value) sets a slider, seek bar or rating bar to value in its own units (snapshot().range gives the type, min, max and current value). A value outside the range fails with ACTION_REJECTED / OUT_OF_RANGE instead of being clamped

An action the node does not offer fails with ACTION_REJECTED / ACTION_NOT_OFFERED before input; snapshot().actions and custom_actions list what it offers. Page actions need Android 10, press-and-hold Android 11 (UNSUPPORTED below).

from tap_e2e import StandardAction

app.element(text("Shipping")).perform_action(StandardAction.EXPAND)
app.element(res("message_row").first()).perform_custom_action("Archive")
app.element(res("volume")).set_progress(7)

Before or after input

An action that fails before input (NOT_FOUND, AMBIGUOUS, INVALID_*, STALE_BEFORE_INPUT, and NOT_INTERACTABLE, including OBSCURED) has changed nothing. An action that fails after input says so: STALE_DURING_COMMAND (the target changed mid-action), ACTION_REJECTED (Android refused issued input; this does not tell you whether earlier input took effect), INDETERMINATE (the transport dropped after the driver accepted the mutation). Tap never re-sends any of them for you. Errors lists every code.