fix(aria): normalize whitespace in accessible name and description
This makes it easier to work with, both asserting and querying. Note: this might break some role locators with RegExp names that were expecting particular whitespace.
This commit is contained in:
parent
f5477d9051
commit
5736d46c87
|
|
@ -147,8 +147,7 @@ function queryRole(scope: SelectorRoot, options: RoleEngineOptions, internal: bo
|
|||
return;
|
||||
}
|
||||
if (options.name !== undefined) {
|
||||
// Always normalize whitespace in the accessible name.
|
||||
const accessibleName = normalizeWhiteSpace(getElementAccessibleName(element, !!options.includeHidden));
|
||||
const accessibleName = getElementAccessibleName(element, !!options.includeHidden);
|
||||
if (typeof options.name === 'string')
|
||||
options.name = normalizeWhiteSpace(options.name);
|
||||
// internal:role assumes that [name="foo"i] also means substring.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
import type { AriaRole } from '@isomorphic/ariaSnapshot';
|
||||
import { closestCrossShadow, elementSafeTagName, enclosingShadowRootOrDocument, getElementComputedStyle, isElementStyleVisibilityVisible, isVisibleTextNode, parentElementOrShadowHost } from './domUtils';
|
||||
import { normalizeWhiteSpace } from '../../utils/isomorphic/stringUtils';
|
||||
|
||||
function hasExplicitAccessibleName(e: Element) {
|
||||
return e.hasAttribute('aria-label') || e.hasAttribute('aria-labelledby');
|
||||
|
|
@ -420,6 +421,8 @@ export function getElementAccessibleName(element: Element, includeHidden: boolea
|
|||
}));
|
||||
}
|
||||
|
||||
// Note: we always normalize whitespace in the accessible name to make it easier to work with.
|
||||
accessibleName = normalizeWhiteSpace(accessibleName);
|
||||
cache?.set(element, accessibleName);
|
||||
}
|
||||
return accessibleName;
|
||||
|
|
@ -452,6 +455,8 @@ export function getElementAccessibleDescription(element: Element, includeHidden:
|
|||
accessibleDescription = asFlatString(element.getAttribute('title') || '');
|
||||
}
|
||||
|
||||
// Note: we always normalize whitespace in the accessible description to make it easier to work with.
|
||||
accessibleDescription = normalizeWhiteSpace(accessibleDescription);
|
||||
cache?.set(element, accessibleDescription);
|
||||
}
|
||||
return accessibleDescription;
|
||||
|
|
|
|||
|
|
@ -475,24 +475,9 @@ test('should ignore stylesheet from hidden aria-labelledby subtree', async ({ pa
|
|||
expect.soft(await getNameAndRole(page, 'input')).toEqual({ role: 'textbox', name: 'hello' });
|
||||
});
|
||||
|
||||
test('should not include hidden pseudo into accessible name', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<style>
|
||||
span:before {
|
||||
content: 'world';
|
||||
display: none;
|
||||
}
|
||||
div:after {
|
||||
content: 'bye';
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
<a href="http://example.com">
|
||||
<span>hello</span>
|
||||
<div>hello</div>
|
||||
</a>
|
||||
`);
|
||||
expect.soft(await getNameAndRole(page, 'a')).toEqual({ role: 'link', name: 'hello hello' });
|
||||
test('should normalize accessible name', async ({ page }) => {
|
||||
await page.setContent(`<button>foo bar\nbaz</button>`);
|
||||
expect.soft(await getNameAndRole(page, 'button')).toEqual({ role: 'button', name: 'foo bar baz' });
|
||||
});
|
||||
|
||||
function toArray(x: any): any[] {
|
||||
|
|
|
|||
|
|
@ -431,6 +431,9 @@ test('toHaveAccessibleName', async ({ page }) => {
|
|||
await expect(page.locator('div')).toHaveAccessibleName(/ell\w/);
|
||||
await expect(page.locator('div')).not.toHaveAccessibleName(/hello/);
|
||||
await expect(page.locator('div')).toHaveAccessibleName(/hello/, { ignoreCase: true });
|
||||
|
||||
await page.setContent(`<button>foo bar\nbaz</button>`);
|
||||
await expect(page.locator('button')).toHaveAccessibleName('foo bar baz');
|
||||
});
|
||||
|
||||
test('toHaveAccessibleDescription', async ({ page }) => {
|
||||
|
|
@ -443,6 +446,12 @@ test('toHaveAccessibleDescription', async ({ page }) => {
|
|||
await expect(page.locator('div')).toHaveAccessibleDescription(/ell\w/);
|
||||
await expect(page.locator('div')).not.toHaveAccessibleDescription(/hello/);
|
||||
await expect(page.locator('div')).toHaveAccessibleDescription(/hello/, { ignoreCase: true });
|
||||
|
||||
await page.setContent(`
|
||||
<div role="button" aria-describedby="desc"></div>
|
||||
<span id="desc">foo bar\nbaz</span>
|
||||
`);
|
||||
await expect(page.locator('div')).toHaveAccessibleDescription('foo bar baz');
|
||||
});
|
||||
|
||||
test('toHaveRole', async ({ page }) => {
|
||||
|
|
|
|||
|
|
@ -479,6 +479,14 @@ it('should escape yaml text in text nodes', async ({ page }) => {
|
|||
`);
|
||||
});
|
||||
|
||||
it('should normalize accessible name', async ({ page }) => {
|
||||
await page.setContent(`<button>foo bar\nbaz</button>`);
|
||||
|
||||
await checkAndMatchSnapshot(page.locator('body'), `
|
||||
- button "foo bar baz"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle long strings', async ({ page }) => {
|
||||
const s = 'a'.repeat(10000);
|
||||
await page.setContent(`
|
||||
|
|
|
|||
Loading…
Reference in a new issue