Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/src/agent-cli/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ Used by `find` and `expect`. Combine several to narrow the match.
| Option | Description |
|--------|-------------|
| `--text <text>` | Visible text. Use `/regex/i` for a pattern |
| `--role <role>` | Semantic role: `button`, `textfield`, `text`, `image`, `switch`, `checkbox`, `radio`, `slider`, `list`, `listitem`, `tab`, `link`, `header` |
| `--role <role>` | Semantic role: `button`, `textfield`, `text`, `image`, `switch`, `checkbox`, `radio`, `slider`, `progressbar`, `alert`, `combobox`, `list`, `listitem`, `tab`, `link`, `header` |
| `--name <name>` | With `--role`: accessible name, plain or `/regex/` |
| `--test-id <id>` | Accessibility identifier (iOS) or resource-id (Android) |
| `--label <label>` | Accessibility label |
Expand Down
16 changes: 13 additions & 3 deletions docs/src/guides/locators.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ the app uses `AppCompatEditText`).
| `android.widget.CheckBox` | `checkbox` |
| `android.widget.RadioButton` | `radio` |
| `android.widget.SeekBar` | `slider` |
| `android.widget.ProgressBar` | `progressbar` |
| `android.widget.Spinner` | `combobox` |
| `android.widget.ListView` | `list` |
| `androidx.recyclerview.widget.RecyclerView` | `list` |
| `android.widget.ScrollView` | `list` |
Expand All @@ -59,6 +61,7 @@ of the collapsed framework class above — these are also recognized:
| `com.google.android.material.button.MaterialButton` | `button` |
| `com.google.android.material.floatingactionbutton.FloatingActionButton` | `button` |
| `androidx.appcompat.widget.AppCompatRadioButton` | `radio` |
| `androidx.appcompat.widget.AppCompatSpinner` | `combobox` |
| `androidx.appcompat.widget.AppCompatEditText` | `textfield` |
| `com.google.android.material.textfield.TextInputEditText` | `textfield` |
| `androidx.appcompat.widget.AppCompatTextView` | `text` |
Expand Down Expand Up @@ -89,6 +92,12 @@ of the collapsed framework class above — these are also recognized:
| `XCUIElementTypeImage` | `image` |
| `XCUIElementTypeSwitch` | `switch` |
| `XCUIElementTypeSlider` | `slider` |
| `XCUIElementTypeProgressIndicator` | `progressbar` |
| `XCUIElementTypeActivityIndicator` | `progressbar` |
| `XCUIElementTypeAlert` | `alert` |
| `XCUIElementTypeSheet` | `alert` |
| `XCUIElementTypePicker` | `combobox` |
| `XCUIElementTypePickerWheel` | `combobox` |
| `XCUIElementTypeTable` | `list` |
| `XCUIElementTypeCollectionView` | `list` |
| `XCUIElementTypeScrollView` | `list` |
Expand All @@ -103,13 +112,14 @@ of the collapsed framework class above — these are also recognized:

- **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 `Spinner`, `RadioGroup`,
`CheckedTextView`; iOS `Picker`, `DatePicker`.
`getByLabel()`, or `getByTestId()`. Examples: Android `RadioGroup`,
`CheckedTextView`; iOS `DatePicker`.

- **iOS source filtering.** mobilecli currently surfaces only a subset of iOS
classes — `Button`, `TextField`, `SecureTextField`, `SearchField`, `Switch`,
`StaticText`, `Image`, `Icon`, `WebView`, `TextView`. Other rows in the iOS table
(`Slider`, `Table`, `CollectionView`, `Cell`, `Tab`, `NavigationBar`, `Link`)
(`Slider`, `ProgressIndicator`, `ActivityIndicator`, `Alert`, `Sheet`, `Picker`,
`PickerWheel`, `Table`, `CollectionView`, `Cell`, `Tab`, `NavigationBar`, `Link`)
are filtered out before they reach the query engine, so `getByRole()`
cannot match them yet even though the mapping exists.

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/skills/mobilewright-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ await screen.getByRole('button', { name: 'Sign in' }).tap();

Read the snapshot file to learn the refs. A snapshot line looks like
`- button "Sign in" [ref=e9] [testid="login"]`. Roles are `button`, `textfield`, `text`,
`image`, `switch`, `checkbox`, `radio`, `slider`, `list`, `listitem`, `tab`, `link`, `header`.
`image`, `switch`, `checkbox`, `radio`, `slider`, `progressbar`, `alert`, `combobox`, `list`, `listitem`, `tab`, `link`, `header`.
Attributes: `testid`, `placeholder`, `value`, `hidden`, `disabled`, `checked`, `selected`, `focused`.

Collect the `Ran Mobilewright code` lines to write a test: they are the exact calls
Expand Down
32 changes: 32 additions & 0 deletions packages/mobilewright-core/src/query-engine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,38 @@ test.describe('material and appcompat widget aliases', () => {
});
});

test.describe('progressbar, alert and combobox roles', () => {
test('progressbar matches iOS indicators and Android ProgressBar', () => {
const tree = [
node({ type: 'ProgressIndicator' }),
node({ type: 'ActivityIndicator' }),
node({ type: 'android.widget.ProgressBar' }),
node({ type: 'Button' }),
];
expect(queryAll(tree, { kind: 'role', value: 'progressbar' })).toHaveLength(3);
});

test('alert matches iOS Alert and Sheet', () => {
const tree = [
node({ type: 'Alert', label: 'Delete photo?' }),
node({ type: 'Sheet' }),
node({ type: 'StaticText', text: 'Delete photo?' }),
];
expect(queryAll(tree, { kind: 'role', value: 'alert' })).toHaveLength(2);
});

test('combobox matches iOS pickers and Android spinners', () => {
const tree = [
node({ type: 'Picker' }),
node({ type: 'PickerWheel' }),
node({ type: 'android.widget.Spinner' }),
node({ type: 'androidx.appcompat.widget.AppCompatSpinner' }),
node({ type: 'android.widget.EditText' }),
];
expect(queryAll(tree, { kind: 'role', value: 'combobox' })).toHaveLength(4);
});
});

test.describe('placeholder strategy', () => {
const tree: ViewNode[] = [
node({ type: 'TextField', placeholder: 'Enter email' }),
Expand Down
3 changes: 3 additions & 0 deletions packages/mobilewright-core/src/query-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ export const ROLE_TYPE_MAP = {
checkbox: ['checkbox'],
radio: ['radio', 'radiobutton', 'appcompatradiobutton'],
slider: ['slider', 'seekbar'],
progressbar: ['progressindicator', 'activityindicator', 'progressbar'],
alert: ['alert', 'sheet'],
combobox: ['picker', 'pickerwheel', 'spinner', 'appcompatspinner'],
Comment thread
coderabbitai[bot] marked this conversation as resolved.
list: ['table', 'collectionview', 'listview', 'recyclerview', 'scrollview', 'reactscrollview'],
listitem: ['cell', 'linearlayout', 'relativelayout', 'other'],
tab: ['tab', 'tabbar'],
Expand Down
Loading