mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(role): accessibleName computation should walk the flat dom tree (#19301)
- When visiting `<slot>` element, descend into assigned nodes. - When node has `assignedSlot`, skip it during regular traversal. Fixes #18989.
This commit is contained in:
parent
8660288518
commit
4784dae10f
@ -579,7 +579,9 @@ function getElementAccessibleNameInternal(element: Element, options: AccessibleN
|
|||||||
if (allowsNameFromContent || options.embeddedInLabelledBy !== 'none' || options.embeddedInLabel !== 'none' || options.embeddedInTextAlternativeElement || options.embeddedInTargetElement === 'descendant') {
|
if (allowsNameFromContent || options.embeddedInLabelledBy !== 'none' || options.embeddedInLabel !== 'none' || options.embeddedInTextAlternativeElement || options.embeddedInTargetElement === 'descendant') {
|
||||||
options.visitedElements.add(element);
|
options.visitedElements.add(element);
|
||||||
const tokens: string[] = [];
|
const tokens: string[] = [];
|
||||||
const visit = (node: Node) => {
|
const visit = (node: Node, skipSlotted: boolean) => {
|
||||||
|
if (skipSlotted && (node as Element | Text).assignedSlot)
|
||||||
|
return;
|
||||||
if (node.nodeType === 1 /* Node.ELEMENT_NODE */) {
|
if (node.nodeType === 1 /* Node.ELEMENT_NODE */) {
|
||||||
const display = getComputedStyle(node as Element)?.getPropertyValue('display') || 'inline';
|
const display = getComputedStyle(node as Element)?.getPropertyValue('display') || 'inline';
|
||||||
let token = getElementAccessibleNameInternal(node as Element, childOptions);
|
let token = getElementAccessibleNameInternal(node as Element, childOptions);
|
||||||
@ -596,14 +598,20 @@ function getElementAccessibleNameInternal(element: Element, options: AccessibleN
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
tokens.push(getPseudoContent(getComputedStyle(element, '::before')));
|
tokens.push(getPseudoContent(getComputedStyle(element, '::before')));
|
||||||
for (let child = element.firstChild; child; child = child.nextSibling)
|
const assignedNodes = element.nodeName === 'SLOT' ? (element as HTMLSlotElement).assignedNodes() : [];
|
||||||
visit(child);
|
if (assignedNodes.length) {
|
||||||
if (element.shadowRoot) {
|
for (const child of assignedNodes)
|
||||||
for (let child = element.shadowRoot.firstChild; child; child = child.nextSibling)
|
visit(child, false);
|
||||||
visit(child);
|
} else {
|
||||||
|
for (let child = element.firstChild; child; child = child.nextSibling)
|
||||||
|
visit(child, true);
|
||||||
|
if (element.shadowRoot) {
|
||||||
|
for (let child = element.shadowRoot.firstChild; child; child = child.nextSibling)
|
||||||
|
visit(child, true);
|
||||||
|
}
|
||||||
|
for (const owned of getIdRefs(element, element.getAttribute('aria-owns')))
|
||||||
|
visit(owned, true);
|
||||||
}
|
}
|
||||||
for (const owned of getIdRefs(element, element.getAttribute('aria-owns')))
|
|
||||||
visit(owned);
|
|
||||||
tokens.push(getPseudoContent(getComputedStyle(element, '::after')));
|
tokens.push(getPseudoContent(getComputedStyle(element, '::after')));
|
||||||
const accessibleName = tokens.join('');
|
const accessibleName = tokens.join('');
|
||||||
if (accessibleName.trim())
|
if (accessibleName.trim())
|
||||||
|
|||||||
@ -423,3 +423,63 @@ test('errors', async ({ page }) => {
|
|||||||
const e8 = await page.$('role=treeitem[expanded="none"]').catch(e => e);
|
const e8 = await page.$('role=treeitem[expanded="none"]').catch(e => e);
|
||||||
expect(e8.message).toContain(`"expanded" must be one of true, false`);
|
expect(e8.message).toContain(`"expanded" must be one of true, false`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should detect accessible name with slots', async ({ page }) => {
|
||||||
|
// Text "foo" is assigned to the slot, should not be used twice.
|
||||||
|
await page.setContent(`
|
||||||
|
<button><div>foo</div></button>
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const container = document.querySelector('div');
|
||||||
|
const shadow = container.attachShadow({ mode: 'open' });
|
||||||
|
const slot = document.createElement('slot');
|
||||||
|
shadow.appendChild(slot);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
`);
|
||||||
|
expect(await page.locator(`role=button[name="foo"]`).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||||
|
`<button><div>foo</div></button>`,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Text "foo" is assigned to the slot, should be used instead of slot content.
|
||||||
|
await page.setContent(`
|
||||||
|
<div>foo</div>
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const container = document.querySelector('div');
|
||||||
|
const shadow = container.attachShadow({ mode: 'open' });
|
||||||
|
const button = document.createElement('button');
|
||||||
|
shadow.appendChild(button);
|
||||||
|
const slot = document.createElement('slot');
|
||||||
|
button.appendChild(slot);
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.textContent = 'pre';
|
||||||
|
slot.appendChild(span);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
`);
|
||||||
|
expect(await page.locator(`role=button[name="foo"]`).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||||
|
`<button><slot><span>pre</span></slot></button>`,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Nothing is assigned to the slot, should use slot content.
|
||||||
|
await page.setContent(`
|
||||||
|
<div></div>
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const container = document.querySelector('div');
|
||||||
|
const shadow = container.attachShadow({ mode: 'open' });
|
||||||
|
const button = document.createElement('button');
|
||||||
|
shadow.appendChild(button);
|
||||||
|
const slot = document.createElement('slot');
|
||||||
|
button.appendChild(slot);
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.textContent = 'pre';
|
||||||
|
slot.appendChild(span);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
`);
|
||||||
|
expect(await page.locator(`role=button[name="pre"]`).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||||
|
`<button><slot><span>pre</span></slot></button>`,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user