chore: make role name case-insensitive (#17888)
This commit is contained in:
Родитель
43208da3f8
Коммит
8b018f6b41
|
@ -396,7 +396,7 @@ export function setTestIdAttribute(attributeName: string) {
|
|||
function getByAttributeTextSelector(attrName: string, text: string | RegExp, options?: { exact?: boolean }): string {
|
||||
if (!isString(text))
|
||||
return `internal:attr=[${attrName}=${text}]`;
|
||||
return `internal:attr=[${attrName}=${escapeForAttributeSelector(text)}${options?.exact ? 's' : 'i'}]`;
|
||||
return `internal:attr=[${attrName}=${escapeForAttributeSelector(text, options?.exact || false)}]`;
|
||||
}
|
||||
|
||||
export function getByTestIdSelector(testId: string): string {
|
||||
|
@ -439,7 +439,7 @@ export function getByRoleSelector(role: string, options: ByRoleOptions = {}): st
|
|||
if (options.level !== undefined)
|
||||
props.push(['level', String(options.level)]);
|
||||
if (options.name !== undefined)
|
||||
props.push(['name', isString(options.name) ? escapeForAttributeSelector(options.name) : String(options.name)]);
|
||||
props.push(['name', isString(options.name) ? escapeForAttributeSelector(options.name, false) : String(options.name)]);
|
||||
if (options.pressed !== undefined)
|
||||
props.push(['pressed', String(options.pressed)]);
|
||||
return `role=${role}${props.map(([n, v]) => `[${n}=${v}]`).join('')}`;
|
||||
|
|
|
@ -148,7 +148,7 @@ function buildCandidates(injectedScript: InjectedScript, element: Element, acces
|
|||
const candidates: SelectorToken[] = [];
|
||||
|
||||
if (element.getAttribute('data-testid'))
|
||||
candidates.push({ engine: 'internal:attr', selector: `[data-testid=${escapeForAttributeSelector(element.getAttribute('data-testid')!)}]`, score: 1 });
|
||||
candidates.push({ engine: 'internal:attr', selector: `[data-testid=${escapeForAttributeSelector(element.getAttribute('data-testid')!, true)}]`, score: 1 });
|
||||
|
||||
for (const attr of ['data-test-id', 'data-test']) {
|
||||
if (element.getAttribute(attr))
|
||||
|
@ -158,7 +158,7 @@ function buildCandidates(injectedScript: InjectedScript, element: Element, acces
|
|||
if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
|
||||
const input = element as HTMLInputElement | HTMLTextAreaElement;
|
||||
if (input.placeholder)
|
||||
candidates.push({ engine: 'internal:attr', selector: `[placeholder=${escapeForAttributeSelector(input.placeholder)}]`, score: 3 });
|
||||
candidates.push({ engine: 'internal:attr', selector: `[placeholder=${escapeForAttributeSelector(input.placeholder, true)}]`, score: 3 });
|
||||
const label = input.labels?.[0];
|
||||
if (label) {
|
||||
const labelText = elementText(injectedScript._evaluator._cacheText, label).full.trim();
|
||||
|
@ -170,13 +170,13 @@ function buildCandidates(injectedScript: InjectedScript, element: Element, acces
|
|||
if (ariaRole) {
|
||||
const ariaName = getElementAccessibleName(element, false, accessibleNameCache);
|
||||
if (ariaName)
|
||||
candidates.push({ engine: 'role', selector: `${ariaRole}[name=${escapeForAttributeSelector(ariaName)}]`, score: 3 });
|
||||
candidates.push({ engine: 'role', selector: `${ariaRole}[name=${escapeForAttributeSelector(ariaName, true)}]`, score: 3 });
|
||||
else
|
||||
candidates.push({ engine: 'role', selector: ariaRole, score: 150 });
|
||||
}
|
||||
|
||||
if (element.getAttribute('alt') && ['APPLET', 'AREA', 'IMG', 'INPUT'].includes(element.nodeName))
|
||||
candidates.push({ engine: 'internal:attr', selector: `[alt=${escapeForAttributeSelector(element.getAttribute('alt')!)}]`, score: 10 });
|
||||
candidates.push({ engine: 'internal:attr', selector: `[alt=${escapeForAttributeSelector(element.getAttribute('alt')!, true)}]`, score: 10 });
|
||||
|
||||
if (element.getAttribute('name') && ['BUTTON', 'FORM', 'FIELDSET', 'FRAME', 'IFRAME', 'INPUT', 'KEYGEN', 'OBJECT', 'OUTPUT', 'SELECT', 'TEXTAREA', 'MAP', 'META', 'PARAM'].includes(element.nodeName))
|
||||
candidates.push({ engine: 'css', selector: `${cssEscape(element.nodeName.toLowerCase())}[name=${quoteAttributeValue(element.getAttribute('name')!)}]`, score: 50 });
|
||||
|
|
|
@ -72,10 +72,10 @@ export function escapeForTextSelector(text: string | RegExp, exact: boolean, cas
|
|||
return text;
|
||||
}
|
||||
|
||||
export function escapeForAttributeSelector(value: string): string {
|
||||
export function escapeForAttributeSelector(value: string, exact: boolean): string {
|
||||
// TODO: this should actually be
|
||||
// cssEscape(value).replace(/\\ /g, ' ')
|
||||
// However, our attribute selectors do not conform to CSS parsing spec,
|
||||
// so we escape them differently.
|
||||
return `"${value.replace(/["]/g, '\\"')}"`;
|
||||
return `"${value.replace(/["]/g, '\\"')}"${exact ? '' : 'i'}`;
|
||||
}
|
||||
|
|
|
@ -360,6 +360,10 @@ test('should support name', async ({ page }) => {
|
|||
`<div role="button" aria-label="Hello"></div>`,
|
||||
`<div role="button" aria-label="Hello" aria-hidden="true"></div>`,
|
||||
]);
|
||||
expect(await page.getByRole('button', { name: 'hello', includeHidden: true }).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||
`<div role="button" aria-label="Hello"></div>`,
|
||||
`<div role="button" aria-label="Hello" aria-hidden="true"></div>`,
|
||||
]);
|
||||
|
||||
expect(await page.locator(`role=button[name=Hello]`).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||
`<div role="button" aria-label="Hello"></div>`,
|
||||
|
|
Загрузка…
Ссылка в новой задаче