Skip to main content

Testing VoiceOver

Guidepup provides programmatic control of VoiceOver, Apple's built-in screen reader for macOS.

You can use Guidepup to start and stop VoiceOver, navigate web content, interact with controls, and capture what VoiceOver communicates to the user.

Requirements

VoiceOver automation requires:

For the current supported versions and configurations, see the support matrix.

Machine setup

Configure your Mac for VoiceOver automation:

npx @guidepup/setup setup

Install the VoiceOver assets required by your installed version of Guidepup:

npx @guidepup/setup install

See the machine setup guide for more information about the configuration performed by @guidepup/setup.

Starting VoiceOver

The voiceOver instance provides direct control over VoiceOver:

import { voiceOver } from "@guidepup/guidepup";

await voiceOver.start();

try {
// VoiceOver automation.
} finally {
await voiceOver.stop();
}

Always stop VoiceOver when the test completes, including when the test fails.

When using @guidepup/playwright, the integration handles starting and stopping VoiceOver for each test.

VoiceOver navigation is usually expressed through its keyboardCommands collection and the perform() API:

await voiceOver.perform(voiceOver.keyboardCommands.findNextGraphic);

expect(await voiceOver.lastSpokenPhrase()).toContain("Products");

Guidepup provides commands for common VoiceOver navigation patterns, including navigating by:

  • Headings
  • Links
  • Landmarks
  • Form controls
  • Tables
  • Other web content

The example above is the same basic pattern you will see on the testing navigation page: discover the target with a screen reader command, then confirm what the reader reported.

When using @guidepup/playwright an additional command for navigating to the web content from the desktop is provided:

await voiceOver.navigateToWebContent();

Interacting with controls

VoiceOver can be used to interact with the same controls a user would encounter during a workflow:

await voiceOver.nextLink();

expect(await voiceOver.lastSpokenPhrase()).toContain("View all");

await voiceOver.act();

You can combine these commands with Playwright's browser APIs to create complete end-to-end tests.

See testing interactions for examples covering forms, links and other interactive content.

Testing spoken output

Guidepup can capture what VoiceOver speaks during a test:

await voiceOver.act();

expect(await voiceOver.lastSpokenPhrase()).toContain("Added to basket");

You can also inspect the spoken phrase history when you need to verify a sequence of announcements.

The lastSpokenPhrase() API and the spokenPhraseLog() API are the two most common ways to assert against VoiceOver output. See testing announcements for more examples.

VoiceOver specific behaviour

Guidepup exposes a common screen reader API where possible, while also providing access to VoiceOver specific commands and settings.

This means tests can share the same general structure across VoiceOver and NVDA without hiding the differences between the two screen readers.

Where there are VoiceOver specific commands the perform() API provides an escape hatch:

await voiceOver.perform(voiceOver.keyboardCommands.readPreviousSentence);

This API is the same extension point described in the VoiceOver reference docs and is the place to use VoiceOver only navigation commands that do not exist on the shared screenReader abstraction.

See testing NVDA for the equivalent NVDA workflow.