@guidepup/playwright 0.12.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +194 -70
- package/lib/applicationNameMap.d.ts +10 -0
- package/lib/applicationNameMap.js +13 -0
- package/lib/index.d.ts +5 -2
- package/lib/index.js +7 -5
- package/lib/nvdaTest.d.ts +65 -0
- package/lib/nvdaTest.js +85 -0
- package/lib/screenReaderConfig.d.ts +6 -0
- package/lib/screenReaderConfig.js +24 -0
- package/lib/voiceOverTest.d.ts +69 -0
- package/lib/voiceOverTest.js +48 -0
- package/package.json +16 -13
- package/lib/voConfig.d.ts +0 -6
- package/lib/voConfig.js +0 -20
- package/lib/voTest.d.ts +0 -11
- package/lib/voTest.js +0 -39
package/README.md
CHANGED
|
@@ -1,46 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
</
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
A11y static analysis tools [only cover 25% of WCAG](https://karlgroves.com/web-accessibility-testing-what-can-be-tested-and-how/) and don't assure on the quality of the user experience for screen reader users. This means teams need to perform lots of manual tests with multiple screen readers to ensure great UX which can take a lot of time... **not anymore!**
|
|
24
|
-
|
|
25
|
-
With [Guidepup](https://www.guidepup.dev/) you can automate your screen reader test workflows the same you as would for mouse or keyboard based scenarios, no sweat!
|
|
26
|
-
|
|
27
|
-
## Quick Features
|
|
28
|
-
|
|
29
|
-
- **Full Control** - if a screen reader has a keyboard command, then Guidepup supports it.
|
|
30
|
-
- **Mirrors Real User Experience** - assert on what users really do and hear when using screen readers.
|
|
31
|
-
- **Framework Agnostic** - run with Jest, with Playwright, as an independent script, no vendor lock-in.
|
|
1
|
+
# Guidepup Playwright
|
|
2
|
+
|
|
3
|
+
<a href="https://www.npmjs.com/package/@guidepup/playwright"><img alt="@guidepup/playwright available on NPM" src="https://img.shields.io/npm/v/@guidepup/playwright" /></a>
|
|
4
|
+
<a href="https://github.com/guidepup/guidepup-playwright/actions/workflows/test.yml"><img alt="@guidepup/playwright test workflows" src="https://github.com/guidepup/guidepup-playwright/workflows/Test/badge.svg" /></a>
|
|
5
|
+
<a href="https://github.com/guidepup/guidepup-playwright/blob/main/LICENSE"><img alt="@guidepup/playwright uses the MIT license" src="https://img.shields.io/github/license/guidepup/guidepup-playwright" /></a>
|
|
6
|
+
|
|
7
|
+
## [Documentation](https://guidepup.dev) | [API Reference](https://www.guidepup.dev/docs/api/class-guidepup)
|
|
8
|
+
|
|
9
|
+
[](https://apps.apple.com/id/app/macos-big-sur/id1526878132)
|
|
10
|
+
[](https://apps.apple.com/us/app/macos-monterey/id1576738294)
|
|
11
|
+
[](https://apps.apple.com/us/app/macos-ventura/id1638787999)
|
|
12
|
+
[](https://www.microsoft.com/en-gb/software-download/windows10ISO)
|
|
13
|
+
[](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2019)
|
|
14
|
+
[](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2022)
|
|
15
|
+
|
|
16
|
+
This package provides [Guidepup](https://github.com/guidepup/guidepup) integration with [Playwright](https://playwright.dev/) for writing screen reader tests that automate <a href="https://www.guidepup.dev/docs/api/class-voiceover"><b>VoiceOver on MacOS</b></a> and <a href="https://www.guidepup.dev/docs/api/class-nvda"><b>NVDA on Windows</b></a>.
|
|
17
|
+
|
|
18
|
+
## Capabilities
|
|
19
|
+
|
|
20
|
+
- **Full Control** - If a screen reader has a keyboard command, then Guidepup supports it.
|
|
21
|
+
- **Mirrors Real User Experience** - Assert on what users really do and hear when using screen readers.
|
|
22
|
+
- **Framework Agnostic** - Run with Jest, with Playwright, as an independent script, no vendor lock-in.
|
|
32
23
|
|
|
33
24
|
## Getting Started
|
|
34
25
|
|
|
35
26
|
Set up your environment for screen reader automation with [`@guidepup/setup`](https://github.com/guidepup/setup):
|
|
36
27
|
|
|
37
|
-
```
|
|
28
|
+
```sh
|
|
38
29
|
npx @guidepup/setup
|
|
39
30
|
```
|
|
40
31
|
|
|
32
|
+
If you are using GitHub Actions, check out the dedicated [`guidepup/setup-action`](https://github.com/marketplace/actions/guidepup-setup):
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
- name: Setup Environment
|
|
36
|
+
uses: guidepup/setup-action
|
|
37
|
+
```
|
|
38
|
+
|
|
41
39
|
Install `@guidepup/playwright` to your project:
|
|
42
40
|
|
|
43
|
-
```
|
|
41
|
+
```sh
|
|
44
42
|
npm install --save-dev @guidepup/playwright @guidepup/guidepup @playwright/test
|
|
45
43
|
```
|
|
46
44
|
|
|
@@ -48,80 +46,206 @@ Note: you require `@guidepup/guidepup` and `@playwright/test` as they are peer d
|
|
|
48
46
|
|
|
49
47
|
And get cracking with your first screen reader tests in Playwright!
|
|
50
48
|
|
|
49
|
+
## Examples
|
|
50
|
+
|
|
51
|
+
Head over to the [Guidepup Website](https://www.guidepup.dev/) for guides, real world examples, environment setup, and complete API documentation with examples.
|
|
52
|
+
|
|
53
|
+
You can also check out these [awesome examples](https://github.com/guidepup/guidepup/tree/main/examples) to learn how you could use Guidepup with Playwright in your projects.
|
|
54
|
+
|
|
55
|
+
Alternatively check out [this project](https://github.com/guidepup/aria-at-tests) which runs several thousand tests to assert screen reader compatibility against [W3C ARIA-AT](https://github.com/w3c/aria-at) test suite.
|
|
56
|
+
|
|
57
|
+
### Playwright Config
|
|
58
|
+
|
|
59
|
+
In your `playwright.config.ts` add the following for the best results with Guidepup for Screen Reader automation:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { devices, PlaywrightTestConfig } from "@playwright/test";
|
|
63
|
+
import { screenReaderConfig } from "@guidepup/playwright";
|
|
64
|
+
|
|
65
|
+
const config: PlaywrightTestConfig = {
|
|
66
|
+
...screenReaderConfig,
|
|
67
|
+
|
|
68
|
+
// ... your custom config
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export default config;
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Check out the configuration this adds [in the `config.ts` file](./src/config.ts).
|
|
75
|
+
|
|
76
|
+
### Web Content Navigation
|
|
77
|
+
|
|
78
|
+
In addition to the Guidepup APIs the `voiceOver` and `nvda` instances provided by the Guidepup Playwright setup have an additional utility method `.navigateToWebContent()`.
|
|
79
|
+
|
|
80
|
+
This method will navigate the screen reader to the first element of the document body in the browser.
|
|
81
|
+
|
|
82
|
+
Use this method after you navigate to a page and have made any necessary checks that the page has loaded as expected. For example, this is how you might use the method with NVDA:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// Navigate to the desired page
|
|
86
|
+
await page.goto("https://github.com/guidepup/guidepup", {
|
|
87
|
+
waitUntil: "load",
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Wait for page to be ready
|
|
91
|
+
await page.locator('header[role="banner"]').waitFor();
|
|
92
|
+
|
|
93
|
+
// Navigate to the web content
|
|
94
|
+
await nvda.navigateToWebContent();
|
|
95
|
+
|
|
96
|
+
// ... some commands
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Note:** This command clears all logs meaning `.spokenPhraseLog()` and `.itemTextLog()` are emptied. If logs from prior to the command are required, first store the logs in a variable for later use:
|
|
100
|
+
|
|
51
101
|
```ts
|
|
52
|
-
|
|
102
|
+
// ... some commands
|
|
103
|
+
|
|
104
|
+
// Store spoken phrases
|
|
105
|
+
const spokenPhrases = await nvda.spokenPhraseLog();
|
|
106
|
+
|
|
107
|
+
// Navigate to the web content
|
|
108
|
+
await nvda.navigateToWebContent();
|
|
109
|
+
|
|
110
|
+
// ... some commands
|
|
111
|
+
|
|
112
|
+
// Collect all spoken phrasees
|
|
113
|
+
const allSpokenPhrases = [...spokenPhrases, ...(await nvda.spokenPhraseLog())];
|
|
114
|
+
|
|
115
|
+
// ... do something with spoken phrases
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### VoiceOver Example
|
|
119
|
+
|
|
120
|
+
`playwright.config.ts`:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { devices, PlaywrightTestConfig } from "@playwright/test";
|
|
124
|
+
import { screenReaderConfig } from "@guidepup/playwright";
|
|
125
|
+
|
|
126
|
+
const config: PlaywrightTestConfig = {
|
|
127
|
+
...screenReaderConfig,
|
|
128
|
+
reportSlowTests: null,
|
|
129
|
+
timeout: 5 * 60 * 1000,
|
|
130
|
+
retries: 2,
|
|
131
|
+
projects: [
|
|
132
|
+
{
|
|
133
|
+
name: "webkit",
|
|
134
|
+
use: { ...devices["Desktop Safari"], headless: false },
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export default config;
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`voiceOver.spec.ts`:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { voiceOverTest as test } from "@guidepup/playwright";
|
|
53
146
|
import { expect } from "@playwright/test";
|
|
54
147
|
|
|
55
148
|
test.describe("Playwright VoiceOver", () => {
|
|
56
|
-
test("I can navigate the Guidepup Github page", async ({
|
|
149
|
+
test("I can navigate the Guidepup Github page with VoiceOver", async ({
|
|
57
150
|
page,
|
|
58
151
|
voiceOver,
|
|
59
152
|
}) => {
|
|
60
153
|
// Navigate to Guidepup GitHub page
|
|
61
154
|
await page.goto("https://github.com/guidepup/guidepup", {
|
|
62
|
-
waitUntil: "
|
|
155
|
+
waitUntil: "load",
|
|
63
156
|
});
|
|
64
157
|
|
|
65
|
-
// Wait for page to be ready
|
|
66
|
-
|
|
67
|
-
await
|
|
158
|
+
// Wait for page to be ready
|
|
159
|
+
const header = page.locator('header[role="banner"]');
|
|
160
|
+
await header.waitFor();
|
|
161
|
+
|
|
162
|
+
// Interact with the page
|
|
163
|
+
await voiceOver.navigateToWebContent();
|
|
68
164
|
|
|
69
165
|
// Move across the page menu to the Guidepup heading using VoiceOver
|
|
70
166
|
while ((await voiceOver.itemText()) !== "Guidepup heading level 1") {
|
|
71
|
-
await voiceOver.perform(voiceOver.
|
|
167
|
+
await voiceOver.perform(voiceOver.keyboardCommands.findNextHeading);
|
|
72
168
|
}
|
|
169
|
+
|
|
170
|
+
// Assert that the spoken phrases are as expected
|
|
171
|
+
expect(JSON.stringify(await voiceOver.spokenPhraseLog())).toMatchSnapshot();
|
|
172
|
+
});
|
|
73
173
|
});
|
|
74
174
|
```
|
|
75
175
|
|
|
76
|
-
|
|
176
|
+
### NVDA Example
|
|
77
177
|
|
|
78
|
-
|
|
79
|
-
Guidepup for VoiceOver automation.
|
|
178
|
+
`playwright.config.ts`:
|
|
80
179
|
|
|
81
180
|
```ts
|
|
82
181
|
import { devices, PlaywrightTestConfig } from "@playwright/test";
|
|
83
|
-
import {
|
|
182
|
+
import { screenReaderConfig } from "@guidepup/playwright";
|
|
84
183
|
|
|
85
184
|
const config: PlaywrightTestConfig = {
|
|
86
|
-
...
|
|
87
|
-
|
|
88
|
-
|
|
185
|
+
...screenReaderConfig,
|
|
186
|
+
reportSlowTests: null,
|
|
187
|
+
timeout: 5 * 60 * 1000,
|
|
188
|
+
retries: 2,
|
|
189
|
+
projects: [
|
|
190
|
+
{
|
|
191
|
+
name: "firefox",
|
|
192
|
+
use: { ...devices["Desktop Firefox"], headless: false },
|
|
193
|
+
},
|
|
194
|
+
],
|
|
89
195
|
};
|
|
90
196
|
|
|
91
197
|
export default config;
|
|
92
198
|
```
|
|
93
199
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
## Environment Setup
|
|
200
|
+
`nvda.spec.ts`:
|
|
97
201
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
npx @guidepup/setup
|
|
102
|
-
```
|
|
202
|
+
```ts
|
|
203
|
+
import { nvdaTest as test } from "@guidepup/playwright";
|
|
204
|
+
import { expect } from "@playwright/test";
|
|
103
205
|
|
|
104
|
-
|
|
206
|
+
test.describe("Playwright NVDA", () => {
|
|
207
|
+
test("I can navigate the Guidepup Github page with NVDA", async ({
|
|
208
|
+
page,
|
|
209
|
+
nvda,
|
|
210
|
+
}) => {
|
|
211
|
+
// Navigate to Guidepup GitHub page
|
|
212
|
+
await page.goto("https://github.com/guidepup/guidepup", {
|
|
213
|
+
waitUntil: "load",
|
|
214
|
+
});
|
|
105
215
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
```
|
|
216
|
+
// Wait for page to be ready and setup
|
|
217
|
+
const header = page.locator('header[role="banner"]');
|
|
218
|
+
await header.waitFor();
|
|
110
219
|
|
|
111
|
-
|
|
220
|
+
// Interact with the page
|
|
221
|
+
await nvda.navigateToWebContent();
|
|
112
222
|
|
|
113
|
-
|
|
223
|
+
// Move across the page menu to the Guidepup heading using NVDA
|
|
224
|
+
while (
|
|
225
|
+
!(await nvda.lastSpokenPhrase()).includes("Guidepup, heading, level 1")
|
|
226
|
+
) {
|
|
227
|
+
await nvda.perform(nvda.keyboardCommands.moveToNextHeading);
|
|
228
|
+
}
|
|
114
229
|
|
|
115
|
-
|
|
230
|
+
// Assert that the spoken phrases are as expected
|
|
231
|
+
expect(JSON.stringify(await nvda.spokenPhraseLog())).toMatchSnapshot();
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
```
|
|
116
235
|
|
|
117
|
-
|
|
236
|
+
## Powerful Tooling
|
|
118
237
|
|
|
119
|
-
|
|
238
|
+
Check out some of the other Guidepup modules:
|
|
120
239
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
240
|
+
- [`@guidepup/guidepup`](https://github.com/guidepup/guidepup/) - Reliable automation for your screen reader a11y workflows through JavaScript supporting VoiceOver and NVDA.
|
|
241
|
+
- [`@guidepup/setup`](https://github.com/guidepup/setup/) - Set up your local or CI environment for screen reader test automation.
|
|
242
|
+
- [`@guidepup/virtual-screen-reader`](https://github.com/guidepup/virtual-screen-reader/) - Reliable unit testing for your screen reader a11y workflows.
|
|
243
|
+
- [`@guidepup/jest`](https://github.com/guidepup/jest/) - Jest matchers for reliable unit testing of your screen reader a11y workflows.
|
|
124
244
|
|
|
125
|
-
##
|
|
245
|
+
## Resources
|
|
126
246
|
|
|
127
|
-
[
|
|
247
|
+
- [Documentation](https://www.guidepup.dev/docs/example)
|
|
248
|
+
- [API Reference](https://www.guidepup.dev/docs/api/class-guidepup)
|
|
249
|
+
- [Contributing](.github/CONTRIBUTING.md)
|
|
250
|
+
- [Changelog](https://github.com/guidepup/guidepup-playwright/releases)
|
|
251
|
+
- [MIT License](https://github.com/guidepup/guidepup-playwright/blob/main/LICENSE)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applicationNameMap = void 0;
|
|
4
|
+
exports.applicationNameMap = {
|
|
5
|
+
chromium: "Chromium",
|
|
6
|
+
chrome: "Google Chrome",
|
|
7
|
+
"chrome-beta": "Google Chrome Beta",
|
|
8
|
+
msedge: "Microsoft Edge",
|
|
9
|
+
"msedge-beta": "Microsoft Edge Beta",
|
|
10
|
+
"msedge-dev": "Microsoft Edge Dev",
|
|
11
|
+
firefox: "Nightly",
|
|
12
|
+
webkit: "Playwright",
|
|
13
|
+
};
|
package/lib/index.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { nvdaTest } from "./nvdaTest";
|
|
2
|
+
export type { NVDAPlaywright } from "./nvdaTest";
|
|
3
|
+
export { screenReaderConfig } from "./screenReaderConfig";
|
|
4
|
+
export { voiceOverTest } from "./voiceOverTest";
|
|
5
|
+
export type { VoiceOverPlaywright } from "./voiceOverTest";
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
var
|
|
5
|
-
Object.defineProperty(exports, "
|
|
6
|
-
var
|
|
7
|
-
Object.defineProperty(exports, "
|
|
3
|
+
exports.voiceOverTest = exports.screenReaderConfig = exports.nvdaTest = void 0;
|
|
4
|
+
var nvdaTest_1 = require("./nvdaTest");
|
|
5
|
+
Object.defineProperty(exports, "nvdaTest", { enumerable: true, get: function () { return nvdaTest_1.nvdaTest; } });
|
|
6
|
+
var screenReaderConfig_1 = require("./screenReaderConfig");
|
|
7
|
+
Object.defineProperty(exports, "screenReaderConfig", { enumerable: true, get: function () { return screenReaderConfig_1.screenReaderConfig; } });
|
|
8
|
+
var voiceOverTest_1 = require("./voiceOverTest");
|
|
9
|
+
Object.defineProperty(exports, "voiceOverTest", { enumerable: true, get: function () { return voiceOverTest_1.voiceOverTest; } });
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { NVDA } from "@guidepup/guidepup";
|
|
2
|
+
/**
|
|
3
|
+
* [API Reference](https://www.guidepup.dev/docs/api/class-nvda)
|
|
4
|
+
*
|
|
5
|
+
* This object can be used to launch and control NVDA.
|
|
6
|
+
*
|
|
7
|
+
* Here's a typical example:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { nvda } from "@guidepup/guidepup";
|
|
11
|
+
*
|
|
12
|
+
* (async () => {
|
|
13
|
+
* // Start NVDA.
|
|
14
|
+
* await nvda.start();
|
|
15
|
+
*
|
|
16
|
+
* // Move to the next item.
|
|
17
|
+
* await nvda.next();
|
|
18
|
+
*
|
|
19
|
+
* // Stop NVDA.
|
|
20
|
+
* await nvda.stop();
|
|
21
|
+
* })();
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export interface NVDAPlaywright extends NVDA {
|
|
25
|
+
/**
|
|
26
|
+
* Guidepup Playwright specific command that navigates NVDA to the beginning
|
|
27
|
+
* of the browser's web content.
|
|
28
|
+
*
|
|
29
|
+
* This command should be used after page navigation.
|
|
30
|
+
*
|
|
31
|
+
* Note: this command clears all logs.
|
|
32
|
+
*/
|
|
33
|
+
navigateToWebContent(): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* These tests extend the default Playwright environment that launches the
|
|
37
|
+
* browser with a running instance of the NVDA screen reader for Windows.
|
|
38
|
+
*
|
|
39
|
+
* A fresh started NVDA instance `nvda` is provided to each test.
|
|
40
|
+
*/
|
|
41
|
+
export declare const nvdaTest: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & {
|
|
42
|
+
/**
|
|
43
|
+
* [API Reference](https://www.guidepup.dev/docs/api/class-nvda)
|
|
44
|
+
*
|
|
45
|
+
* This object can be used to launch and control NVDA.
|
|
46
|
+
*
|
|
47
|
+
* Here's a typical example:
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* import { nvda } from "@guidepup/guidepup";
|
|
51
|
+
*
|
|
52
|
+
* (async () => {
|
|
53
|
+
* // Start NVDA.
|
|
54
|
+
* await nvda.start();
|
|
55
|
+
*
|
|
56
|
+
* // Move to the next item.
|
|
57
|
+
* await nvda.next();
|
|
58
|
+
*
|
|
59
|
+
* // Stop NVDA.
|
|
60
|
+
* await nvda.stop();
|
|
61
|
+
* })();
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
nvda: NVDAPlaywright;
|
|
65
|
+
}, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
|
package/lib/nvdaTest.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nvdaTest = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const guidepup_1 = require("@guidepup/guidepup");
|
|
6
|
+
const applicationNameMap_1 = require("./applicationNameMap");
|
|
7
|
+
const nvdaPlaywright = guidepup_1.nvda;
|
|
8
|
+
const MAX_APPLICATION_SWITCH_RETRY_COUNT = 10;
|
|
9
|
+
const SWITCH_APPLICATION = {
|
|
10
|
+
keyCode: [guidepup_1.WindowsKeyCodes.Escape],
|
|
11
|
+
modifiers: [guidepup_1.WindowsModifiers.Alt],
|
|
12
|
+
};
|
|
13
|
+
const MOVE_TO_TOP = {
|
|
14
|
+
keyCode: [guidepup_1.WindowsKeyCodes.Home],
|
|
15
|
+
modifiers: [guidepup_1.WindowsModifiers.Control],
|
|
16
|
+
};
|
|
17
|
+
const focusBrowser = async ({ applicationName, }) => {
|
|
18
|
+
await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.reportTitle);
|
|
19
|
+
let windowTitle = await nvdaPlaywright.lastSpokenPhrase();
|
|
20
|
+
if (windowTitle.includes(applicationName)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
let applicationSwitchRetryCount = 0;
|
|
24
|
+
while (applicationSwitchRetryCount < MAX_APPLICATION_SWITCH_RETRY_COUNT) {
|
|
25
|
+
applicationSwitchRetryCount++;
|
|
26
|
+
await nvdaPlaywright.perform(SWITCH_APPLICATION);
|
|
27
|
+
await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.reportTitle);
|
|
28
|
+
windowTitle = await nvdaPlaywright.lastSpokenPhrase();
|
|
29
|
+
if (windowTitle.includes(applicationName)) {
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* These tests extend the default Playwright environment that launches the
|
|
36
|
+
* browser with a running instance of the NVDA screen reader for Windows.
|
|
37
|
+
*
|
|
38
|
+
* A fresh started NVDA instance `nvda` is provided to each test.
|
|
39
|
+
*/
|
|
40
|
+
exports.nvdaTest = test_1.test.extend({
|
|
41
|
+
nvda: async ({ browserName, page }, use) => {
|
|
42
|
+
try {
|
|
43
|
+
const applicationName = applicationNameMap_1.applicationNameMap[browserName];
|
|
44
|
+
if (!applicationName) {
|
|
45
|
+
throw new Error(`Browser ${browserName} is not installed.`);
|
|
46
|
+
}
|
|
47
|
+
await page.goto("about:blank", { waitUntil: "load" });
|
|
48
|
+
await page.bringToFront();
|
|
49
|
+
nvdaPlaywright.navigateToWebContent = async () => {
|
|
50
|
+
// Make sure NVDA is not in focus mode.
|
|
51
|
+
await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.exitFocusMode);
|
|
52
|
+
// Ensure application is brought to front and focused.
|
|
53
|
+
await focusBrowser({ applicationName });
|
|
54
|
+
// NVDA appears to not work well with Firefox when switching between
|
|
55
|
+
// applications resulting in the entire browser window having NVDA focus
|
|
56
|
+
// with focus mode.
|
|
57
|
+
//
|
|
58
|
+
// One workaround is to tab to the next focusable item. From there we can
|
|
59
|
+
// toggle into (yes although we are already in it...) focus mode and back
|
|
60
|
+
// out. In case this ever transpires to not happen as expect, we then ensure
|
|
61
|
+
// we exit focus mode and move NVDA to the top of the page.
|
|
62
|
+
//
|
|
63
|
+
// REF: https://github.com/nvaccess/nvda/issues/5758
|
|
64
|
+
await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.readNextFocusableItem);
|
|
65
|
+
await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.toggleBetweenBrowseAndFocusMode);
|
|
66
|
+
await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.toggleBetweenBrowseAndFocusMode);
|
|
67
|
+
await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.exitFocusMode);
|
|
68
|
+
await nvdaPlaywright.perform(MOVE_TO_TOP);
|
|
69
|
+
// Clear out logs.
|
|
70
|
+
await nvdaPlaywright.clearItemTextLog();
|
|
71
|
+
await nvdaPlaywright.clearSpokenPhraseLog();
|
|
72
|
+
};
|
|
73
|
+
await nvdaPlaywright.start();
|
|
74
|
+
await use(nvdaPlaywright);
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
try {
|
|
78
|
+
await nvdaPlaywright.stop();
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// swallow stop failure
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { PlaywrightTestConfig } from "@playwright/test";
|
|
2
|
+
export type ScreenReaderPlaywrightTestConfig = PlaywrightTestConfig;
|
|
3
|
+
/**
|
|
4
|
+
* Minimal required configuration for Screen Reader tests in Playwright.
|
|
5
|
+
*/
|
|
6
|
+
export declare const screenReaderConfig: ScreenReaderPlaywrightTestConfig;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.screenReaderConfig = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Minimal required configuration for Screen Reader tests in Playwright.
|
|
6
|
+
*/
|
|
7
|
+
exports.screenReaderConfig = {
|
|
8
|
+
/**
|
|
9
|
+
* We can only run a single instance of Screen Readers, so we must run a
|
|
10
|
+
* single test at a time.
|
|
11
|
+
*/
|
|
12
|
+
workers: 1,
|
|
13
|
+
/**
|
|
14
|
+
* We can't parallelize Screen Reader tests as they are singletons, you can't
|
|
15
|
+
* start multiple instances at once.
|
|
16
|
+
*/
|
|
17
|
+
fullyParallel: false,
|
|
18
|
+
use: {
|
|
19
|
+
/**
|
|
20
|
+
* Screen Readers don't work against headless browsers.
|
|
21
|
+
*/
|
|
22
|
+
headless: false,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { VoiceOver } from "@guidepup/guidepup";
|
|
2
|
+
/**
|
|
3
|
+
* [API Reference](https://www.guidepup.dev/docs/api/class-voiceover)
|
|
4
|
+
*
|
|
5
|
+
* This object can be used to launch and control VoiceOver.
|
|
6
|
+
*
|
|
7
|
+
* Here's a typical example:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { voiceOver } from "@guidepup/guidepup";
|
|
11
|
+
*
|
|
12
|
+
* (async () => {
|
|
13
|
+
* // Start VoiceOver.
|
|
14
|
+
* await voiceOver.start();
|
|
15
|
+
*
|
|
16
|
+
* // Move to the next item.
|
|
17
|
+
* await voiceOver.next();
|
|
18
|
+
*
|
|
19
|
+
* // ... perform some commands.
|
|
20
|
+
*
|
|
21
|
+
* // Stop VoiceOver.
|
|
22
|
+
* await voiceOver.stop();
|
|
23
|
+
* })();
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export interface VoiceOverPlaywright extends VoiceOver {
|
|
27
|
+
/**
|
|
28
|
+
* Guidepup Playwright specific command that navigates VoiceOver to the beginning
|
|
29
|
+
* of the browser's web content.
|
|
30
|
+
*
|
|
31
|
+
* This command should be used after page navigation.
|
|
32
|
+
*
|
|
33
|
+
* Note: this command clears all logs.
|
|
34
|
+
*/
|
|
35
|
+
navigateToWebContent(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* These tests extend the default Playwright environment that launches the
|
|
39
|
+
* browser with a running instance of the VoiceOver screen reader for MacOS.
|
|
40
|
+
*
|
|
41
|
+
* A fresh started VoiceOver instance `voiceOver` is provided to each test.
|
|
42
|
+
*/
|
|
43
|
+
export declare const voiceOverTest: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & {
|
|
44
|
+
/**
|
|
45
|
+
* [API Reference](https://www.guidepup.dev/docs/api/class-voiceover)
|
|
46
|
+
*
|
|
47
|
+
* This object can be used to launch and control VoiceOver.
|
|
48
|
+
*
|
|
49
|
+
* Here's a typical example:
|
|
50
|
+
*
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { voiceOver } from "@guidepup/guidepup";
|
|
53
|
+
*
|
|
54
|
+
* (async () => {
|
|
55
|
+
* // Start VoiceOver.
|
|
56
|
+
* await voiceOver.start();
|
|
57
|
+
*
|
|
58
|
+
* // Move to the next item.
|
|
59
|
+
* await voiceOver.next();
|
|
60
|
+
*
|
|
61
|
+
* // ... perform some commands.
|
|
62
|
+
*
|
|
63
|
+
* // Stop VoiceOver.
|
|
64
|
+
* await voiceOver.stop();
|
|
65
|
+
* })();
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
voiceOver: VoiceOverPlaywright;
|
|
69
|
+
}, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.voiceOverTest = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const guidepup_1 = require("@guidepup/guidepup");
|
|
6
|
+
const applicationNameMap_1 = require("./applicationNameMap");
|
|
7
|
+
const voiceOverPlaywright = guidepup_1.voiceOver;
|
|
8
|
+
/**
|
|
9
|
+
* These tests extend the default Playwright environment that launches the
|
|
10
|
+
* browser with a running instance of the VoiceOver screen reader for MacOS.
|
|
11
|
+
*
|
|
12
|
+
* A fresh started VoiceOver instance `voiceOver` is provided to each test.
|
|
13
|
+
*/
|
|
14
|
+
exports.voiceOverTest = test_1.test.extend({
|
|
15
|
+
voiceOver: async ({ browserName, page }, use) => {
|
|
16
|
+
try {
|
|
17
|
+
const applicationName = applicationNameMap_1.applicationNameMap[browserName];
|
|
18
|
+
if (!applicationName) {
|
|
19
|
+
throw new Error(`Browser ${browserName} is not installed.`);
|
|
20
|
+
}
|
|
21
|
+
voiceOverPlaywright.navigateToWebContent = async () => {
|
|
22
|
+
// Ensure application is brought to front and focused.
|
|
23
|
+
await (0, guidepup_1.macOSActivate)(applicationName);
|
|
24
|
+
// Ensure the document is ready and focused.
|
|
25
|
+
await page.bringToFront();
|
|
26
|
+
await page.locator("body").waitFor();
|
|
27
|
+
await page.locator("body").focus();
|
|
28
|
+
// Navigate to the beginning of the web content.
|
|
29
|
+
await voiceOverPlaywright.interact();
|
|
30
|
+
await voiceOverPlaywright.perform(voiceOverPlaywright.keyboardCommands.jumpToLeftEdge);
|
|
31
|
+
// Clear out logs.
|
|
32
|
+
await voiceOverPlaywright.clearItemTextLog();
|
|
33
|
+
await voiceOverPlaywright.clearSpokenPhraseLog();
|
|
34
|
+
};
|
|
35
|
+
await voiceOverPlaywright.start();
|
|
36
|
+
await (0, guidepup_1.macOSActivate)(applicationName);
|
|
37
|
+
await use(voiceOverPlaywright);
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
try {
|
|
41
|
+
await voiceOverPlaywright.stop();
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// swallow stop failure
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guidepup/playwright",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "Screen reader driver for Playwright tests.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
@@ -24,33 +24,36 @@
|
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "yarn clean && yarn compile",
|
|
26
26
|
"ci": "yarn clean && yarn lint && yarn build",
|
|
27
|
-
"clean": "rimraf lib",
|
|
27
|
+
"clean": "rimraf lib test-results recordings",
|
|
28
28
|
"compile": "tsc",
|
|
29
29
|
"lint": "eslint . --ext .ts",
|
|
30
30
|
"lint:fix": "yarn lint --fix",
|
|
31
31
|
"prepublish": "yarn build",
|
|
32
32
|
"pretest": "npx playwright install chromium firefox webkit",
|
|
33
|
-
"test": "yarn test:chromium && yarn test:firefox
|
|
34
|
-
"test:
|
|
35
|
-
"test:
|
|
36
|
-
"test:
|
|
33
|
+
"test:nvda": "yarn test:nvda:chromium && yarn test:nvda:firefox",
|
|
34
|
+
"test:voiceover": "yarn test:voiceover:chromium && yarn test:voiceover:firefox && yarn test:voiceover:webkit",
|
|
35
|
+
"test:nvda:chromium": "playwright test --config ./examples/playwright-nvda/chromium.config.ts ./examples/playwright-nvda/tests/chromium/",
|
|
36
|
+
"test:nvda:firefox": "playwright test --config ./examples/playwright-nvda/firefox.config.ts ./examples/playwright-nvda/tests/firefox/",
|
|
37
|
+
"test:voiceover:chromium": "playwright test --config ./examples/playwright-voiceover/chromium.config.ts ./examples/playwright-voiceover/tests/chromium/",
|
|
38
|
+
"test:voiceover:firefox": "playwright test --config ./examples/playwright-voiceover/firefox.config.ts ./examples/playwright-voiceover/tests/firefox/",
|
|
39
|
+
"test:voiceover:webkit": "playwright test --config ./examples/playwright-voiceover/webkit.config.ts ./examples/playwright-voiceover/tests/webkit/"
|
|
37
40
|
},
|
|
38
41
|
"devDependencies": {
|
|
39
|
-
"@guidepup/guidepup": "^0.
|
|
42
|
+
"@guidepup/guidepup": "^0.22.0",
|
|
40
43
|
"@playwright/test": "^1.40.1",
|
|
41
|
-
"@types/node": "^20.10.
|
|
42
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
43
|
-
"@typescript-eslint/parser": "^6.
|
|
44
|
+
"@types/node": "^20.10.6",
|
|
45
|
+
"@typescript-eslint/eslint-plugin": "^6.18.0",
|
|
46
|
+
"@typescript-eslint/parser": "^6.18.0",
|
|
44
47
|
"eslint": "^8.56.0",
|
|
45
48
|
"eslint-config-prettier": "^9.1.0",
|
|
46
49
|
"rimraf": "^5.0.5",
|
|
47
50
|
"ts-node": "^10.9.2",
|
|
48
|
-
"typedoc": "^0.25.
|
|
51
|
+
"typedoc": "^0.25.6",
|
|
49
52
|
"typescript": "^5.3.3"
|
|
50
53
|
},
|
|
51
54
|
"peerDependencies": {
|
|
52
|
-
"@guidepup/guidepup": "^0.
|
|
53
|
-
"@playwright/test": "^1.
|
|
55
|
+
"@guidepup/guidepup": "^0.22.0",
|
|
56
|
+
"@playwright/test": "^1.40.1"
|
|
54
57
|
},
|
|
55
58
|
"resolutions": {
|
|
56
59
|
"strip-ansi": "6.0.1",
|
package/lib/voConfig.d.ts
DELETED
package/lib/voConfig.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.voConfig = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Minimal required configuration for VoiceOver tests in Playwright.
|
|
6
|
-
*/
|
|
7
|
-
exports.voConfig = {
|
|
8
|
-
/**
|
|
9
|
-
* We can only run a single instance of VoiceOver, so we must run a single
|
|
10
|
-
* test at a time.
|
|
11
|
-
*/
|
|
12
|
-
workers: 1,
|
|
13
|
-
use: {
|
|
14
|
-
/**
|
|
15
|
-
* Although VoiceOver can interact with headless applications, not all
|
|
16
|
-
* behaviours work as expected.
|
|
17
|
-
*/
|
|
18
|
-
headless: false,
|
|
19
|
-
},
|
|
20
|
-
};
|
package/lib/voTest.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { VoiceOver } from "@guidepup/guidepup";
|
|
2
|
-
/**
|
|
3
|
-
* These tests extend the default Playwright environment that launches the
|
|
4
|
-
* browser with a running instance of the VoiceOver screen reader for MacOS.
|
|
5
|
-
*
|
|
6
|
-
* A fresh started VoiceOver instance `vo` is provided to each test.
|
|
7
|
-
*/
|
|
8
|
-
declare const voTest: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & {
|
|
9
|
-
voiceOver: VoiceOver;
|
|
10
|
-
}, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
|
|
11
|
-
export { voTest };
|
package/lib/voTest.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.voTest = void 0;
|
|
4
|
-
const test_1 = require("@playwright/test");
|
|
5
|
-
const guidepup_1 = require("@guidepup/guidepup");
|
|
6
|
-
const applicationNameMap = {
|
|
7
|
-
chromium: "Chromium",
|
|
8
|
-
chrome: "Google Chrome",
|
|
9
|
-
"chrome-beta": "Google Chrome Beta",
|
|
10
|
-
msedge: "Microsoft Edge",
|
|
11
|
-
"msedge-beta": "Microsoft Edge Beta",
|
|
12
|
-
"msedge-dev": "Microsoft Edge Dev",
|
|
13
|
-
firefox: "Nightly",
|
|
14
|
-
webkit: "Playwright",
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* These tests extend the default Playwright environment that launches the
|
|
18
|
-
* browser with a running instance of the VoiceOver screen reader for MacOS.
|
|
19
|
-
*
|
|
20
|
-
* A fresh started VoiceOver instance `vo` is provided to each test.
|
|
21
|
-
*/
|
|
22
|
-
const voTest = test_1.test.extend({
|
|
23
|
-
voiceOver: async ({ browserName }, use) => {
|
|
24
|
-
try {
|
|
25
|
-
await guidepup_1.voiceOver.start();
|
|
26
|
-
await (0, guidepup_1.macOSActivate)(applicationNameMap[browserName]);
|
|
27
|
-
await use(guidepup_1.voiceOver);
|
|
28
|
-
}
|
|
29
|
-
finally {
|
|
30
|
-
try {
|
|
31
|
-
await guidepup_1.voiceOver.stop();
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
// swallow stop failure
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
exports.voTest = voTest;
|