Skip to main content

Locators

getByRole() lets a single test target the same element on both Android and iOS, even though each platform names its native classes differently. Mobilewright does this by normalizing the native type reported by the device and mapping it to a semantic role.

How a native type becomes a role​

When you call screen.getByRole('textfield'), the query engine:

  1. Takes the raw native type from the UI dump (Android: android.widget.EditText, iOS: XCUIElementTypeTextField).
  2. Normalizes it — strips the Android package prefix (android.widget., androidx.*, …) and the iOS XCUIElementType prefix — leaving a bare name (edittext, textfield).
  3. Matches that bare name against the role's class list.

getByType() skips normalization and matches the raw native class instead, so use it (or getByLabel() / getByTestId()) for classes that have no role mapping.

Android class → role​

Android reports the base framework class (e.g. android.widget.EditText, even when the app uses AppCompatEditText).

Native classgetByRole()
android.widget.Buttonbutton
android.widget.ImageButtonbutton
android.widget.EditTexttextfield
android.widget.TextViewtext
android.widget.ImageViewimage
android.widget.Switchswitch
android.widget.ToggleButtonswitch
android.widget.CheckBoxcheckbox
android.widget.RadioButtonradio
android.widget.SeekBarslider
android.widget.ProgressBarprogressbar
android.widget.Spinnercombobox
android.widget.ListViewlist
androidx.recyclerview.widget.RecyclerViewlist
android.widget.ScrollViewlist
android.widget.LinearLayoutlistitem
android.widget.RelativeLayoutlistitem
android.widget.Toolbarheader

AndroidX / Material Components​

Some dumps surface the AndroidX or Material Components subclass name directly instead of the collapsed framework class above — these are also recognized:

Native classgetByRole()
androidx.appcompat.widget.AppCompatButtonbutton
com.google.android.material.button.MaterialButtonbutton
com.google.android.material.floatingactionbutton.FloatingActionButtonbutton
androidx.appcompat.widget.AppCompatRadioButtonradio
androidx.appcompat.widget.AppCompatSpinnercombobox
androidx.appcompat.widget.AppCompatEditTexttextfield
com.google.android.material.textfield.TextInputEditTexttextfield
androidx.appcompat.widget.AppCompatTextViewtext
com.google.android.material.textview.MaterialTextViewtext
androidx.appcompat.widget.AppCompatImageViewimage
com.google.android.material.imageview.ShapeableImageViewimage

React Native (Android)​

Native classgetByRole()
…textinput.ReactEditTexttextfield
…text.ReactTextViewtext
…image.ReactImageViewimage
…scroll.ReactScrollViewlist
…view.ReactViewGroupbutton — only when clickable="true" or accessible="true"

Jetpack Compose (Android)​

Compose reports most of its widgets as a plain android.view.View. It only reports android.widget.Button for a button with no children, so a Button { Text("Save") } arrives as a clickable View with its caption on a child TextView.

Compose nodegetByRole()
android.view.Viewbutton — only when clickable
android.view.Viewalert — only when its pane title is Dialog, as on a Material3 AlertDialog

A Compose TextField / OutlinedTextField is a real android.widget.EditText (textfield), but its floating label = { Text("Email") } is a child TextView. A text field without a label of its own is labelled by that child, so both of these find it:

await screen.getByLabel('Email').fill('glorpax@milliways.space');
await expect(screen.getByRole('textfield', { name: 'Email' })).toHaveValue('glorpax@milliways.space');

iOS class → role​

Native classgetByRole()
XCUIElementTypeButtonbutton
XCUIElementTypeTextFieldtextfield
XCUIElementTypeSecureTextFieldtextfield
XCUIElementTypeSearchFieldtextfield
XCUIElementTypeStaticTexttext
XCUIElementTypeTextViewtext — see note below
XCUIElementTypeImageimage
XCUIElementTypeSwitchswitch
XCUIElementTypeSliderslider
XCUIElementTypeProgressIndicatorprogressbar
XCUIElementTypeActivityIndicatorprogressbar
XCUIElementTypeAlertalert
XCUIElementTypeSheetalert
XCUIElementTypePickercombobox
XCUIElementTypePickerWheelcombobox
XCUIElementTypeTablelist
XCUIElementTypeCollectionViewlist
XCUIElementTypeScrollViewlist
XCUIElementTypeCelllistitem
XCUIElementTypeOtherlistitem
XCUIElementTypeTabtab
XCUIElementTypeTabBartab
XCUIElementTypeLinklink
XCUIElementTypeNavigationBarheader

Accessible names​

getByRole('button', { name }) matches the element's own label, then its own text. Roles that take their name from their content — button, link, tab, header, switch, checkbox and radio, as in ARIA — then fall back to the text of their children. That is how a Compose button is named by its caption:

await screen.getByRole('button', { name: 'Save' }).tap();

Other roles are never named by their children, so a list isn't named by its items. The one exception is a textfield, which is named by its floating label (see above).

Notes and known gaps​

  • Classes with no role. Anything not listed above has no role mapping — getByRole() won't find it. Target it with getByType('<raw.native.Class>'), getByLabel(), or getByTestId(). Examples: Android RadioGroup, CheckedTextView; iOS DatePicker.

  • iOS source filtering. mobilecli keeps the iOS dump small, so not every element reaches the query engine. Button, TextField, SecureTextField, SearchField, TextView, Switch, WebView, Slider, Picker and PickerWheel are always included. Every other class in the iOS table is included only when it has a label, name or accessibilityIdentifier — so getByRole('list') finds a labeled Table but not an anonymous one. XCUIElementTypeOther is included only when it carries an accessibilityIdentifier. Requires mobilecli 1.0.13 or later; earlier versions only surface Button, TextField, SecureTextField, SearchField, TextView, Switch, WebView, StaticText, Image and Icon.

  • TextView is platform-ambiguous. On Android, TextView is a static label (text). On iOS, XCUIElementTypeTextView is an editable multiline input (UITextView / SwiftUI TextEditor), which is closer to textfield. Both normalize to the same textview token, so they currently share the text role. Aligning an editable iOS text view with textfield (matching the web/ARIA model, where <textarea> and <input> are both textbox) is tracked separately.