mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chrome: expose link/url in aria (#35134)
This commit is contained in:
parent
e2f95747e1
commit
63e5257a4c
@ -27,6 +27,7 @@ export type AriaNode = AriaProps & {
|
||||
name: string;
|
||||
children: (AriaNode | string)[];
|
||||
element: Element;
|
||||
props: Record<string, string>;
|
||||
};
|
||||
|
||||
export type AriaSnapshot = {
|
||||
@ -40,7 +41,7 @@ export function generateAriaTree(rootElement: Element, generation: number): Aria
|
||||
const visited = new Set<Node>();
|
||||
|
||||
const snapshot: AriaSnapshot = {
|
||||
root: { role: 'fragment', name: '', children: [], element: rootElement },
|
||||
root: { role: 'fragment', name: '', children: [], element: rootElement, props: {} },
|
||||
elements: new Map<number, Element>(),
|
||||
generation,
|
||||
ids: new Map<Element, number>(),
|
||||
@ -124,6 +125,11 @@ export function generateAriaTree(rootElement: Element, generation: number): Aria
|
||||
|
||||
if (ariaNode.children.length === 1 && ariaNode.name === ariaNode.children[0])
|
||||
ariaNode.children = [];
|
||||
|
||||
if (ariaNode.role === 'link' && element.hasAttribute('href')) {
|
||||
const href = element.getAttribute('href')!;
|
||||
ariaNode.props['url'] = href;
|
||||
}
|
||||
}
|
||||
|
||||
roleUtils.beginAriaCaches();
|
||||
@ -143,7 +149,7 @@ function toAriaNode(element: Element): AriaNode | null {
|
||||
return null;
|
||||
|
||||
const name = normalizeWhiteSpace(roleUtils.getElementAccessibleName(element, false) || '');
|
||||
const result: AriaNode = { role, name, children: [], element };
|
||||
const result: AriaNode = { role, name, children: [], props: {}, element };
|
||||
|
||||
if (roleUtils.kAriaCheckedRoles.includes(role))
|
||||
result.checked = roleUtils.getAriaChecked(element);
|
||||
@ -263,6 +269,8 @@ function matchesNode(node: AriaNode | string, template: AriaTemplateNode, depth:
|
||||
return false;
|
||||
if (!matchesName(node.name, template))
|
||||
return false;
|
||||
if (!matchesText(node.props.url, template.props?.url))
|
||||
return false;
|
||||
if (!containsList(node.children || [], template.children || [], depth))
|
||||
return false;
|
||||
return true;
|
||||
@ -355,9 +363,10 @@ export function renderAriaTree(ariaSnapshot: AriaSnapshot, options?: { mode?: 'r
|
||||
}
|
||||
|
||||
const escapedKey = indent + '- ' + yamlEscapeKeyIfNeeded(key);
|
||||
if (!ariaNode.children.length) {
|
||||
const hasProps = !!Object.keys(ariaNode.props).length;
|
||||
if (!ariaNode.children.length && !hasProps) {
|
||||
lines.push(escapedKey);
|
||||
} else if (ariaNode.children.length === 1 && typeof ariaNode.children[0] === 'string') {
|
||||
} else if (ariaNode.children.length === 1 && typeof ariaNode.children[0] === 'string' && !hasProps) {
|
||||
const text = includeText(ariaNode, ariaNode.children[0]) ? renderString(ariaNode.children[0] as string) : null;
|
||||
if (text)
|
||||
lines.push(escapedKey + ': ' + yamlEscapeValueIfNeeded(text));
|
||||
@ -365,6 +374,8 @@ export function renderAriaTree(ariaSnapshot: AriaSnapshot, options?: { mode?: 'r
|
||||
lines.push(escapedKey);
|
||||
} else {
|
||||
lines.push(escapedKey + ':');
|
||||
for (const [name, value] of Object.entries(ariaNode.props))
|
||||
lines.push(indent + ' - /' + name + ': ' + yamlEscapeValueIfNeeded(value));
|
||||
for (const child of ariaNode.children || [])
|
||||
visit(child, ariaNode, indent + ' ');
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@ export type AriaTemplateRoleNode = AriaProps & {
|
||||
role: AriaRole | 'fragment';
|
||||
name?: AriaRegex | string;
|
||||
children?: AriaTemplateNode[];
|
||||
props?: Record<string, string | AriaRegex>;
|
||||
};
|
||||
|
||||
export type AriaTemplateNode = AriaTemplateRoleNode | AriaTemplateTextNode;
|
||||
@ -151,6 +152,21 @@ export function parseAriaSnapshot(yaml: YamlLibrary, text: string, options: yaml
|
||||
continue;
|
||||
}
|
||||
|
||||
// - /url: "about:blank"
|
||||
if (key.value.startsWith('/')) {
|
||||
const valueIsString = value instanceof yaml.Scalar && typeof value.value === 'string';
|
||||
if (!valueIsString) {
|
||||
errors.push({
|
||||
message: 'Property value should be a string',
|
||||
range: convertRange(((entry.value as any).range || map.range)),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
container.props = container.props ?? {};
|
||||
container.props[key.value.slice(1)] = valueOrRegex(value.value);
|
||||
continue;
|
||||
}
|
||||
|
||||
// role "name": ...
|
||||
const childNode = KeyParser.parse(key, parseOptions, errors);
|
||||
if (!childNode)
|
||||
|
||||
@ -79,7 +79,8 @@ it('should snapshot complex', async ({ page }) => {
|
||||
await checkAndMatchSnapshot(page.locator('body'), `
|
||||
- list:
|
||||
- listitem:
|
||||
- link "link"
|
||||
- link "link":
|
||||
- /url: about:blank
|
||||
`);
|
||||
});
|
||||
|
||||
@ -149,7 +150,8 @@ it('should snapshot integration', async ({ page }) => {
|
||||
- listitem:
|
||||
- group: Verified
|
||||
- listitem:
|
||||
- link "Sponsor"
|
||||
- link "Sponsor":
|
||||
- /url: about:blank
|
||||
`);
|
||||
});
|
||||
|
||||
@ -220,7 +222,8 @@ it('should include pseudo in text', async ({ page }) => {
|
||||
`);
|
||||
|
||||
await checkAndMatchSnapshot(page.locator('body'), `
|
||||
- link "worldhello hellobye"
|
||||
- link "worldhello hellobye":
|
||||
- /url: about:blank
|
||||
`);
|
||||
});
|
||||
|
||||
@ -243,7 +246,8 @@ it('should not include hidden pseudo in text', async ({ page }) => {
|
||||
`);
|
||||
|
||||
await checkAndMatchSnapshot(page.locator('body'), `
|
||||
- link "hello hello"
|
||||
- link "hello hello":
|
||||
- /url: about:blank
|
||||
`);
|
||||
});
|
||||
|
||||
@ -266,7 +270,8 @@ it('should include new line for block pseudo', async ({ page }) => {
|
||||
`);
|
||||
|
||||
await checkAndMatchSnapshot(page.locator('body'), `
|
||||
- link "world hello hello bye"
|
||||
- link "world hello hello bye":
|
||||
- /url: about:blank
|
||||
`);
|
||||
});
|
||||
|
||||
@ -450,10 +455,12 @@ it('should respect aria-owns', async ({ page }) => {
|
||||
// - Disregarding these as aria-owns can't suggest multiple parts by spec.
|
||||
await checkAndMatchSnapshot(page.locator('body'), `
|
||||
- link "Link 1 Value Paragraph":
|
||||
- /url: about:blank
|
||||
- region: Link 1
|
||||
- textbox: Value
|
||||
- paragraph: Paragraph
|
||||
- link "Link 2 Value Paragraph":
|
||||
- /url: about:blank
|
||||
- region: Link 2
|
||||
`);
|
||||
});
|
||||
@ -467,6 +474,7 @@ it('should be ok with circular ownership', async ({ page }) => {
|
||||
|
||||
await checkAndMatchSnapshot(page.locator('body'), `
|
||||
- link "Hello":
|
||||
- /url: about:blank
|
||||
- region: Hello
|
||||
`);
|
||||
});
|
||||
@ -488,22 +496,30 @@ it('should escape yaml text in text nodes', async ({ page }) => {
|
||||
await checkAndMatchSnapshot(page.locator('body'), `
|
||||
- group:
|
||||
- text: "one:"
|
||||
- link "link1"
|
||||
- link "link1":
|
||||
- /url: "#"
|
||||
- text: "\\\"two"
|
||||
- link "link2"
|
||||
- link "link2":
|
||||
- /url: "#"
|
||||
- text: "'three"
|
||||
- link "link3"
|
||||
- link "link3":
|
||||
- /url: "#"
|
||||
- text: "\`four"
|
||||
- list:
|
||||
- link "one"
|
||||
- link "one":
|
||||
- /url: "#"
|
||||
- text: ","
|
||||
- link "two"
|
||||
- link "two":
|
||||
- /url: "#"
|
||||
- text: (
|
||||
- link "three"
|
||||
- link "three":
|
||||
- /url: "#"
|
||||
- text: ") {"
|
||||
- link "four"
|
||||
- link "four":
|
||||
- /url: "#"
|
||||
- text: "} ["
|
||||
- link "five"
|
||||
- link "five":
|
||||
- /url: "#"
|
||||
- text: "]"
|
||||
- text: "[Select all]"
|
||||
`);
|
||||
@ -521,7 +537,8 @@ it('should normalize whitespace', async ({ page }) => {
|
||||
await checkAndMatchSnapshot(page.locator('body'), `
|
||||
- group:
|
||||
- text: one two
|
||||
- link "link 1"
|
||||
- link "link 1":
|
||||
- /url: "#"
|
||||
- textbox: hello world
|
||||
- button "helloworld"
|
||||
`);
|
||||
@ -532,7 +549,8 @@ it('should normalize whitespace', async ({ page }) => {
|
||||
- text: |
|
||||
one
|
||||
two
|
||||
- link " link 1 "
|
||||
- link " link 1 ":
|
||||
- /url: "#"
|
||||
- textbox: hello world
|
||||
- button "he\u00adlloworld\u200b"
|
||||
`);
|
||||
@ -548,6 +566,7 @@ it('should handle long strings', async ({ page }) => {
|
||||
|
||||
await checkAndMatchSnapshot(page.locator('body'), `
|
||||
- link:
|
||||
- /url: about:blank
|
||||
- region: ${s}
|
||||
`);
|
||||
});
|
||||
@ -562,15 +581,20 @@ it('should escape special yaml characters', async ({ page }) => {
|
||||
`);
|
||||
|
||||
await checkAndMatchSnapshot(page.locator('body'), `
|
||||
- link "@hello"
|
||||
- link "@hello":
|
||||
- /url: "#"
|
||||
- text: "@hello"
|
||||
- link "]hello"
|
||||
- link "]hello":
|
||||
- /url: "#"
|
||||
- text: "]hello"
|
||||
- link "hello"
|
||||
- link "hello":
|
||||
- /url: "#"
|
||||
- text: hello
|
||||
- link "hello"
|
||||
- link "hello":
|
||||
- /url: "#"
|
||||
- text: hello
|
||||
- link "#hello"
|
||||
- link "#hello":
|
||||
- /url: "#"
|
||||
- text: "#hello"
|
||||
`);
|
||||
});
|
||||
@ -589,21 +613,29 @@ it('should escape special yaml values', async ({ page }) => {
|
||||
`);
|
||||
|
||||
await checkAndMatchSnapshot(page.locator('body'), `
|
||||
- link "true"
|
||||
- link "true":
|
||||
- /url: "#"
|
||||
- text: "False"
|
||||
- link "NO"
|
||||
- link "NO":
|
||||
- /url: "#"
|
||||
- text: "yes"
|
||||
- link "y"
|
||||
- link "y":
|
||||
- /url: "#"
|
||||
- text: "N"
|
||||
- link "on"
|
||||
- link "on":
|
||||
- /url: "#"
|
||||
- text: "Off"
|
||||
- link "null"
|
||||
- link "null":
|
||||
- /url: "#"
|
||||
- text: "NULL"
|
||||
- link "123"
|
||||
- link "123":
|
||||
- /url: "#"
|
||||
- text: "123"
|
||||
- link "-1.2"
|
||||
- link "-1.2":
|
||||
- /url: "#"
|
||||
- text: "-1.2"
|
||||
- link "-"
|
||||
- link "-":
|
||||
- /url: "#"
|
||||
- text: "-"
|
||||
- textbox: "555"
|
||||
`);
|
||||
|
||||
@ -682,3 +682,14 @@ test('should not match what is not matched', async ({ page }) => {
|
||||
- - button "bogus"
|
||||
+ - paragraph: Text`);
|
||||
});
|
||||
|
||||
test('should match url', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<a href='https://example.com'>Link</a>
|
||||
`);
|
||||
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||
- link:
|
||||
- /url: /.*example.com/
|
||||
`);
|
||||
});
|
||||
|
||||
@ -256,7 +256,7 @@ test('should generate baseline with special characters', async ({ runInlineTest
|
||||
expect(trimPatch(data)).toBe(`diff --git a/a.spec.ts b/a.spec.ts
|
||||
--- a/a.spec.ts
|
||||
+++ b/a.spec.ts
|
||||
@@ -17,6 +17,27 @@
|
||||
@@ -17,6 +17,30 @@
|
||||
<li>Item: 1</li>
|
||||
<li>Item {a: b}</li>
|
||||
</ul>\`);
|
||||
@ -265,11 +265,14 @@ test('should generate baseline with special characters', async ({ runInlineTest
|
||||
+ - list:
|
||||
+ - group:
|
||||
+ - text: "one:"
|
||||
+ - link "link1"
|
||||
+ - link "link1":
|
||||
+ - /url: "#"
|
||||
+ - text: "\\\\\"two"
|
||||
+ - link "link2"
|
||||
+ - link "link2":
|
||||
+ - /url: "#"
|
||||
+ - text: "'three"
|
||||
+ - link "link3"
|
||||
+ - link "link3":
|
||||
+ - /url: "#"
|
||||
+ - text: "\\\`four"
|
||||
+ - heading "heading \\\\"name\\\\" [level=1]" [level=1]
|
||||
+ - 'button "Click: me"'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user