@guidepup/playwright 0.15.0 → 0.16.0

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 CHANGED
@@ -6,12 +6,11 @@
6
6
 
7
7
  ## [Documentation](https://guidepup.dev) | [API Reference](https://www.guidepup.dev/docs/api/class-guidepup)
8
8
 
9
- [![MacOS Monetary Support](https://img.shields.io/badge/macos-Monetary-blue.svg?logo=apple)](https://apps.apple.com/us/app/macos-monterey/id1576738294)
10
- [![MacOS Ventura Support](https://img.shields.io/badge/macos-Ventura-blue.svg?logo=apple)](https://apps.apple.com/us/app/macos-ventura/id1638787999)
11
9
  [![MacOS Sonoma Support](https://img.shields.io/badge/macos-Somona-blue.svg?logo=apple)](https://apps.apple.com/us/app/macos-sonoma/id6450717509)
12
- [![Windows 10 Support](https://img.shields.io/badge/windows-10-blue.svg?logo=windows10)](https://www.microsoft.com/en-gb/software-download/windows10ISO)
13
- [![Windows Server 2019 Support](https://img.shields.io/badge/windows_server-2019-blue.svg?logo=windows)](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2019)
10
+ [![MacOS Sequoia Support](https://img.shields.io/badge/macos-Somona-blue.svg?logo=apple)](https://apps.apple.com/us/app/macos-sequoia/id6596773750)
11
+ [![MacOS Tahoe Support](https://img.shields.io/badge/macos-Tahoe-blue.svg?logo=apple)](https://www.apple.com/uk/os/macos/)
14
12
  [![Windows Server 2022 Support](https://img.shields.io/badge/windows_server-2022-blue.svg?logo=windows)](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2022)
13
+ [![Windows Server 2025 Support](https://img.shields.io/badge/windows_server-2025-blue.svg?logo=windows)](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2025)
15
14
 
16
15
  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
16
 
@@ -95,7 +94,7 @@ await nvda.navigateToWebContent();
95
94
  // ... some commands
96
95
  ```
97
96
 
98
- **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:
97
+ **Note:** This command clears all logs by default meaning `.spokenPhraseLog()` and `.itemTextLog()` are emptied. If logs from prior to the command are required, either store the logs in a variable for later use:
99
98
 
100
99
  ```ts
101
100
  // ... some commands
@@ -114,6 +113,17 @@ const allSpokenPhrases = [...spokenPhrases, ...(await nvda.spokenPhraseLog())];
114
113
  // ... do something with spoken phrases
115
114
  ```
116
115
 
116
+ or pass a flag to prevent log clearing:
117
+
118
+ ```ts
119
+ // ... some commands
120
+
121
+ // Navigate to the web content
122
+ await nvda.navigateToWebContent(false);
123
+
124
+ // ... some commands
125
+ ```
126
+
117
127
  ### Providing Screen Reader Start Options
118
128
 
119
129
  The options provided to `nvda.start([options])` or `voiceOver.start([options])` can be configured using `test.use(config)` as follows:
@@ -122,16 +132,20 @@ The options provided to `nvda.start([options])` or `voiceOver.start([options])`
122
132
  // VoiceOver Example
123
133
  import { voiceOverTest as test } from "@guidepup/playwright";
124
134
 
125
- test.use({ voiceOverStartOptions: { capture: "initial" } });
135
+ // Capture all spoken phrases, including usage hints
136
+ test.use({ voiceOverStartOptions: { capture: true } });
126
137
  ```
127
138
 
128
139
  ```ts
129
140
  // NVDA Example
130
141
  import { nvdaTest as test } from "@guidepup/playwright";
131
142
 
132
- test.use({ nvdaStartOptions: { capture: "initial" } });
143
+ // Capture all spoken phrases, including usage hints
144
+ test.use({ nvdaStartOptions: { capture: true } });
133
145
  ```
134
146
 
147
+ The default for VoiceOver and NVDA is set to `"initial"`. `true` captures all spoken phrases, including usage hints. `false` disables spoken phrase capture.
148
+
135
149
  ### VoiceOver Example
136
150
 
137
151
  `playwright.config.ts`:
@@ -148,6 +162,8 @@ const config: PlaywrightTestConfig = {
148
162
  projects: [
149
163
  {
150
164
  name: "webkit",
165
+ // Take care to ensure all usage is headed - screen readers cannot
166
+ // operate against headless browsers.
151
167
  use: { ...devices["Desktop Safari"], headless: false },
152
168
  },
153
169
  ],
@@ -206,6 +222,8 @@ const config: PlaywrightTestConfig = {
206
222
  projects: [
207
223
  {
208
224
  name: "firefox",
225
+ // Take care to ensure all usage is headed - screen readers cannot
226
+ // operate against headless browsers.
209
227
  use: { ...devices["Desktop Firefox"], headless: false },
210
228
  },
211
229
  ],
package/lib/nvdaTest.d.ts CHANGED
@@ -32,9 +32,9 @@ export interface NVDAPlaywright extends NVDA {
32
32
  *
33
33
  * This command should be used after page navigation.
34
34
  *
35
- * Note: this command clears all logs.
35
+ * Note: this command clears all logs by default.
36
36
  */
37
- navigateToWebContent(): Promise<void>;
37
+ navigateToWebContent(clearLogs?: boolean): Promise<void>;
38
38
  }
39
39
  /**
40
40
  * These tests extend the default Playwright environment that launches the
package/lib/nvdaTest.js CHANGED
@@ -14,10 +14,14 @@ const MOVE_TO_TOP = {
14
14
  keyCode: [guidepup_1.WindowsKeyCodes.Home],
15
15
  modifiers: [guidepup_1.WindowsModifiers.Control],
16
16
  };
17
- const focusBrowser = async ({ applicationName, }) => {
17
+ const hasFocus = ({ applicationName, pageTitle, windowTitle, }) => {
18
+ return ((pageTitle.length && windowTitle.startsWith(pageTitle)) ||
19
+ windowTitle.includes(applicationName));
20
+ };
21
+ const focusBrowser = async ({ applicationName, pageTitle, }) => {
18
22
  await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.reportTitle);
19
23
  let windowTitle = await nvdaPlaywright.lastSpokenPhrase();
20
- if (windowTitle.includes(applicationName)) {
24
+ if (hasFocus({ applicationName, pageTitle, windowTitle })) {
21
25
  return;
22
26
  }
23
27
  let applicationSwitchRetryCount = 0;
@@ -26,7 +30,7 @@ const focusBrowser = async ({ applicationName, }) => {
26
30
  await nvdaPlaywright.perform(SWITCH_APPLICATION);
27
31
  await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.reportTitle);
28
32
  windowTitle = await nvdaPlaywright.lastSpokenPhrase();
29
- if (windowTitle.includes(applicationName)) {
33
+ if (hasFocus({ applicationName, pageTitle, windowTitle })) {
30
34
  break;
31
35
  }
32
36
  }
@@ -38,7 +42,7 @@ const focusBrowser = async ({ applicationName, }) => {
38
42
  * A fresh started NVDA instance `nvda` is provided to each test.
39
43
  */
40
44
  exports.nvdaTest = test_1.test.extend({
41
- nvdaStartOptions: {},
45
+ nvdaStartOptions: { capture: "initial" },
42
46
  nvda: async ({ browserName, page, nvdaStartOptions }, use) => {
43
47
  try {
44
48
  const applicationName = applicationNameMap_1.applicationNameMap[browserName];
@@ -47,11 +51,18 @@ exports.nvdaTest = test_1.test.extend({
47
51
  }
48
52
  await page.goto("about:blank", { waitUntil: "load" });
49
53
  await page.bringToFront();
50
- nvdaPlaywright.navigateToWebContent = async () => {
54
+ nvdaPlaywright.navigateToWebContent = async (clearLogs = true) => {
51
55
  // Make sure NVDA is not in focus mode.
52
56
  await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.exitFocusMode);
57
+ const pageTitle = await page.title();
53
58
  // Ensure application is brought to front and focused.
54
- await focusBrowser({ applicationName });
59
+ await focusBrowser({ applicationName, pageTitle });
60
+ // Ensure the document is ready and focused.
61
+ await page.bringToFront();
62
+ await page.locator("body").waitFor();
63
+ await page.locator("body").focus();
64
+ await page.locator("body").click();
65
+ await page.locator("body").blur();
55
66
  // NVDA appears to not work well with Firefox when switching between
56
67
  // applications resulting in the entire browser window having NVDA focus
57
68
  // with focus mode.
@@ -67,9 +78,11 @@ exports.nvdaTest = test_1.test.extend({
67
78
  await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.toggleBetweenBrowseAndFocusMode);
68
79
  await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.exitFocusMode);
69
80
  await nvdaPlaywright.perform(MOVE_TO_TOP);
70
- // Clear out logs.
71
- await nvdaPlaywright.clearItemTextLog();
72
- await nvdaPlaywright.clearSpokenPhraseLog();
81
+ if (clearLogs) {
82
+ // Clear out logs.
83
+ await nvdaPlaywright.clearItemTextLog();
84
+ await nvdaPlaywright.clearSpokenPhraseLog();
85
+ }
73
86
  };
74
87
  await nvdaPlaywright.start(nvdaStartOptions);
75
88
  await use(nvdaPlaywright);
@@ -2,5 +2,7 @@ import { PlaywrightTestConfig } from "@playwright/test";
2
2
  export type ScreenReaderPlaywrightTestConfig = PlaywrightTestConfig;
3
3
  /**
4
4
  * Minimal required configuration for Screen Reader tests in Playwright.
5
+ *
6
+ * Depending on your configuration this may not be sufficient.
5
7
  */
6
8
  export declare const screenReaderConfig: ScreenReaderPlaywrightTestConfig;
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.screenReaderConfig = void 0;
4
4
  /**
5
5
  * Minimal required configuration for Screen Reader tests in Playwright.
6
+ *
7
+ * Depending on your configuration this may not be sufficient.
6
8
  */
7
9
  exports.screenReaderConfig = {
8
10
  /**
@@ -28,11 +28,11 @@ export interface VoiceOverPlaywright extends VoiceOver {
28
28
  * Guidepup Playwright specific command that navigates VoiceOver to the beginning
29
29
  * of the browser's web content.
30
30
  *
31
- * This command should be used after page navigation.
31
+ * This command should be used after a page navigation has completed.
32
32
  *
33
- * Note: this command clears all logs.
33
+ * Note: this command clears all logs by default.
34
34
  */
35
- navigateToWebContent(): Promise<void>;
35
+ navigateToWebContent(clearLogs?: boolean): Promise<void>;
36
36
  }
37
37
  /**
38
38
  * These tests extend the default Playwright environment that launches the
@@ -12,26 +12,36 @@ const voiceOverPlaywright = guidepup_1.voiceOver;
12
12
  * A fresh started VoiceOver instance `voiceOver` is provided to each test.
13
13
  */
14
14
  exports.voiceOverTest = test_1.test.extend({
15
- voiceOverStartOptions: {},
15
+ voiceOverStartOptions: { capture: "initial" },
16
16
  voiceOver: async ({ browserName, page, voiceOverStartOptions }, use) => {
17
17
  try {
18
18
  const applicationName = applicationNameMap_1.applicationNameMap[browserName];
19
19
  if (!applicationName) {
20
20
  throw new Error(`Browser ${browserName} is not installed.`);
21
21
  }
22
- voiceOverPlaywright.navigateToWebContent = async () => {
22
+ voiceOverPlaywright.navigateToWebContent = async (clearLogs = true) => {
23
23
  // Ensure application is brought to front and focused.
24
24
  await (0, guidepup_1.macOSActivate)(applicationName);
25
25
  // Ensure the document is ready and focused.
26
26
  await page.bringToFront();
27
27
  await page.locator("body").waitFor();
28
28
  await page.locator("body").focus();
29
- // Navigate to the beginning of the web content.
29
+ await page.locator("body").click();
30
+ await page.locator("body").blur();
31
+ // Try to navigate into web content.
30
32
  await voiceOverPlaywright.interact();
31
- await voiceOverPlaywright.perform(voiceOverPlaywright.keyboardCommands.jumpToLeftEdge);
32
- // Clear out logs.
33
- await voiceOverPlaywright.clearItemTextLog();
34
- await voiceOverPlaywright.clearSpokenPhraseLog();
33
+ // Series of find previous commands to escape accidental interaction
34
+ // with sub-content of web content area.
35
+ await voiceOverPlaywright.perform(voiceOverPlaywright.keyboardCommands.findPreviousHeading);
36
+ await voiceOverPlaywright.perform(voiceOverPlaywright.keyboardCommands.findPreviousGraphic);
37
+ await voiceOverPlaywright.perform(voiceOverPlaywright.keyboardCommands.findPreviousPlainText);
38
+ // Navigate to the beginning of the web content.
39
+ await voiceOverPlaywright.perform(voiceOverPlaywright.keyboardCommands.moveToBeginningOfText);
40
+ if (clearLogs) {
41
+ // Clear out logs.
42
+ await voiceOverPlaywright.clearItemTextLog();
43
+ await voiceOverPlaywright.clearSpokenPhraseLog();
44
+ }
35
45
  };
36
46
  await voiceOverPlaywright.start(voiceOverStartOptions);
37
47
  await (0, guidepup_1.macOSActivate)(applicationName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guidepup/playwright",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Screen reader driver for Playwright tests.",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -8,7 +8,7 @@
8
8
  "license": "MIT",
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "https://github.com/guidepup/guidepup-playwright"
11
+ "url": "git+https://github.com/guidepup/guidepup-playwright.git"
12
12
  },
13
13
  "bugs": {
14
14
  "url": "https://github.com/guidepup/guidepup-playwright/issues"
@@ -42,9 +42,9 @@
42
42
  "test:voiceover:webkit": "playwright test --config ./examples/playwright-voiceover/webkit.config.ts ./examples/playwright-voiceover/tests/webkit/"
43
43
  },
44
44
  "devDependencies": {
45
- "@guidepup/guidepup": "^0.24.0",
45
+ "@guidepup/guidepup": "^0.25.0",
46
46
  "@guidepup/record": "^0.1.0",
47
- "@playwright/test": "^1.58.0",
47
+ "@playwright/test": "^1.60.0",
48
48
  "@types/node": "^24.10.1",
49
49
  "@typescript-eslint/eslint-plugin": "^8.48.1",
50
50
  "@typescript-eslint/parser": "^8.48.1",
@@ -56,7 +56,7 @@
56
56
  "typescript": "^5.9.3"
57
57
  },
58
58
  "peerDependencies": {
59
- "@guidepup/guidepup": ">=0.22.1",
59
+ "@guidepup/guidepup": ">=0.25.0",
60
60
  "@playwright/test": "^1.57.0"
61
61
  },
62
62
  "resolutions": {