mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore(aria): do not generate refs for pointer-events none (#35819)
This commit is contained in:
parent
cb99e260fa
commit
eda5a9efeb
@ -29,6 +29,7 @@ export type AriaNode = AriaProps & {
|
|||||||
children: (AriaNode | string)[];
|
children: (AriaNode | string)[];
|
||||||
element: Element;
|
element: Element;
|
||||||
box: Box;
|
box: Box;
|
||||||
|
receivesPointerEvents: boolean;
|
||||||
props: Record<string, string>;
|
props: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ export function generateAriaTree(rootElement: Element, generation: number, optio
|
|||||||
const visited = new Set<Node>();
|
const visited = new Set<Node>();
|
||||||
|
|
||||||
const snapshot: AriaSnapshot = {
|
const snapshot: AriaSnapshot = {
|
||||||
root: { role: 'fragment', name: '', children: [], element: rootElement, props: {}, box: box(rootElement) },
|
root: { role: 'fragment', name: '', children: [], element: rootElement, props: {}, box: box(rootElement), receivesPointerEvents: true },
|
||||||
elements: new Map<number, Element>(),
|
elements: new Map<number, Element>(),
|
||||||
generation,
|
generation,
|
||||||
ids: new Map<Element, number>(),
|
ids: new Map<Element, number>(),
|
||||||
@ -148,7 +149,7 @@ export function generateAriaTree(rootElement: Element, generation: number, optio
|
|||||||
|
|
||||||
function toAriaNode(element: Element, options?: { emitGeneric?: boolean }): AriaNode | null {
|
function toAriaNode(element: Element, options?: { emitGeneric?: boolean }): AriaNode | null {
|
||||||
if (element.nodeName === 'IFRAME')
|
if (element.nodeName === 'IFRAME')
|
||||||
return { role: 'iframe', name: '', children: [], props: {}, element, box: box(element) };
|
return { role: 'iframe', name: '', children: [], props: {}, element, box: box(element), receivesPointerEvents: true };
|
||||||
|
|
||||||
const defaultRole = options?.emitGeneric ? 'generic' : null;
|
const defaultRole = options?.emitGeneric ? 'generic' : null;
|
||||||
const role = roleUtils.getAriaRole(element) ?? defaultRole;
|
const role = roleUtils.getAriaRole(element) ?? defaultRole;
|
||||||
@ -156,7 +157,8 @@ function toAriaNode(element: Element, options?: { emitGeneric?: boolean }): Aria
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
const name = normalizeWhiteSpace(roleUtils.getElementAccessibleName(element, false) || '');
|
const name = normalizeWhiteSpace(roleUtils.getElementAccessibleName(element, false) || '');
|
||||||
const result: AriaNode = { role, name, children: [], props: {}, element, box: box(element) };
|
const receivesPointerEvents = roleUtils.receivesPointerEvents(element);
|
||||||
|
const result: AriaNode = { role, name, children: [], props: {}, element, box: box(element), receivesPointerEvents };
|
||||||
|
|
||||||
if (roleUtils.kAriaCheckedRoles.includes(role))
|
if (roleUtils.kAriaCheckedRoles.includes(role))
|
||||||
result.checked = roleUtils.getAriaChecked(element);
|
result.checked = roleUtils.getAriaChecked(element);
|
||||||
@ -406,7 +408,7 @@ export function renderAriaTree(ariaSnapshot: AriaSnapshot, options?: { mode?: 'r
|
|||||||
key += ` [pressed]`;
|
key += ` [pressed]`;
|
||||||
if (ariaNode.selected === true)
|
if (ariaNode.selected === true)
|
||||||
key += ` [selected]`;
|
key += ` [selected]`;
|
||||||
if (options?.ref && ariaNode.box.visible) {
|
if (options?.ref && ariaNode.box.visible && ariaNode.receivesPointerEvents) {
|
||||||
const id = ariaSnapshot.ids.get(ariaNode.element);
|
const id = ariaSnapshot.ids.get(ariaNode.element);
|
||||||
if (id)
|
if (id)
|
||||||
key += ` [ref=s${ariaSnapshot.generation}e${id}]`;
|
key += ` [ref=s${ariaSnapshot.generation}e${id}]`;
|
||||||
|
@ -1051,6 +1051,40 @@ function getAccessibleNameFromAssociatedLabels(labels: Iterable<HTMLLabelElement
|
|||||||
})).filter(accessibleName => !!accessibleName).join(' ');
|
})).filter(accessibleName => !!accessibleName).join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function receivesPointerEvents(element: Element): boolean {
|
||||||
|
const cache = cachePointerEvents!;
|
||||||
|
let e: Element | undefined = element;
|
||||||
|
let result: boolean | undefined;
|
||||||
|
const parents: Element[] = [];
|
||||||
|
for (; e; e = parentElementOrShadowHost(e!)) {
|
||||||
|
const cached = cache.get(e);
|
||||||
|
if (cached !== undefined) {
|
||||||
|
result = cached;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
parents.push(e);
|
||||||
|
const style = getElementComputedStyle(e);
|
||||||
|
if (!style) {
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = style.pointerEvents;
|
||||||
|
if (value) {
|
||||||
|
result = value !== 'none';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result === undefined)
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
for (const parent of parents)
|
||||||
|
cache.set(parent, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
let cacheAccessibleName: Map<Element, string> | undefined;
|
let cacheAccessibleName: Map<Element, string> | undefined;
|
||||||
let cacheAccessibleNameHidden: Map<Element, string> | undefined;
|
let cacheAccessibleNameHidden: Map<Element, string> | undefined;
|
||||||
let cacheAccessibleDescription: Map<Element, string> | undefined;
|
let cacheAccessibleDescription: Map<Element, string> | undefined;
|
||||||
@ -1059,6 +1093,7 @@ let cacheAccessibleErrorMessage: Map<Element, string> | undefined;
|
|||||||
let cacheIsHidden: Map<Element, boolean> | undefined;
|
let cacheIsHidden: Map<Element, boolean> | undefined;
|
||||||
let cachePseudoContentBefore: Map<Element, string> | undefined;
|
let cachePseudoContentBefore: Map<Element, string> | undefined;
|
||||||
let cachePseudoContentAfter: Map<Element, string> | undefined;
|
let cachePseudoContentAfter: Map<Element, string> | undefined;
|
||||||
|
let cachePointerEvents: Map<Element, boolean> | undefined;
|
||||||
let cachesCounter = 0;
|
let cachesCounter = 0;
|
||||||
|
|
||||||
export function beginAriaCaches() {
|
export function beginAriaCaches() {
|
||||||
@ -1071,6 +1106,7 @@ export function beginAriaCaches() {
|
|||||||
cacheIsHidden ??= new Map();
|
cacheIsHidden ??= new Map();
|
||||||
cachePseudoContentBefore ??= new Map();
|
cachePseudoContentBefore ??= new Map();
|
||||||
cachePseudoContentAfter ??= new Map();
|
cachePseudoContentAfter ??= new Map();
|
||||||
|
cachePointerEvents ??= new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function endAriaCaches() {
|
export function endAriaCaches() {
|
||||||
@ -1083,6 +1119,7 @@ export function endAriaCaches() {
|
|||||||
cacheIsHidden = undefined;
|
cacheIsHidden = undefined;
|
||||||
cachePseudoContentBefore = undefined;
|
cachePseudoContentBefore = undefined;
|
||||||
cachePseudoContentAfter = undefined;
|
cachePseudoContentAfter = undefined;
|
||||||
|
cachePointerEvents = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -731,7 +731,7 @@ it('ref mode can be used to stitch all frame snapshots', async ({ page, server }
|
|||||||
`.trim());
|
`.trim());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not include hidden input elements', async ({ page }) => {
|
it('should not generate refs for hidden elements', async ({ page }) => {
|
||||||
await page.setContent(`
|
await page.setContent(`
|
||||||
<button>One</button>
|
<button>One</button>
|
||||||
<button style="width: 0; height: 0; appearance: none; border: 0; padding: 0;">Two</button>
|
<button style="width: 0; height: 0; appearance: none; border: 0; padding: 0;">Two</button>
|
||||||
@ -744,6 +744,37 @@ it('should not include hidden input elements', async ({ page }) => {
|
|||||||
- button "Three" [ref=s1e5]`);
|
- button "Three" [ref=s1e5]`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not generate refs for elements with pointer-events:none', async ({ page }) => {
|
||||||
|
await page.setContent(`
|
||||||
|
<button style="pointer-events: none">no-ref</button>
|
||||||
|
<div style="pointer-events: none">
|
||||||
|
<button style="pointer-events: auto">with-ref</button>
|
||||||
|
</div>
|
||||||
|
<div style="pointer-events: none">
|
||||||
|
<div style="pointer-events: initial">
|
||||||
|
<button>with-ref</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="pointer-events: none">
|
||||||
|
<div style="pointer-events: auto">
|
||||||
|
<button>with-ref</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="pointer-events: auto">
|
||||||
|
<div style="pointer-events: none">
|
||||||
|
<button>no-ref</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
const snapshot = await page.locator('body').ariaSnapshot({ ref: true });
|
||||||
|
expect(snapshot).toContain(`- button "no-ref"
|
||||||
|
- button "with-ref" [ref=s1e5]
|
||||||
|
- button "with-ref" [ref=s1e8]
|
||||||
|
- button "with-ref" [ref=s1e11]
|
||||||
|
- button "no-ref"`);
|
||||||
|
});
|
||||||
|
|
||||||
it('emit generic roles for nodes w/o roles', async ({ page }) => {
|
it('emit generic roles for nodes w/o roles', async ({ page }) => {
|
||||||
await page.setContent(`
|
await page.setContent(`
|
||||||
<style>
|
<style>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user