Locators & Finding Elements
Find any element on iOS and Android with getByText, getByLabel, getByTestId, getByRole, and getByType — plus chaining and collections.
The Five Locators
A locator describes how to find an element on the screen. Mobilewright gives you five, and they work identically on iOS and Android:
import { test, expect } from '@mobilewright/test';
test('the five locators', async ({ screen }) => {
// 1. By visible text
screen.getByText('Sign In');
// 2. By accessibility label
screen.getByLabel('Username');
// 3. By test ID (most stable — survives text changes)
screen.getByTestId('submit-button');
// 4. By semantic role — works cross-platform
screen.getByRole('button', { name: 'Submit' });
// 5. By raw native element type
screen.getByType('TextField');
});
const { test, expect } = require('@mobilewright/test');
test('the five locators', async ({ screen }) => {
// 1. By visible text
screen.getByText('Sign In');
// 2. By accessibility label
screen.getByLabel('Username');
// 3. By test ID (most stable — survives text changes)
screen.getByTestId('submit-button');
// 4. By semantic role — works cross-platform
screen.getByRole('button', { name: 'Submit' });
// 5. By raw native element type
screen.getByType('TextField');
});
When should you use which? Follow this priority order — it is the same order the Mobilewright Inspector recommends: getByTestId > getByRole > getByLabel > getByText. Test IDs are the most stable because they don't change when the UI text changes.
getByTestId matches the accessibilityIdentifier; on Android it matches the resourceId. In React Native, just set the testID prop and it works on both.How Roles Work Across Platforms
The magic of getByRole() is that one test targets the same element on both platforms, even though iOS and Android name their native classes differently. Mobilewright normalizes the native type and maps it to a semantic role:
| Role | Android class | iOS class |
|---|---|---|
button | android.widget.Button | XCUIElementTypeButton |
textfield | android.widget.EditText | XCUIElementTypeTextField |
text | android.widget.TextView | XCUIElementTypeStaticText |
image | android.widget.ImageView | XCUIElementTypeImage |
switch | android.widget.Switch | XCUIElementTypeSwitch |
list | RecyclerView / ListView | XCUIElementTypeTable |
If an element's class has no role mapping (for example Android Spinner or iOS DatePicker), fall back to getByType() with the raw class name, or use getByLabel() / getByTestId().
Chaining Locators
Locators can be chained to search inside a parent element. This is perfect for lists, where many rows look the same:
import { test, expect } from '@mobilewright/test';
test('delete the first item in the list', async ({ screen }) => {
// Narrow the search: find the Delete button INSIDE the first cell
const row = screen.getByType('Cell').first();
await row.getByRole('button', { name: 'Delete' }).tap();
});
const { test, expect } = require('@mobilewright/test');
test('delete the first item in the list', async ({ screen }) => {
// Narrow the search: find the Delete button INSIDE the first cell
const row = screen.getByType('Cell').first();
await row.getByRole('button', { name: 'Delete' }).tap();
});
Working with Collections
When a locator matches several elements, pick one by position or work with all of them:
import { test, expect } from '@mobilewright/test';
test('working with multiple matches', async ({ screen }) => {
// Pick by position
screen.getByRole('button').first();
screen.getByRole('button').last();
screen.getByRole('button').nth(2);
// Count matching elements
const count = await screen.getByRole('listitem').count();
expect(count).toBeGreaterThan(0);
// Iterate over all matches
const items = await screen.getByRole('listitem').all();
for (const item of items) {
await expect(item).toBeVisible();
}
});
const { test, expect } = require('@mobilewright/test');
test('working with multiple matches', async ({ screen }) => {
// Pick by position
screen.getByRole('button').first();
screen.getByRole('button').last();
screen.getByRole('button').nth(2);
// Count matching elements
const count = await screen.getByRole('listitem').count();
expect(count).toBeGreaterThan(0);
// Iterate over all matches
const items = await screen.getByRole('listitem').all();
for (const item of items) {
await expect(item).toBeVisible();
}
});
npx mobilewright inspect — the Inspector shows the recommended locator for every element on screen. We cover it in the final chapter.Written by PV
© 2026 All Rights Reserved