chore(snapshot): support aria-owns (#33404)
This commit is contained in:
parent
3cd753f6bb
commit
1d4650cea2
|
@ -50,7 +50,12 @@ export type AriaTemplateRoleNode = AriaProps & {
|
||||||
export type AriaTemplateNode = AriaTemplateRoleNode | AriaTemplateTextNode;
|
export type AriaTemplateNode = AriaTemplateRoleNode | AriaTemplateTextNode;
|
||||||
|
|
||||||
export function generateAriaTree(rootElement: Element): AriaNode {
|
export function generateAriaTree(rootElement: Element): AriaNode {
|
||||||
|
const visited = new Set<Node>();
|
||||||
const visit = (ariaNode: AriaNode, node: Node) => {
|
const visit = (ariaNode: AriaNode, node: Node) => {
|
||||||
|
if (visited.has(node))
|
||||||
|
return;
|
||||||
|
visited.add(node);
|
||||||
|
|
||||||
if (node.nodeType === Node.TEXT_NODE && node.nodeValue) {
|
if (node.nodeType === Node.TEXT_NODE && node.nodeValue) {
|
||||||
const text = node.nodeValue;
|
const text = node.nodeValue;
|
||||||
if (text)
|
if (text)
|
||||||
|
@ -65,13 +70,23 @@ export function generateAriaTree(rootElement: Element): AriaNode {
|
||||||
if (roleUtils.isElementHiddenForAria(element))
|
if (roleUtils.isElementHiddenForAria(element))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const ariaChildren: Element[] = [];
|
||||||
|
if (element.hasAttribute('aria-owns')) {
|
||||||
|
const ids = element.getAttribute('aria-owns')!.split(/\s+/);
|
||||||
|
for (const id of ids) {
|
||||||
|
const ownedElement = rootElement.ownerDocument.getElementById(id);
|
||||||
|
if (ownedElement)
|
||||||
|
ariaChildren.push(ownedElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const childAriaNode = toAriaNode(element);
|
const childAriaNode = toAriaNode(element);
|
||||||
if (childAriaNode)
|
if (childAriaNode)
|
||||||
ariaNode.children.push(childAriaNode);
|
ariaNode.children.push(childAriaNode);
|
||||||
processChildNodes(childAriaNode || ariaNode, element);
|
processElement(childAriaNode || ariaNode, element, ariaChildren);
|
||||||
};
|
};
|
||||||
|
|
||||||
function processChildNodes(ariaNode: AriaNode, element: Element) {
|
function processElement(ariaNode: AriaNode, element: Element, ariaChildren: Element[] = []) {
|
||||||
// Surround every element with spaces for the sake of concatenated text nodes.
|
// Surround every element with spaces for the sake of concatenated text nodes.
|
||||||
const display = getElementComputedStyle(element)?.display || 'inline';
|
const display = getElementComputedStyle(element)?.display || 'inline';
|
||||||
const treatAsBlock = (display !== 'inline' || element.nodeName === 'BR') ? ' ' : '';
|
const treatAsBlock = (display !== 'inline' || element.nodeName === 'BR') ? ' ' : '';
|
||||||
|
@ -94,6 +109,9 @@ export function generateAriaTree(rootElement: Element): AriaNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const child of ariaChildren)
|
||||||
|
visit(ariaNode, child);
|
||||||
|
|
||||||
ariaNode.children.push(roleUtils.getPseudoContent(element, '::after'));
|
ariaNode.children.push(roleUtils.getPseudoContent(element, '::after'));
|
||||||
|
|
||||||
if (treatAsBlock)
|
if (treatAsBlock)
|
||||||
|
|
|
@ -421,3 +421,41 @@ it('should treat input value as text in templates', async ({ page }) => {
|
||||||
- textbox: hello world
|
- textbox: hello world
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should respect aria-owns', async ({ page }) => {
|
||||||
|
await page.setContent(`
|
||||||
|
<a href='about:blank' aria-owns='input p'>
|
||||||
|
<div role='region'>Link 1</div>
|
||||||
|
</a>
|
||||||
|
<a href='about:blank' aria-owns='input p'>
|
||||||
|
<div role='region'>Link 2</div>
|
||||||
|
</a>
|
||||||
|
<input id='input' value='Value'>
|
||||||
|
<p id='p'>Paragraph</p>
|
||||||
|
`);
|
||||||
|
|
||||||
|
// - Different from Chrome DevTools which attributes ownership to the last element.
|
||||||
|
// - CDT also does not include non-owned children in accessible name.
|
||||||
|
// - Disregarding these as aria-owns can't suggest multiple parts by spec.
|
||||||
|
await checkAndMatchSnapshot(page.locator('body'), `
|
||||||
|
- link "Link 1 Value Paragraph":
|
||||||
|
- region: Link 1
|
||||||
|
- textbox: Value
|
||||||
|
- paragraph: Paragraph
|
||||||
|
- link "Link 2 Value Paragraph":
|
||||||
|
- region: Link 2
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be ok with circular ownership', async ({ page }) => {
|
||||||
|
await page.setContent(`
|
||||||
|
<a href='about:blank' id='parent'>
|
||||||
|
<div role='region' aria-owns='parent'>Hello</div>
|
||||||
|
</a>
|
||||||
|
`);
|
||||||
|
|
||||||
|
await checkAndMatchSnapshot(page.locator('body'), `
|
||||||
|
- link "Hello":
|
||||||
|
- region: Hello
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue