Skip to main content
🎉 A11y Issue Detection Agent is now live! Detect accessibility issues like a WCAG expert with AI. Try now!
No Result Found
Get your setup working faster. Join our Discord for optimisation tips from elite testers. Join our DiscordJoin our Discord

Use VoiceOver executors with Maestro

Learn how to use executors to automate VoiceOver gestures, capture spoken output, and add accessibility assertions in Maestro flows on real iOS devices.

Screen Reader Automation is available only under the Ultimate plan. For more details, check the pricing page.

BrowserStack App Accessibility provides three VoiceOver executors that you can call using a Maestro flow on iOS devices:

  • voiceOverToggle turns VoiceOver on or off.
  • voiceOverGesture performs one VoiceOver gesture, such as moving focus to the next element or activating the focused element.
  • getVoiceOverElement returns the most recent phrase from VoiceOver and the element that has the VoiceOver focus.

After your Maestro flow captures the returned values, you can check them with assertTrue assertions.

Appium tests call the same executors with the browserstack_executor command. If your tests use Appium, see Use executors to test screen reader interactions.

Prerequisites

Before you start, make sure you have the following:

Add the helper scripts to your test suite

BrowserStack provides a helper script for each executor. Save the three scripts in the same folder as the Maestro flow that calls them. The following example shows the folder structure:

flows/
├── voiceover-flow.yaml
├── bs_executor.js
├── bs_gesture.js
└── bs_get_element.js

Don’t edit the scripts. BrowserStack sets the session values that the scripts need, such as the session ID and device ID. Your flow passes the executor’s arguments to each script, as described in Call the executors from your flow.

VoiceOver toggle script

Save the following script as bs_executor.js to run the voiceOverToggle executor:

bs_executor.js
Copy icon Copy

VoiceOver gesture script

Save the following script as bs_gesture.js to run the voiceOverGesture executor:

bs_gesture.js
Copy icon Copy

VoiceOver element script

Save the following script as bs_get_element.js to run the getVoiceOverElement executor and store the result in output.spokenDescription and output.element:

bs_get_element.js
Copy icon Copy

Call the executors from your flow

Add a runScript step to your Maestro flow for each executor call. Each step uses the following parameters:

  • file: the helper script file, for example, bs_executor.js.
  • env: the executor’s arguments, BS_STATE for bs_executor.js or BS_GESTURE for bs_gesture.js.
  • label: the step name that appears in your Maestro logs.

Turn VoiceOver on or off

Call bs_executor.js with BS_STATE set to "on" or "off":

voiceover-flow.yaml
Copy icon Copy

Turn VoiceOver on before you call the other executors. The executors work only in builds that set voiceOver to true. For details, see Run your build with VoiceOver.

Simulate VoiceOver gestures

Call bs_gesture.js with BS_GESTURE set to the gesture you want to perform:

voiceover-flow.yaml
Copy icon Copy

Gesture values

Gesture names are case-sensitive. See the following table for a list of valid values:

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 home and navigate_first gestures perform the same action of moving the screen reader focus to the first element on the screen.
  • The read_from_top and read_from_current gestures start continuous reading. Use pause_speech to pause or resume continuous reading.

Capture the focused element and spoken output

Call bs_get_element.js to capture the element that has the VoiceOver focus and the most recent phrase from VoiceOver:

voiceover-flow.yaml
Copy icon Copy

The script takes no env values and always reads the element that has the VoiceOver focus.

Captured values

After the step runs, your flow can use the following captured values:

  • output.element: the element object as a JSON string.
  • output.spokenDescription: the spokenDescription text.

The element object and spokenDescription text come from the executor’s result, which has the following fields:

Field Description
element.elementClass 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.

In Maestro flows, element.traits is an array of trait names, such as ["Button"] or ["Link", "StaticText"], instead of a number.

output.spokenDescription and output.element are captured separately. If the spoken output can’t be captured, output.spokenDescription holds an empty string. If the element details can’t be captured, output.element holds {}. In either case, the step still succeeds, so add assertions only for the values that your test needs.

VoiceOver reads an element’s label, value, traits, and hint as separate phrases, so output.spokenDescription might hold only one of those phrases.

Add assertions

Add assertTrue steps to your Maestro flow to check output.spokenDescription and output.element, which hold the values from the most recent getVoiceOverElement call. The following steps check the spoken text and the element’s traits:

voiceover-flow.yaml
Copy icon Copy

output.element is a JSON string, so use indexOf to check for a trait name.

Maestro flow example

The following flow turns VoiceOver on, moves focus through two elements, checks each one, and turns VoiceOver off:

voiceover-flow.yaml
Copy icon Copy

Run your build with VoiceOver

To run your build with VoiceOver enabled, follow these steps:

  1. Zip your test suite folder with the helper scripts inside. For detailed steps, see Structure your Maestro accessibility tests.
  2. Upload your app and test suite. For detailed steps, see Integrate Maestro test suite with App Accessibility testing.
  3. Start the build with the iOS endpoint and voiceOver set to true:
Terminal
Copy icon Copy

With voiceOver set to true, your Maestro flow runs on a VoiceOver-enabled iOS device and can call the VoiceOver executors. If you don’t set voiceOver, the first executor step fails and the flow stops.

Check VoiceOver output and errors in your logs

Maestro builds run on BrowserStack App Automate, so the executor output appears in the Maestro Logs tab of your session on the App Automate dashboard. Use these logs to check the VoiceOver output for each element and to troubleshoot failed executor steps. For more information on Maestro logs, see View Maestro logs for your tests.

VoiceOver output

Each bs_get_element.js call adds the following to the Maestro Logs tab:

  • The focused element as JSON
  • The spoken description of that element

For example:

element   {"label":"Send","value":"","hint":"Double tap to send your message","traits":["Button"],"elementClass":"AccessibilityNode","identifier":"","frame":{"x":24,"y":204,"width":345,"height":52}}
spokenDescription   Send

The spoken output doesn’t appear in the Text Logs tab.

Executor errors

If an executor call returns an HTTP error, the step fails with an error message that starts with [EXECUTOR] and includes the HTTP status and response.

For example:

[EXECUTOR] voiceOverToggle on failed: HTTP <status> <response>

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

Is this page helping you?

Yes
No

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!

Talk to an Expert
Download Copy Check Circle