From 9d187fc09a49cf8a6705ca4fbb0dd927b9a177c1 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Wed, 12 Jun 2024 17:13:17 +0200 Subject: [PATCH] feat: include iframes into the a11y snapshot --- .../puppeteer-core/src/cdp/Accessibility.ts | 35 +++++++++++++++++++ test/src/accessibility.spec.ts | 19 ++++++++++ 2 files changed, 54 insertions(+) diff --git a/packages/puppeteer-core/src/cdp/Accessibility.ts b/packages/puppeteer-core/src/cdp/Accessibility.ts index d79a5707669..65b213f1c5f 100644 --- a/packages/puppeteer-core/src/cdp/Accessibility.ts +++ b/packages/puppeteer-core/src/cdp/Accessibility.ts @@ -195,6 +195,34 @@ export class Accessibility { backendNodeId = node.backendNodeId; } const defaultRoot = AXNode.createTree(this.#realm, nodes); + + const resolveIframes = async (root: AXNode): Promise => { + if (root.payload.role?.value === 'Iframe') { + if (!root.payload.backendDOMNodeId) { + return; + } + console.log(root.payload) + using handle = await this.#realm.adoptBackendNode( + root.payload.backendDOMNodeId + ) as ElementHandle; + if (!handle || !('contentFrame' in handle)) { + return; + } + const frame = await handle.contentFrame(); + if (!frame) { + return; + } + const iframeSnapshot = await frame.accessibility.snapshot(options); + root.iframeSnapshot = iframeSnapshot ?? undefined; + console.log('iframe', root.iframeSnapshot) + } + for (const child of root.children) { + await resolveIframes(child); + } + } + + await resolveIframes(defaultRoot); + let needle: AXNode | null = defaultRoot; if (backendNodeId) { needle = defaultRoot.find(node => { @@ -233,6 +261,12 @@ export class Accessibility { if (children.length) { serializedNode.children = children; } + if (node.iframeSnapshot) { + if (!serializedNode.children) { + serializedNode.children = []; + } + serializedNode.children.push(node.iframeSnapshot); + } return [serializedNode]; } @@ -257,6 +291,7 @@ export class Accessibility { class AXNode { public payload: Protocol.Accessibility.AXNode; public children: AXNode[] = []; + public iframeSnapshot?: SerializedAXNode; #richlyEditable = false; #editable = false; diff --git a/test/src/accessibility.spec.ts b/test/src/accessibility.spec.ts index a2547a5023d..359c20c43fe 100644 --- a/test/src/accessibility.spec.ts +++ b/test/src/accessibility.spec.ts @@ -10,6 +10,7 @@ import expect from 'expect'; import type {SerializedAXNode} from 'puppeteer-core/internal/cdp/Accessibility.js'; import {getTestState, setupTestBrowserHooks} from './mocha-utils.js'; +import { attachFrame } from './utils.js'; describe('Accessibility', function () { setupTestBrowserHooks(); @@ -242,6 +243,24 @@ describe('Accessibility', function () { assert(snapshot.children[0]); expect(snapshot.children[0]!.multiselectable).toEqual(true); }); + + it.only('iframes', async () => { + const {page, server} = await getTestState(); + + await attachFrame(page, 'frame1', server.EMPTY_PAGE); + const frame1 = page.frames()[1]; + await frame1!.evaluate(() => { + const button = document.createElement('button'); + button.innerText = 'value1'; + document.body.appendChild(button) + }); + const snapshot = await page.accessibility.snapshot(); + console.log(JSON.stringify(snapshot, null, 2)) + expect(snapshot).toMatchObject({ + + }); + }); + it('keyshortcuts', async () => { const {page} = await getTestState();