Use executors to test screen reader interactions
Learn how to use executors to automate screen reader gestures, capture spoken output, and add accessibility assertions.
Screen Reader Automation is currently in the Beta phase and is available on Android and iOS devices under the Ultimate plan. To get access, contact BrowserStack Support. For more details on the Ultimate plan, check the pricing page.
With BrowserStack App Accessibility, you can automate screen reader accessibility testing by simulating screen reader interactions, capturing screen reader output, and validating accessibility metadata directly within your test scripts. This guide helps you get started with setting up and using Screen Reader Automation on Android and iOS.
How screen reader automation works
BrowserStack provides the browserstack_executor command, which gives you granular control over the screen reader environment within your tests. It is a custom extension that lets your test scripts send commands to control the screen reader on real BrowserStack devices.
The command can be used only with Appium. However, it is language-agnostic and can be used in any Appium-supported language to run tests on BrowserStack real devices.
This feature provides the following capabilities:
- Programmatic control: Enable and disable the screen reader directly from your test scripts.
- Gesture simulation: Programmatically simulate common accessibility gestures, such as moving between elements, activating an element, or scrolling.
- Real output capture: Capture the screen reader’s actual spoken output as plain text.
- Assertions: Use the captured spoken output to add assertions in your test scripts, so your app meets accessibility standards.
The following video provides an overview of the Screen Reader Automation executor’s capabilities:
Supported platforms and screen readers
You can automate screen reader accessibility testing on the following platforms and their corresponding screen readers:
| Platform | Screen Reader |
|---|---|
| Android | TalkBack |
| iOS | VoiceOver |
Set up screen reader automation
To set up screen reader automation, execute interactions, and add assertions, select your platform and follow the corresponding steps.
Implement screen reader automation
Integrate the browserstack_executor command into your test scripts to enable or disable the screen reader, simulate screen reader gestures, or capture spoken output.
Enable or disable the screen reader
Use the screenReader action to enable or disable TalkBack.
- To enable TalkBack:
driver.execute_script("browserstack_executor: {\"action\":\"screenReader\",\"arguments\": {\"enable\" : \"true\"}}"); - To disable TalkBack:
driver.execute_script("browserstack_executor: {\"action\":\"screenReader\",\"arguments\": {\"enable\" : \"false\"}}");
Simulate screen reader gestures
Use the screenReaderGesture action to simulate common screen reader gestures, such as navigate, activate, or scroll.
- To simulate a navigate next gesture:
driver.execute_script("browserstack_executor: {\"action\":\"screenReaderGesture\",\"arguments\": {\"gesture\" : \"navigate_next\"}}"); - To simulate an activate gesture:
driver.execute_script("browserstack_executor: {\"action\":\"screenReaderGesture\",\"arguments\": {\"gesture\" : \"activate_item\"}}"); - To simulate a scroll down gesture:
driver.execute_script("browserstack_executor: {\"action\":\"screenReaderGesture\",\"arguments\": {\"gesture\" : \"scroll_down\"}}");
List of supported gestures
The following gestures are supported for TalkBack automation:
Gestures supported on both Android and iOS use the same argument names, so you can reuse them across platforms. Gestures supported on iOS only aren’t available on Android.
| Gesture | Argument |
|---|---|
| Move focus to next element | "navigate_next" |
| Move focus to previous element | "navigate_previous" |
| Activate focused element | "activate_item" |
| Scroll down | "scroll_down" |
| Scroll up | "scroll_up" |
| Navigate back | "back" |
| Return to home screen | "home" |
Capture spoken output
Use the screenReaderSpokenDescription action and specify the resource ID of an element to capture the spoken output of that element. If you don’t specify a resource ID, the command returns the spoken output for all elements captured by the screen reader.
- To capture the spoken output of a specific UI element:
driver.execute_script("browserstack_executor: {\"action\":\"screenReaderSpokenDescription\",\"arguments\": {\"resourceId\" : \"<your-element-resource-id>\"}}")
The command returns the spoken output as a string, which you can log, use for assertions, or save to a file for offline review.
If you specify a resource ID but the element does not have accessibility metadata, the command returns the spoken output for all elements captured by the screen reader. This allows you to debug issues related to missing accessibility labels or focus order.
Add accessibility assertions
With the captured screen reader data, you can add assertions to your test scripts. This lets you verify critical accessibility aspects:
- Focusability: Verify that all relevant UI elements receive focus and are reachable by the screen reader.
- Spoken output verification: Check that elements expose the correct accessibility metadata to screen readers, such as labels, roles, and hints, and that the actual spoken output matches your expected accessibility label or announcement.
- Traversal order: Record the screen reader traversal sequence. Compare it to the Accessibility Tree or a visual test to confirm the focus order follows a logical reading flow.
Enable assertions
You can enable assertions in your test scripts by using the browserstack_executor command with the screenReaderSpokenDescription action to capture the spoken output of a specific UI element and verify it against your expected values.
To enable assertions:
driver.execute_script("browserstack_executor: {\"action\":\"screenReaderSpokenDescription\",\"arguments\": {\"resourceId\" : \"<your-element-resource-id>\"}}");
Example assertion
-
Spoken output verification:
The following example captures the spoken output of an element and asserts that it matches the expected description:print("Getting spoken text of element\n") out = driver.execute_script("browserstack_executor: {\"action\":\"screenReaderSpokenDescription\",\"arguments\": {\"resourceId\" : \"<your-element-resource-id>\"}}"); expected_description = "Master Switch, ON" actual_description = out['spoken_description'][0] if actual_description == expected_description puts "Assertion Passed: Spoken description is correct." else raise "Assertion Failed: Expected '#{expected_description}', but got '#{actual_description}'" end
Prerequisites
Add the voiceOver capability to your test session to allocate a VoiceOver-enabled device for your tests. If you do not add the capability, screen reader executors will not work in your test session.
Use the format that matches your existing setup:
Add the voiceOver capability inside bstack:options:
{
"bstack:options": {
"deviceName": "iPhone 16",
"osVersion": "18",
"voiceOver": true
}
}
Add the browserstack.voiceOver capability as a top-level key:
{
"browserstack.deviceName": "iPhone 16",
"browserstack.osVersion": "18",
"browserstack.voiceOver": true
}
Implement screen reader automation
Integrate the browserstack_executor command into your test scripts to enable or disable the screen reader, simulate screen reader gestures, or capture spoken output. You can run only one VoiceOver executor command at a time in a session. Parallel calls return an error.
Enable or disable the screen reader
Use the voiceOverToggle action to enable or disable VoiceOver. The state argument accepts on or off, and isn’t case-sensitive. VoiceOver isn’t enabled at session start, so enable it before you call any other VoiceOver executor.
- To enable VoiceOver:
driver.execute_script("browserstack_executor: {\"action\": \"voiceOverToggle\", \"arguments\": {\"state\": \"on\"}}"); - To disable VoiceOver:
driver.execute_script("browserstack_executor: {\"action\": \"voiceOverToggle\", \"arguments\": {\"state\": \"off\"}}");
The first voiceOverToggle call with state set to on can take longer than later calls.
Simulate screen reader gestures
Use the voiceOverGesture action to simulate common screen reader gestures, such as navigate, activate, or scroll. Gesture names are case-sensitive.
- To simulate a navigate next gesture:
driver.execute_script("browserstack_executor: {\"action\": \"voiceOverGesture\", \"arguments\": {\"gesture\": \"navigate_next\"}}"); - To simulate an activate gesture:
driver.execute_script("browserstack_executor: {\"action\": \"voiceOverGesture\", \"arguments\": {\"gesture\": \"activate_item\"}}"); - To simulate a scroll down gesture:
driver.execute_script("browserstack_executor: {\"action\": \"voiceOverGesture\", \"arguments\": {\"gesture\": \"scroll_down\"}}");
Verification
- If successful, the command returns the gesture that was performed:
{ "success": true, "gesture": "navigate_next" }
List of supported gestures
The following gestures are supported for VoiceOver automation:
Gestures supported on both Android and iOS use the same argument names, so you can reuse them across platforms. Gestures supported on iOS only aren’t available on Android.
| Gesture | Argument | Platform support |
|---|---|---|
| Move focus to next element | "navigate_next" |
Android and iOS |
| Move focus to previous element | "navigate_previous" |
Android and iOS |
| Activate focused element | "activate_item" |
Android and iOS |
| Scroll down | "scroll_down" |
Android and iOS |
| Scroll up | "scroll_up" |
Android and iOS |
| Navigate back or dismiss | "back" |
Android and iOS |
| Move focus to first element on screen | "home" |
Android and iOS |
| Move focus to first element on screen | "navigate_first" |
iOS |
| Move focus to last element on screen | "navigate_last" |
iOS |
| Read continuously from the top of the screen | "read_from_top" |
iOS |
| Read continuously from the current position | "read_from_current" |
iOS |
| Move to the next item for the current rotor setting | "rotor_next" |
iOS |
| Move to the previous item for the current rotor setting | "rotor_previous" |
iOS |
| Pause or resume speech | "pause_speech" |
iOS |
On iOS, the following gestures behave as described:
- The
homeandnavigate_firstgestures perform the same action of moving the screen reader focus to the first element on the screen. - The
read_from_topandread_from_currentgestures start continuous reading. Usepause_speechto pause or resume it.
Capture spoken output
Use the getVoiceOverElement action to capture the element VoiceOver is focused on and its spoken output. This action takes no arguments.
- To capture the focused element and its spoken output:
result = driver.execute_script("browserstack_executor: {\"action\": \"getVoiceOverElement\", \"arguments\": {}}");
Verification
- The command returns the focused element’s accessibility attributes and the most recent VoiceOver announcement as plain text:
{ "element": { "elementClass": "UIButton", "label": "Send", "value": "", "hint": "Double tap to send message", "identifier": "send_button", "traits": 8589934593, "frame": { "x": 300, "y": 700, "width": 60, "height": 44 } }, "spokenDescription": "Send. Button. Double tap to send message." }
The response has the following fields:
| Field | Description |
|---|---|
element.elementClass |
Native class of the focused element, for example, UIButton. |
element.label |
The element’s accessibilityLabel. |
element.value |
The element’s accessibilityValue. |
element.hint |
The element’s accessibilityHint. |
element.identifier |
The element’s accessibilityIdentifier. |
element.traits |
Raw UIAccessibilityTraits bitmask, as a number. |
element.frame |
On-screen position and size, in points. |
spokenDescription |
The most recent VoiceOver announcement, as plain text. |
Considerations
- The two parts of the response,
elementandspokenDescription, are independent. If element details can’t be captured,elementis an empty object. If the spoken output can’t be captured,spokenDescriptionis an empty string. The command doesn’t fail on a partial capture, so assert only on the fields you need. - Unlike Android’s
screenReaderSpokenDescription, this action doesn’t take a resource ID. It always reads the currently focused element.
Add accessibility assertions
With the captured screen reader data, you can add assertions to your test scripts. This lets you verify critical accessibility aspects:
- Focusability: Verify that all relevant UI elements receive focus and are reachable by the screen reader.
- Spoken output verification: Check that elements expose the correct accessibility metadata to screen readers, such as labels, roles, and hints, and that the actual spoken output matches your expected accessibility label or announcement.
- Traversal order: Record the screen reader traversal sequence. Compare it to the Accessibility Tree or a visual test to confirm the focus order follows a logical reading flow.
Enable assertions
You can enable assertions in your test scripts by using the browserstack_executor command with the getVoiceOverElement action to capture the spoken output of the focused element and verify it against your expected values.
To enable assertions:
result = driver.execute_script("browserstack_executor: {\"action\": \"getVoiceOverElement\", \"arguments\": {}}");
Example assertion
-
Spoken output verification:
The following example captures the spoken output of an element and asserts that it matches the expected description:driver.execute_script('browserstack_executor: {"action": "voiceOverGesture", "arguments": {"gesture": "navigate_next"}}') result = driver.execute_script('browserstack_executor: {"action": "getVoiceOverElement", "arguments": {}}') expected = "Send. Button. Double tap to send message." actual = result["spokenDescription"] assert actual == expected, f"Assertion Failed: expected '{expected}', but got '{actual}'" print("Assertion Passed: spoken description is correct.")
Error reference
The VoiceOver executor commands on iOS return the following errors. Each response includes an error code and a descriptive message.
| Error code | When it occurs |
|---|---|
BROWSERSTACK_INVALID_VALUE |
The voiceOverToggle action is called with a state other than on or off. |
BROWSERSTACK_INVALID_VOICEOVER_GESTURE |
The voiceOverGesture action is called with a gesture that isn’t in the supported list. Gesture names are case-sensitive. |
BROWSERSTACK_INCOMPATIBLE_COMMAND_PASSED |
A VoiceOver executor is called on a non-iOS device. VoiceOver executors are supported on iOS only. |
BROWSERSTACK_UNSUPPORTED_PRODUCT |
A VoiceOver executor is called outside an iOS App Automate session. |
BROWSERSTACK_MULTIPLE_VOICEOVER_COMMANDS_ERROR |
A VoiceOver executor is called while another VoiceOver command is still running. Run commands one at a time. |
BROWSERSTACK_FEATURE_NOT_AVAILABLE_IN_CURRENT_PLAN |
The account isn’t on the Ultimate plan. |
BROWSERSTACK_VOICEOVER_NOT_AVAILABLE |
The session didn’t start on a VoiceOver-enabled device. Add the voiceOver capability and run the test again. |
BROWSERSTACK_VOICEOVER_EXECUTOR_DISABLED |
VoiceOver automation is temporarily disabled for the region or device. |
BROWSERSTACK_VOICEOVER_EXECUTOR_ERROR |
The command couldn’t run because of an internal issue. Contact BrowserStack Support if it continues. |
We're sorry to hear that. Please share your feedback so we can do better
Contact our Support team for immediate help while we work on improving our docs.
We're continuously improving our docs. We'd love to know what you liked
We're sorry to hear that. Please share your feedback so we can do better
Contact our Support team for immediate help while we work on improving our docs.
We're continuously improving our docs. We'd love to know what you liked
Thank you for your valuable feedback!