A11y Unlocked: Screen Reader Automation Tests
Tags: javascript, a11y, webdev, node
Published: 2022-10-16 17:31:19

In the past year since my last post [A11y Testing: Automating ScreenReaders](https://dev.to/craigmorten/a11y-testing-automating-screenreaders-1a3n) on the topic of automating screen readers, we're now in a better place with progress made in standards and tooling.

It is exciting to see the [W3C ARIA-AT Community Group](https://aria-at.w3.org/) formed with a mission to improve screen reader interoperability by building out a suite of specifications, test standards, and test automation to mature the screen reader ecosystem towards first-class tooling.

> _"Enabling AT interoperability is a large, ongoing endeavor that requires industry-wide collaboration and support. The W3C ARIA-AT Community Group is focusing on a stable and mature test suite for five screen readers by the end of 2023. In the future, we plan to test additional screen readers and other kinds of assistive technologies with a broader set of web design patterns and test material."_
>
> _Source: <https://aria-at.w3.org/>_

While the standards community slowly work towards their long term goal, I am pleased to share some screen reader automation tooling which you can use **today**! 🚀

## Guidepup for playwright

In this tutorial we're going to make use of the package [`@guidepup/playwright`](https://www.npmjs.com/package/@guidepup/playwright) to write e2e tests for testing the accessibility of pages for people who use the VoiceOver screen reader for MacOS.

{% github guidepup/guidepup-playwright %}

The package provides a reliable set of APIs to automate your screen reader a11y workflows when using Playwright. It does this by providing both Playwright configuration as well as a custom Playwright `test` instance that exposes a `voiceOver` object for controlling VoiceOver with.

## Getting setup

Let's create a new project:

```bash
npm init
```

Install the `@guidepup/playwright` package as a dependency:

```bash
npm install @guidepup/playwright
```

We'll also need Playwright as the test runner for our browser:

```bash
npm install @playwright/test
```

Last but not least we'll want to test against Safari using Playwright, so let's use their tool get grab the browser:

```bash
npx playwright install webkit
```

We now have all the dependencies we need! :rocket:

## A touch of config

Create a new `playwright.config.js` file:

```js
import { voConfig } from "@guidepup/playwright";

const config: PlaywrightTestConfig = {
  ...voConfig,

  // Your custom config ...
};

export default config;
```

Here we're pulling in the minimum recommended Playwright config for using Guidepup - you can [check it out in the source code](https://github.com/guidepup/guidepup-playwright/blob/main/src/voConfig.ts). This sets the number of workers to `1` and sets Playwright to use "headed" mode because when we're using VoiceOver we can only control one browser at a time (and ideally it's visible!).

We should now be completely set to get going with our first test! :tada:

## Writing your first screen-reader test

Create a new file `voiceover.spec.js` and copy in the following contents:

```js
// We use a special test instance from the Guidepup package
// that gives us access to VoiceOver!
import { voTest as test } from "@guidepup/playwright";
import { expect } from "@playwright/test";

// The test setup is exactly the same as normal for
// Playwright, expect we now get a `voiceOver` object as well
// as the `page` object!
test.describe("Playwright VoiceOver", () => {
  test("I can navigate the Guidepup Github page", async ({
    page,
    voiceOver,
  }) => {
    // Let's navigate to Guidepup GitHub page and wait for
    // page to be ready, nothing special here!
    await page.goto("https://github.com/guidepup/guidepup", {
      waitUntil: "domcontentloaded",
    });
    await expect(
      page.locator('header[role="banner"]')
    ).toBeVisible();

    // This is where things start to get awesome! Let's tell
    // VoiceOver to interact with the main page content, just 
    // the same as you would when use VoiceOver normally.
    await voiceOver.interact();

    // Let's do something a lil more exciting - move across
    // the page's headings until we reach the main Guidepup
    // repo title in the README using VoiceOver!
    while ((await voiceOver.itemText()) !== "Guidepup heading level 1") {
      await voiceOver.perform(
        voiceOver.keyboard.commands.findNextHeading
      );
    }
});
```

Check out the comments in the code for what will happen in each part - pretty awesome huh? 🙌

## Last checks before we launch

In order to automate screen-readers a few OS level settings need to be sorted first!

You can head over to the [Guidepup environment setup docs](https://www.guidepup.dev/docs/guides/environment) for more details, but I recommend trying out the [`@guidepup/setup`](https://github.com/guidepup/setup) package which is designed to get you setup no faff!

```bash
npx @guidepup/setup
```

You're now ready to rock! 🤘

## Running your first screen-reader test

No special commands needed here, just the same as with any other Playwright test! Fire off the command then hold tight - we don't want to interact with the machine while the test is running as it might move VoiceOver's focus!

```bash
npx playwright test
```

![Gif over Playwright controlled Safari browser being driven with VoiceOver. Announcements read: "heading level 2, Latest commit", "heading level 2, Git stats", "You are currently on a heading level 2.", "heading level 2, Files", "You are currently on a heading level 2.", "heading level 2, link, README.md", "heading level 1, Guidepup"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4vnjufu0zxfvz8vdln5.gif)

## What's next?

To find out more there's a whole load of reading, modules, and docs - check some of these out:

- <https://www.guidepup.dev/>
- <https://github.com/guidepup>

---

Time to try out some e2e screen-reader tests?

Let me know your thoughts, questions, and opinions in the comments below!

Till next time folks :wave: