From f1085d2a2f8b5516682add13520f1d47e431711c Mon Sep 17 00:00:00 2001 From: Clay Miller Date: Fri, 29 May 2026 15:12:15 -0400 Subject: [PATCH 1/7] Force ariaNotify polyfill in tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- arianotify-polyfill.js | 68 +++++++++++++++++++----------- package.json | 4 +- playwright.config.mjs | 7 ++- tests/force-arianotify-polyfill.js | 15 +++++++ tests/guidepup/nvda.spec.mjs | 7 +++ tests/guidepup/voiceover.spec.mjs | 7 +++ web-test-runner.config.js | 5 ++- 7 files changed, 82 insertions(+), 31 deletions(-) create mode 100644 tests/force-arianotify-polyfill.js diff --git a/arianotify-polyfill.js b/arianotify-polyfill.js index aeb7fd7..5115ef7 100644 --- a/arianotify-polyfill.js +++ b/arianotify-polyfill.js @@ -3,10 +3,16 @@ const domAPIsAreAvailable = typeof globalThis.Element !== "undefined" && typeof globalThis.Document !== "undefined"; +const shouldForceAriaNotifyPolyfill = + /** @type {typeof globalThis & {__ariaNotifyPolyfillForce?: boolean}} */ ( + globalThis + ).__ariaNotifyPolyfillForce === true; if ( domAPIsAreAvailable && - (!("ariaNotify" in Element.prototype) || !("ariaNotify" in Document.prototype)) + (shouldForceAriaNotifyPolyfill || + !("ariaNotify" in Element.prototype) || + !("ariaNotify" in Document.prototype)) ) { /** @type {string} */ let uniqueId = `${Date.now()}`; @@ -200,31 +206,43 @@ if ( AssertiveLiveRegionCustomElement ); - if (!("ariaNotify" in Element.prototype)) { - /** - * @param {string} message - * @param {object} options - * @param {"high" | "normal"} [options.priority] - */ - Element.prototype["ariaNotify"] = function ( - message, - { priority = "normal" } = {} - ) { - queue.enqueue(new Message({ element: this, message, priority })); - }; + /** + * @param {string} message + * @param {object} options + * @param {"high" | "normal"} [options.priority] + */ + const elementAriaNotify = function ( + message, + { priority = "normal" } = {} + ) { + queue.enqueue(new Message({ element: this, message, priority })); + }; + + if (shouldForceAriaNotifyPolyfill || !("ariaNotify" in Element.prototype)) { + Object.defineProperty(Element.prototype, "ariaNotify", { + configurable: true, + writable: true, + value: elementAriaNotify, + }); } - if (!("ariaNotify" in Document.prototype)) { - /** - * @param {string} message - * @param {object} options - * @param {"high" | "normal"} [options.priority] - */ - Document.prototype["ariaNotify"] = function ( - message, - { priority = "normal" } = {} - ) { - queue.enqueue(new Message({ element: this.documentElement, message, priority })); - }; + /** + * @param {string} message + * @param {object} options + * @param {"high" | "normal"} [options.priority] + */ + const documentAriaNotify = function ( + message, + { priority = "normal" } = {} + ) { + queue.enqueue(new Message({ element: this.documentElement, message, priority })); + }; + + if (shouldForceAriaNotifyPolyfill || !("ariaNotify" in Document.prototype)) { + Object.defineProperty(Document.prototype, "ariaNotify", { + configurable: true, + writable: true, + value: documentAriaNotify, + }); } } diff --git a/package.json b/package.json index 871a2e7..05873bd 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ }, "files": [], "scripts": { - "test": "npx playwright install firefox && web-test-runner", - "test:guidepup": "npx playwright install firefox && npx playwright test" + "test": "web-test-runner", + "test:guidepup": "npx playwright test" }, "devDependencies": { "@esm-bundle/chai": "^4.3.4-fix.0", diff --git a/playwright.config.mjs b/playwright.config.mjs index 506e134..283d636 100644 --- a/playwright.config.mjs +++ b/playwright.config.mjs @@ -9,8 +9,11 @@ const config = { retries: 0, projects: [ { - name: 'firefox', // Use Firefox because Firefox doesn’t have a native implementation of 'ariaNotify' (as of 2026-01-15), so we can test the polyfill in it. - use: devices['Desktop Firefox'], + name: "Microsoft Edge", + use: { + ...devices["Desktop Edge"], + channel: "msedge", + }, }, ], quiet: false, diff --git a/tests/force-arianotify-polyfill.js b/tests/force-arianotify-polyfill.js new file mode 100644 index 0000000..b4f33b5 --- /dev/null +++ b/tests/force-arianotify-polyfill.js @@ -0,0 +1,15 @@ +// @ts-check + +for (const prototype of [Element.prototype, Document.prototype]) { + Object.defineProperty(prototype, "ariaNotify", { + configurable: true, + writable: true, + value() { + throw new Error("Expected tests to use the ariaNotify polyfill"); + }, + }); +} + +/** @type {typeof globalThis & {__ariaNotifyPolyfillForce?: boolean}} */ ( + globalThis +).__ariaNotifyPolyfillForce = true; diff --git a/tests/guidepup/nvda.spec.mjs b/tests/guidepup/nvda.spec.mjs index f5b7954..075faef 100644 --- a/tests/guidepup/nvda.spec.mjs +++ b/tests/guidepup/nvda.spec.mjs @@ -25,6 +25,13 @@ import path from "node:path"; const test = baseTest.extend({ context: async ({ context }, run) => { + await context.addInitScript({ + path: path.join( + import.meta.dirname, + "..", + "force-arianotify-polyfill.js" + ), + }); await context.route("**/*", (route, request) => route.fulfill({ path: path.join( diff --git a/tests/guidepup/voiceover.spec.mjs b/tests/guidepup/voiceover.spec.mjs index 42bcaaf..20c768d 100644 --- a/tests/guidepup/voiceover.spec.mjs +++ b/tests/guidepup/voiceover.spec.mjs @@ -9,6 +9,13 @@ import path from "node:path"; const test = baseTest.extend({ context: async ({ context }, run) => { + await context.addInitScript({ + path: path.join( + import.meta.dirname, + "..", + "force-arianotify-polyfill.js" + ), + }); await context.route("**/*", (route, request) => route.fulfill({ path: path.join( diff --git a/web-test-runner.config.js b/web-test-runner.config.js index 4819f27..2cfa37e 100644 --- a/web-test-runner.config.js +++ b/web-test-runner.config.js @@ -6,8 +6,8 @@ export default { nodeResolve: true, browsers: [ playwrightLauncher({ - product: 'firefox', // Use Firefox instead of Chrome (web-test-runner’s default), because Firefox doesn’t have a native implementation of 'ariaNotify' (as of 2025-11-07), so we can test the polyfill in it. - launchOptions: { headless: true } + product: 'chromium', + launchOptions: { channel: "msedge", headless: true } }), ], plugins: [ @@ -18,6 +18,7 @@ export default { return context.body.replace( /<\/body>/, ` + + - + +