@guidepup/playwright 0.16.2 → 0.17.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/lib/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { nvdaTest } from "./nvdaTest";
2
2
  export type { NVDAPlaywright } from "./nvdaTest";
3
3
  export { screenReaderConfig } from "./screenReaderConfig";
4
+ export { screenReaderTest } from "./screenReaderTest";
5
+ export type { ScreenReaderPlaywright } from "./screenReaderTest";
4
6
  export { voiceOverTest } from "./voiceOverTest";
5
7
  export type { VoiceOverPlaywright } from "./voiceOverTest";
package/lib/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.voiceOverTest = exports.screenReaderConfig = exports.nvdaTest = void 0;
3
+ exports.voiceOverTest = exports.screenReaderTest = exports.screenReaderConfig = exports.nvdaTest = void 0;
4
4
  var nvdaTest_1 = require("./nvdaTest");
5
5
  Object.defineProperty(exports, "nvdaTest", { enumerable: true, get: function () { return nvdaTest_1.nvdaTest; } });
6
6
  var screenReaderConfig_1 = require("./screenReaderConfig");
7
7
  Object.defineProperty(exports, "screenReaderConfig", { enumerable: true, get: function () { return screenReaderConfig_1.screenReaderConfig; } });
8
+ var screenReaderTest_1 = require("./screenReaderTest");
9
+ Object.defineProperty(exports, "screenReaderTest", { enumerable: true, get: function () { return screenReaderTest_1.screenReaderTest; } });
8
10
  var voiceOverTest_1 = require("./voiceOverTest");
9
11
  Object.defineProperty(exports, "voiceOverTest", { enumerable: true, get: function () { return voiceOverTest_1.voiceOverTest; } });
package/lib/nvdaTest.js CHANGED
@@ -14,9 +14,19 @@ const MOVE_TO_TOP = {
14
14
  keyCode: [guidepup_1.WindowsKeyCodes.Home],
15
15
  modifiers: [guidepup_1.WindowsModifiers.Control],
16
16
  };
17
+ const cleanString = (str) => str
18
+ .toLowerCase()
19
+ // REF: https://github.com/nvaccess/nvda/blob/master/source/locale/en/symbols.dic
20
+ .replace(/[|¦:;'"`\-‐–—·_()[\]{}\\^~]/g, " ")
21
+ .replace(/\s+/g, " ")
22
+ .trim();
17
23
  const hasFocus = ({ applicationName, pageTitle, windowTitle, }) => {
18
- return ((pageTitle.length && windowTitle.startsWith(pageTitle)) ||
19
- windowTitle.includes(applicationName));
24
+ const cleanedApplicationName = cleanString(applicationName);
25
+ const cleanedPageTitle = cleanString(pageTitle);
26
+ const cleanedWindowTitle = cleanString(windowTitle);
27
+ return ((cleanedPageTitle.length &&
28
+ cleanedWindowTitle.startsWith(cleanedPageTitle)) ||
29
+ cleanedWindowTitle.includes(cleanedApplicationName));
20
30
  };
21
31
  const focusBrowser = async ({ applicationName, pageTitle, }) => {
22
32
  await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.reportTitle);
@@ -49,14 +59,15 @@ exports.nvdaTest = test_1.test.extend({
49
59
  if (!applicationName) {
50
60
  throw new Error(`Browser ${browserName} is not installed.`);
51
61
  }
52
- await page.goto("about:blank", { waitUntil: "load" });
53
- await page.bringToFront();
54
62
  nvdaPlaywright.navigateToWebContent = async (clearLogs = true) => {
55
63
  // Make sure NVDA is not in focus mode.
56
64
  await nvdaPlaywright.perform(nvdaPlaywright.keyboardCommands.exitFocusMode);
57
65
  const pageTitle = await page.title();
58
66
  // Ensure application is brought to front and focused.
59
- await focusBrowser({ applicationName, pageTitle });
67
+ await focusBrowser({
68
+ applicationName,
69
+ pageTitle,
70
+ });
60
71
  // Ensure the document is ready and focused.
61
72
  await page.bringToFront();
62
73
  await page.locator("body").waitFor();
@@ -0,0 +1,78 @@
1
+ import { CommandOptions, type ScreenReader } from "@guidepup/guidepup";
2
+ type Prettify<T> = {
3
+ [K in keyof T]: T[K];
4
+ } & {};
5
+ type CaptureCommandOptions = Prettify<Pick<CommandOptions, "capture">>;
6
+ /**
7
+ * This object can be used to launch and control the default screen reader for
8
+ * the environment.
9
+ *
10
+ * Here's a typical example:
11
+ *
12
+ * ```ts
13
+ * import { screenReader } from "@guidepup/guidepup";
14
+ *
15
+ * (async () => {
16
+ * // Start the screen reader.
17
+ * await screenReader.start();
18
+ *
19
+ * // Move to the next item.
20
+ * await screenReader.next();
21
+ *
22
+ * // ... perform some commands.
23
+ *
24
+ * // Stop the screen reader.
25
+ * await screenReader.stop();
26
+ * })();
27
+ * ```
28
+ */
29
+ export interface ScreenReaderPlaywright extends ScreenReader {
30
+ /**
31
+ * Guidepup Playwright specific command that navigates the screen reader to
32
+ * the beginning of the browser's web content.
33
+ *
34
+ * This command should be used after page navigation.
35
+ *
36
+ * Note: this command clears all logs by default.
37
+ */
38
+ navigateToWebContent(clearLogs?: boolean): Promise<void>;
39
+ }
40
+ /**
41
+ * These tests extend the default Playwright environment that launches the
42
+ * browser with a running instance of the default screen reader for the current
43
+ * OS.
44
+ *
45
+ * A fresh started screen reader instance `screenReader` is provided to each
46
+ * test.
47
+ */
48
+ export declare const screenReaderTest: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & {
49
+ /**
50
+ * This object can be used to launch and control the default screen reader for
51
+ * the environment.
52
+ *
53
+ * Here's a typical example:
54
+ *
55
+ * ```ts
56
+ * import { screenReader } from "@guidepup/guidepup";
57
+ *
58
+ * (async () => {
59
+ * // Start the screen reader.
60
+ * await screenReader.start();
61
+ *
62
+ * // Move to the next item.
63
+ * await screenReader.next();
64
+ *
65
+ * // ... perform some commands.
66
+ *
67
+ * // Stop the screen reader.
68
+ * await screenReader.stop();
69
+ * })();
70
+ * ```
71
+ */
72
+ screenReader: ScreenReaderPlaywright;
73
+ /**
74
+ * Options to start the default screen reader with.
75
+ */
76
+ screenReaderStartOptions: CaptureCommandOptions;
77
+ }, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
78
+ export {};
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.screenReaderTest = void 0;
4
+ const test_1 = require("@playwright/test");
5
+ const guidepup_1 = require("@guidepup/guidepup");
6
+ const applicationNameMap_1 = require("./applicationNameMap");
7
+ const MAX_APPLICATION_SWITCH_RETRY_COUNT = 10;
8
+ const SWITCH_APPLICATION = {
9
+ keyCode: [guidepup_1.WindowsKeyCodes.Escape],
10
+ modifiers: [guidepup_1.WindowsModifiers.Alt],
11
+ };
12
+ const MOVE_TO_TOP = {
13
+ keyCode: [guidepup_1.WindowsKeyCodes.Home],
14
+ modifiers: [guidepup_1.WindowsModifiers.Control],
15
+ };
16
+ const cleanString = (str) => str
17
+ .toLowerCase()
18
+ // REF: https://github.com/nvaccess/nvda/blob/master/source/locale/en/symbols.dic
19
+ .replace(/[|¦:;'"`\-‐–—·_()[\]{}\\^~]/g, " ")
20
+ .replace(/\s+/g, " ")
21
+ .trim();
22
+ const hasFocus = ({ applicationName, pageTitle, windowTitle, }) => {
23
+ const cleanedApplicationName = cleanString(applicationName);
24
+ const cleanedPageTitle = cleanString(pageTitle);
25
+ const cleanedWindowTitle = cleanString(windowTitle);
26
+ return ((cleanedPageTitle.length &&
27
+ cleanedWindowTitle.startsWith(cleanedPageTitle)) ||
28
+ cleanedWindowTitle.includes(cleanedApplicationName));
29
+ };
30
+ const focusBrowser = async ({ applicationName, pageTitle, }) => {
31
+ await screenReaderPlaywright.perform(guidepup_1.NVDAKeyCodeCommands.reportTitle);
32
+ let windowTitle = await screenReaderPlaywright.lastSpokenPhrase();
33
+ if (hasFocus({ applicationName, pageTitle, windowTitle })) {
34
+ return;
35
+ }
36
+ let applicationSwitchRetryCount = 0;
37
+ while (applicationSwitchRetryCount < MAX_APPLICATION_SWITCH_RETRY_COUNT) {
38
+ applicationSwitchRetryCount++;
39
+ await screenReaderPlaywright.perform(SWITCH_APPLICATION);
40
+ await screenReaderPlaywright.perform(guidepup_1.NVDAKeyCodeCommands.reportTitle);
41
+ windowTitle = await screenReaderPlaywright.lastSpokenPhrase();
42
+ if (hasFocus({ applicationName, pageTitle, windowTitle })) {
43
+ break;
44
+ }
45
+ }
46
+ };
47
+ const screenReaderPlaywright = guidepup_1.screenReader;
48
+ /**
49
+ * These tests extend the default Playwright environment that launches the
50
+ * browser with a running instance of the default screen reader for the current
51
+ * OS.
52
+ *
53
+ * A fresh started screen reader instance `screenReader` is provided to each
54
+ * test.
55
+ */
56
+ exports.screenReaderTest = test_1.test.extend({
57
+ screenReaderStartOptions: { capture: "initial" },
58
+ screenReader: async ({ browserName, page, screenReaderStartOptions }, use) => {
59
+ try {
60
+ const applicationName = applicationNameMap_1.applicationNameMap[browserName];
61
+ if (!applicationName) {
62
+ throw new Error(`Browser ${browserName} is not installed.`);
63
+ }
64
+ if (guidepup_1.nvda.default()) {
65
+ screenReaderPlaywright.navigateToWebContent = async (clearLogs = true) => {
66
+ // Make sure NVDA is not in focus mode.
67
+ await screenReaderPlaywright.perform(guidepup_1.NVDAKeyCodeCommands.exitFocusMode);
68
+ const pageTitle = await page.title();
69
+ // Ensure application is brought to front and focused.
70
+ await focusBrowser({ applicationName, pageTitle });
71
+ // Ensure the document is ready and focused.
72
+ await page.bringToFront();
73
+ await page.locator("body").waitFor();
74
+ await page.locator("body").focus();
75
+ await page.locator("body").click();
76
+ await page.locator("body").blur();
77
+ // NVDA appears to not work well with Firefox when switching between
78
+ // applications resulting in the entire browser window having NVDA focus
79
+ // with focus mode.
80
+ //
81
+ // One workaround is to tab to the next focusable item. From there we can
82
+ // toggle into (yes although we are already in it...) focus mode and back
83
+ // out. In case this ever transpires to not happen as expect, we then ensure
84
+ // we exit focus mode and move NVDA to the top of the page.
85
+ //
86
+ // REF: https://github.com/nvaccess/nvda/issues/5758
87
+ await screenReaderPlaywright.perform(guidepup_1.NVDAKeyCodeCommands.readNextFocusableItem);
88
+ await screenReaderPlaywright.perform(guidepup_1.NVDAKeyCodeCommands.toggleBetweenBrowseAndFocusMode);
89
+ await screenReaderPlaywright.perform(guidepup_1.NVDAKeyCodeCommands.toggleBetweenBrowseAndFocusMode);
90
+ await screenReaderPlaywright.perform(guidepup_1.NVDAKeyCodeCommands.exitFocusMode);
91
+ await screenReaderPlaywright.perform(MOVE_TO_TOP);
92
+ if (clearLogs) {
93
+ // Clear out logs.
94
+ await screenReaderPlaywright.clearItemTextLog();
95
+ await screenReaderPlaywright.clearSpokenPhraseLog();
96
+ }
97
+ };
98
+ }
99
+ else if (guidepup_1.voiceOver.default()) {
100
+ screenReaderPlaywright.navigateToWebContent = async (clearLogs = true) => {
101
+ await (0, guidepup_1.macOSActivate)(applicationName);
102
+ await screenReaderPlaywright.perform({
103
+ keyCode: guidepup_1.MacOSKeyCodes.Control,
104
+ });
105
+ await page.bringToFront();
106
+ await page.locator("body").waitFor();
107
+ await screenReaderPlaywright.perform(guidepup_1.voiceOverKeyCodeCommands.openItemChooser);
108
+ await screenReaderPlaywright.type("web content");
109
+ await screenReaderPlaywright.perform({
110
+ keyCode: guidepup_1.MacOSKeyCodes.Enter,
111
+ });
112
+ await screenReaderPlaywright.interact();
113
+ await screenReaderPlaywright.perform(guidepup_1.voiceOverKeyCodeCommands.moveToBeginningOfText);
114
+ await screenReaderPlaywright.perform({
115
+ keyCode: guidepup_1.MacOSKeyCodes.Control,
116
+ });
117
+ if (clearLogs) {
118
+ await screenReaderPlaywright.clearItemTextLog();
119
+ await screenReaderPlaywright.clearSpokenPhraseLog();
120
+ }
121
+ };
122
+ }
123
+ else {
124
+ throw new Error("No supported screen reader");
125
+ }
126
+ await screenReaderPlaywright.start(screenReaderStartOptions);
127
+ await use(screenReaderPlaywright);
128
+ }
129
+ finally {
130
+ try {
131
+ await screenReaderPlaywright.stop();
132
+ }
133
+ catch {
134
+ // swallow stop failure
135
+ }
136
+ }
137
+ },
138
+ });
@@ -47,9 +47,6 @@ exports.voiceOverTest = test_1.test.extend({
47
47
  }
48
48
  };
49
49
  await voiceOverPlaywright.start(voiceOverStartOptions);
50
- await (0, guidepup_1.macOSActivate)(applicationName);
51
- // Cancel auto navigation
52
- await voiceOverPlaywright.perform({ keyCode: guidepup_1.MacOSKeyCodes.Control }, { capture: false });
53
50
  await use(voiceOverPlaywright);
54
51
  }
55
52
  finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guidepup/playwright",
3
- "version": "0.16.2",
3
+ "version": "0.17.0",
4
4
  "description": "Screen reader automation library for Playwright testing. ",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -34,15 +34,17 @@
34
34
  "prepublish": "yarn build",
35
35
  "pretest": "npx playwright install chromium firefox webkit",
36
36
  "test:nvda": "yarn test:nvda:chromium && yarn test:nvda:firefox",
37
+ "test:screenreader": "yarn test:screenreader:chromium",
37
38
  "test:voiceover": "yarn test:voiceover:chromium && yarn test:voiceover:firefox && yarn test:voiceover:webkit",
38
39
  "test:nvda:chromium": "playwright test --config ./examples/playwright-nvda/chromium.config.ts ./examples/playwright-nvda/tests/chromium/",
39
40
  "test:nvda:firefox": "playwright test --config ./examples/playwright-nvda/firefox.config.ts ./examples/playwright-nvda/tests/firefox/",
41
+ "test:screenreader:chromium": "playwright test --config ./examples/playwright-screenreader/chromium.config.ts ./examples/playwright-screenreader/tests/chromium/",
40
42
  "test:voiceover:chromium": "playwright test --config ./examples/playwright-voiceover/chromium.config.ts ./examples/playwright-voiceover/tests/chromium/",
41
43
  "test:voiceover:firefox": "playwright test --config ./examples/playwright-voiceover/firefox.config.ts ./examples/playwright-voiceover/tests/firefox/",
42
44
  "test:voiceover:webkit": "playwright test --config ./examples/playwright-voiceover/webkit.config.ts ./examples/playwright-voiceover/tests/webkit/"
43
45
  },
44
46
  "devDependencies": {
45
- "@guidepup/guidepup": "^0.25.0",
47
+ "@guidepup/guidepup": "^0.27.0",
46
48
  "@guidepup/record": "^0.1.0",
47
49
  "@playwright/test": "^1.60.0",
48
50
  "@types/node": "^24.10.1",
@@ -56,7 +58,7 @@
56
58
  "typescript": "^5.9.3"
57
59
  },
58
60
  "peerDependencies": {
59
- "@guidepup/guidepup": ">=0.25.0",
61
+ "@guidepup/guidepup": ">=0.27.0",
60
62
  "@playwright/test": "^1.57.0"
61
63
  },
62
64
  "resolutions": {