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
39 changes: 39 additions & 0 deletions .github/workflows/test-screenreader.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Test Screen Reader

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
test-screenreader:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-14, macos-15, macos-26, windows-2022, windows-2025]
browser: [chromium]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- name: Guidepup Setup
uses: guidepup/setup-action@0.20.0
with:
record: true
- run: yarn install --frozen-lockfile
- run: yarn pretest
- run: yarn test:screenreader:${{ matrix.browser }}
- uses: actions/upload-artifact@v4
if: always()
continue-on-error: true
with:
name: artifacts-${{ matrix.os }}-${{ matrix.browser }}
path: |
**/test-results/**/*
**/recordings/**/*
10 changes: 6 additions & 4 deletions examples/logIncludesExpectedPhrases.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
export const logIncludesExpectedPhrases = (
log: string[],
expectedPhrases: string[]
expectedPhrases: string[],
) => {
const failures: string[] = [];

for (const expectedPhrase of expectedPhrases) {
const foundExpectedPhrase = !!log.find((logItem) =>
logItem.includes(expectedPhrase)
const foundExpectedPhrase = !!log.find(
(logItem) =>
logItem.includes(expectedPhrase) ||
logItem.replaceAll(/\s/g, "").includes(expectedPhrase),
);

if (!foundExpectedPhrase) {
Expand All @@ -16,7 +18,7 @@ export const logIncludesExpectedPhrases = (

if (failures.length) {
throw new Error(
`Did not find the following expected text:\n- ${failures.join("\n- ")}`
`Did not find the following expected text:\n- ${failures.join("\n- ")}`,
);
}
};
2 changes: 1 addition & 1 deletion examples/playwright-nvda/tests/chromium/chromium.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { nvdaTest as test } from "../../../../src";
test.use({ nvdaStartOptions: { capture: "initial" } });

test.describe("Chromium Playwright NVDA", () => {
test("I can navigate the Guidepup Github page", async ({
test("I can navigate the Guidepup documentation site", async ({
browser,
browserName,
page,
Expand Down
2 changes: 1 addition & 1 deletion examples/playwright-nvda/tests/firefox/firefox.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { nvdaTest as test } from "../../../../src";
test.use({ nvdaStartOptions: { capture: "initial" } });

test.describe("Firefox Playwright VoiceOver", () => {
test("I can navigate the Guidepup Github page", async ({
test("I can navigate the Guidepup documentation site", async ({
browser,
browserName,
page,
Expand Down
18 changes: 18 additions & 0 deletions examples/playwright-screenreader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Screen Reader Example

An example demonstrating using Guidepup for testing the default screen reader automation with [Playwright](https://playwright.dev/).

Run this example's test with the following from the root directory:

```console
npm run test
```

> Note: please ensure you have [setup you environment](https://www.guidepup.dev/docs/guides/automated-environment-setup) for NVDA or VoiceOver automation before running this example.

## Test flow

1. The test launches the browser using Playwright
2. Navigates to the GitHub website
3. Moves through the website using the default screen reader for the environment, controlled by Guidepup
4. Traverses headings until the Guidepup heading in the README.md is found
18 changes: 18 additions & 0 deletions examples/playwright-screenreader/chromium.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { devices, PlaywrightTestConfig } from "@playwright/test";
import { screenReaderConfig } from "../../src";

const config: PlaywrightTestConfig = {
...screenReaderConfig,
reportSlowTests: null,
timeout: 5 * 60 * 1000,
retries: 1,
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"], headless: false },
},
],
reporter: process.env.CI ? [["github"], ["html", { open: "never" }]] : "list",
};

export default config;
40 changes: 40 additions & 0 deletions examples/playwright-screenreader/tests/chromium/chromium.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { platform, release } from "os";
import { headerNavigation } from "../headerNavigation";
import { logIncludesExpectedPhrases } from "../../../logIncludesExpectedPhrases";
import spokenPhraseSnapshot from "./chromium.spokenPhrase.snapshot.json";
import { screenReaderTest as test } from "../../../../src";

test.describe("Chromium Playwright Screen Reader", () => {
test("I can navigate the Guidepup documentation site", async ({
browser,
browserName,
page,
screenReader,
}) => {
const osName = platform();
const osVersion = release();
const browserVersion = browser.version();
const { retry } = test.info();

console.table({
osName,
osVersion,
browserName,
browserVersion,
retry,
});

await headerNavigation({ page, screenReader });

// Assert that we've ended up where we expected and what we were told on
// the way there is as expected.

const itemTextLog = await screenReader.itemTextLog();
const spokenPhraseLog = await screenReader.spokenPhraseLog();

console.log(JSON.stringify(itemTextLog, undefined, 2));
console.log(JSON.stringify(spokenPhraseLog, undefined, 2));

logIncludesExpectedPhrases(spokenPhraseLog, spokenPhraseSnapshot);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["Guidepup", "Docs", "API", "GitHub"]
50 changes: 50 additions & 0 deletions examples/playwright-screenreader/tests/headerNavigation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { log } from "../../log";
import { Page } from "@playwright/test";
import type { ScreenReaderPlaywright } from "../../../src";

const MAX_NAVIGATION_LOOP = 10;

export async function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

export async function headerNavigation({
page,
screenReader,
}: {
page: Page;
screenReader: ScreenReaderPlaywright;
}) {
// Navigate to Guidepup Website 🎉
log("Navigating to URL: https://www.guidepup.dev.");
await page.goto("https://www.guidepup.dev", {
waitUntil: "load",
});

// Wait for page to be ready and interact 🙌
const header = page.locator("h1");
await header.waitFor();
await delay(500);

// Make sure interacting with the web content
await screenReader.navigateToWebContent();
await delay(500);

let navigationCount = 0;

// Move across the content
while (
!(await screenReader.itemText()).replaceAll(/\s/g, "").includes("GitHub") &&
navigationCount <= MAX_NAVIGATION_LOOP
) {
navigationCount++;

log(`Performing command: next`);
await screenReader.next();
log(`Screen reader output: "${await screenReader.lastSpokenPhrase()}".`);
}

log(`Performing command: act`);
await screenReader.act();
log(`Screen reader output: "${await screenReader.lastSpokenPhrase()}".`);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { voiceOverTest as test } from "../../../../src";
test.use({ voiceOverStartOptions: { capture: "initial" } });

test.describe("Chromium Playwright VoiceOver", () => {
test("I can navigate the Guidepup Github page", async ({
test("I can navigate the Guidepup documentation site", async ({
browser,
browserName,
page,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { voiceOverTest as test } from "../../../../src";
test.use({ voiceOverStartOptions: { capture: "initial" } });

test.describe("Firefox Playwright VoiceOver", () => {
test("I can navigate the Guidepup Github page", async ({
test("I can navigate the Guidepup documentation site", async ({
browser,
browserName,
page,
Expand Down
2 changes: 1 addition & 1 deletion examples/playwright-voiceover/tests/webkit/webkit.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { voiceOverTest as test } from "../../../../src";
test.use({ voiceOverStartOptions: { capture: "initial" } });

test.describe("Webkit Playwright VoiceOver", () => {
test("I can navigate the Guidepup Github page", async ({
test("I can navigate the Guidepup documentation site", async ({
browser,
browserName,
page,
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@guidepup/playwright",
"version": "0.16.3",
"version": "0.17.0",
"description": "Screen reader automation library for Playwright testing. ",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -34,15 +34,17 @@
"prepublish": "yarn build",
"pretest": "npx playwright install chromium firefox webkit",
"test:nvda": "yarn test:nvda:chromium && yarn test:nvda:firefox",
"test:screenreader": "yarn test:screenreader:chromium",
"test:voiceover": "yarn test:voiceover:chromium && yarn test:voiceover:firefox && yarn test:voiceover:webkit",
"test:nvda:chromium": "playwright test --config ./examples/playwright-nvda/chromium.config.ts ./examples/playwright-nvda/tests/chromium/",
"test:nvda:firefox": "playwright test --config ./examples/playwright-nvda/firefox.config.ts ./examples/playwright-nvda/tests/firefox/",
"test:screenreader:chromium": "playwright test --config ./examples/playwright-screenreader/chromium.config.ts ./examples/playwright-screenreader/tests/chromium/",
"test:voiceover:chromium": "playwright test --config ./examples/playwright-voiceover/chromium.config.ts ./examples/playwright-voiceover/tests/chromium/",
"test:voiceover:firefox": "playwright test --config ./examples/playwright-voiceover/firefox.config.ts ./examples/playwright-voiceover/tests/firefox/",
"test:voiceover:webkit": "playwright test --config ./examples/playwright-voiceover/webkit.config.ts ./examples/playwright-voiceover/tests/webkit/"
},
"devDependencies": {
"@guidepup/guidepup": "^0.25.0",
"@guidepup/guidepup": "^0.27.0",
"@guidepup/record": "^0.1.0",
"@playwright/test": "^1.60.0",
"@types/node": "^24.10.1",
Expand All @@ -56,7 +58,7 @@
"typescript": "^5.9.3"
},
"peerDependencies": {
"@guidepup/guidepup": ">=0.25.0",
"@guidepup/guidepup": ">=0.27.0",
"@playwright/test": "^1.57.0"
},
"resolutions": {
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export { nvdaTest } from "./nvdaTest";
export type { NVDAPlaywright } from "./nvdaTest";
export { screenReaderConfig } from "./screenReaderConfig";
export { screenReaderTest } from "./screenReaderTest";
export type { ScreenReaderPlaywright } from "./screenReaderTest";
export { voiceOverTest } from "./voiceOverTest";
export type { VoiceOverPlaywright } from "./voiceOverTest";
3 changes: 0 additions & 3 deletions src/nvdaTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,6 @@ export const nvdaTest = test.extend<{
throw new Error(`Browser ${browserName} is not installed.`);
}

await page.goto("about:blank", { waitUntil: "load" });
await page.bringToFront();

nvdaPlaywright.navigateToWebContent = async (
clearLogs: boolean = true,
) => {
Expand Down
Loading
Loading