Beginner Chapter 4 · 9 min read

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:

locators.test.ts
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');
});
locators.test.js
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.

Tip: On iOS, 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:

RoleAndroid classiOS class
buttonandroid.widget.ButtonXCUIElementTypeButton
textfieldandroid.widget.EditTextXCUIElementTypeTextField
textandroid.widget.TextViewXCUIElementTypeStaticText
imageandroid.widget.ImageViewXCUIElementTypeImage
switchandroid.widget.SwitchXCUIElementTypeSwitch
listRecyclerView / ListViewXCUIElementTypeTable

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:

chaining.test.ts
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();
});
chaining.test.js
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:

collections.test.ts
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();
  }
});
collections.test.js
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();
  }
});
Tip: Not sure which locator to use for an element? Run npx mobilewright inspect — the Inspector shows the recommended locator for every element on screen. We cover it in the final chapter.

Mobilewright Beginner Locators Elements

Written by PV

© 2026 All Rights Reserved