chore: use internal locator for role (#18187)
This commit is contained in:
parent
ad9729f246
commit
84daeafb3a
|
|
@ -442,5 +442,5 @@ export function getByRoleSelector(role: string, options: ByRoleOptions = {}): st
|
||||||
props.push(['name', isString(options.name) ? escapeForAttributeSelector(options.name, false) : String(options.name)]);
|
props.push(['name', isString(options.name) ? escapeForAttributeSelector(options.name, false) : String(options.name)]);
|
||||||
if (options.pressed !== undefined)
|
if (options.pressed !== undefined)
|
||||||
props.push(['pressed', String(options.pressed)]);
|
props.push(['pressed', String(options.pressed)]);
|
||||||
return `role=${role}${props.map(([n, v]) => `[${n}=${v}]`).join('')}`;
|
return `internal:role=${role}${props.map(([n, v]) => `[${n}=${v}]`).join('')}`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@ export class InjectedScript {
|
||||||
this._engines.set('internal:label', this._createInternalLabelEngine());
|
this._engines.set('internal:label', this._createInternalLabelEngine());
|
||||||
this._engines.set('internal:text', this._createTextEngine(true, true));
|
this._engines.set('internal:text', this._createTextEngine(true, true));
|
||||||
this._engines.set('internal:attr', this._createNamedAttributeEngine());
|
this._engines.set('internal:attr', this._createNamedAttributeEngine());
|
||||||
|
this._engines.set('internal:role', RoleEngine);
|
||||||
|
|
||||||
for (const { name, engine } of customEngines)
|
for (const { name, engine } of customEngines)
|
||||||
this._engines.set(name, engine);
|
this._engines.set(name, engine);
|
||||||
|
|
|
||||||
|
|
@ -170,9 +170,9 @@ function buildCandidates(injectedScript: InjectedScript, element: Element, acces
|
||||||
if (ariaRole) {
|
if (ariaRole) {
|
||||||
const ariaName = getElementAccessibleName(element, false, accessibleNameCache);
|
const ariaName = getElementAccessibleName(element, false, accessibleNameCache);
|
||||||
if (ariaName)
|
if (ariaName)
|
||||||
candidates.push({ engine: 'role', selector: `${ariaRole}[name=${escapeForAttributeSelector(ariaName, true)}]`, score: 3 });
|
candidates.push({ engine: 'internal:role', selector: `${ariaRole}[name=${escapeForAttributeSelector(ariaName, true)}]`, score: 3 });
|
||||||
else
|
else
|
||||||
candidates.push({ engine: 'role', selector: ariaRole, score: 150 });
|
candidates.push({ engine: 'internal:role', selector: ariaRole, score: 150 });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.getAttribute('alt') && ['APPLET', 'AREA', 'IMG', 'INPUT'].includes(element.nodeName))
|
if (element.getAttribute('alt') && ['APPLET', 'AREA', 'IMG', 'INPUT'].includes(element.nodeName))
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ function innerAsLocator(factory: LocatorFactory, selector: string, isFrameLocato
|
||||||
tokens.push(factory.generateLocator(base, 'label', text, { exact }));
|
tokens.push(factory.generateLocator(base, 'label', text, { exact }));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (part.name === 'role') {
|
if (part.name === 'internal:role') {
|
||||||
const attrSelector = parseAttributeSelector(part.body as string, true);
|
const attrSelector = parseAttributeSelector(part.body as string, true);
|
||||||
const attrs: Record<string, boolean | string> = {};
|
const attrs: Record<string, boolean | string> = {};
|
||||||
for (const attr of attrSelector.attributes!)
|
for (const attr of attrSelector.attributes!)
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ export class Selectors {
|
||||||
'data-test-id', 'data-test-id:light',
|
'data-test-id', 'data-test-id:light',
|
||||||
'data-test', 'data-test:light',
|
'data-test', 'data-test:light',
|
||||||
'nth', 'visible', 'internal:control', 'internal:has',
|
'nth', 'visible', 'internal:control', 'internal:has',
|
||||||
'role', 'internal:attr', 'internal:label', 'internal:text'
|
'role', 'internal:attr', 'internal:label', 'internal:text', 'internal:role',
|
||||||
]);
|
]);
|
||||||
this._builtinEnginesInMainWorld = new Set([
|
this._builtinEnginesInMainWorld = new Set([
|
||||||
'_react', '_vue',
|
'_react', '_vue',
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ test.describe('cli codegen', () => {
|
||||||
await recorder.setContentAndWait(`<button onclick="console.log('click')">Submit</button>`);
|
await recorder.setContentAndWait(`<button onclick="console.log('click')">Submit</button>`);
|
||||||
|
|
||||||
const selector = await recorder.hoverOverElement('button');
|
const selector = await recorder.hoverOverElement('button');
|
||||||
expect(selector).toBe('role=button[name=\"Submit\"]');
|
expect(selector).toBe('internal:role=button[name=\"Submit\"]');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
|
|
@ -69,7 +69,7 @@ test.describe('cli codegen', () => {
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
const selector = await recorder.hoverOverElement('button');
|
const selector = await recorder.hoverOverElement('button');
|
||||||
expect(selector).toBe('role=button[name=\"Submit\"]');
|
expect(selector).toBe('internal:role=button[name=\"Submit\"]');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
|
|
@ -149,7 +149,7 @@ test.describe('cli codegen', () => {
|
||||||
</body>`);
|
</body>`);
|
||||||
|
|
||||||
const selector = await recorder.hoverOverElement('button');
|
const selector = await recorder.hoverOverElement('button');
|
||||||
expect(selector).toBe('role=button[name=\"Submit\"]');
|
expect(selector).toBe('internal:role=button[name=\"Submit\"]');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
|
|
@ -540,7 +540,7 @@ test.describe('cli codegen', () => {
|
||||||
await recorder.setContentAndWait('<a target=_blank rel=noopener href="about:blank">link</a>');
|
await recorder.setContentAndWait('<a target=_blank rel=noopener href="about:blank">link</a>');
|
||||||
|
|
||||||
const selector = await recorder.hoverOverElement('a');
|
const selector = await recorder.hoverOverElement('a');
|
||||||
expect(selector).toBe('role=link[name=\"link\"]');
|
expect(selector).toBe('internal:role=link[name=\"link\"]');
|
||||||
|
|
||||||
const [popup, sources] = await Promise.all([
|
const [popup, sources] = await Promise.all([
|
||||||
page.context().waitForEvent('page'),
|
page.context().waitForEvent('page'),
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,7 @@ test.describe('cli codegen', () => {
|
||||||
await recorder.setContentAndWait(`<a href="about:blank?foo">link</a>`);
|
await recorder.setContentAndWait(`<a href="about:blank?foo">link</a>`);
|
||||||
|
|
||||||
const selector = await recorder.hoverOverElement('a');
|
const selector = await recorder.hoverOverElement('a');
|
||||||
expect(selector).toBe('role=link[name=\"link\"]');
|
expect(selector).toBe('internal:role=link[name=\"link\"]');
|
||||||
|
|
||||||
await page.click('a', { modifiers: [platform === 'darwin' ? 'Meta' : 'Control'] });
|
await page.click('a', { modifiers: [platform === 'darwin' ? 'Meta' : 'Control'] });
|
||||||
const sources = await recorder.waitForOutput('JavaScript', 'page1');
|
const sources = await recorder.waitForOutput('JavaScript', 'page1');
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ test.describe('cli codegen', () => {
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const selector = await recorder.hoverOverElement('button');
|
const selector = await recorder.hoverOverElement('button');
|
||||||
expect(selector).toBe('role=button[name=\"Submit\"] >> nth=0');
|
expect(selector).toBe('internal:role=button[name=\"Submit\"] >> nth=0');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
|
|
@ -63,7 +63,7 @@ test.describe('cli codegen', () => {
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const selector = await recorder.hoverOverElement('button >> nth=1');
|
const selector = await recorder.hoverOverElement('button >> nth=1');
|
||||||
expect(selector).toBe('role=button[name=\"Submit\"] >> nth=1');
|
expect(selector).toBe('internal:role=button[name=\"Submit\"] >> nth=1');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ it.describe('selector generator', () => {
|
||||||
|
|
||||||
it('should prefer role=button over inner span', async ({ page }) => {
|
it('should prefer role=button over inner span', async ({ page }) => {
|
||||||
await page.setContent(`<div role=button><span></span></div>`);
|
await page.setContent(`<div role=button><span></span></div>`);
|
||||||
expect(await generate(page, 'div')).toBe('role=button');
|
expect(await generate(page, 'div')).toBe('internal:role=button');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate text and normalize whitespace', async ({ page }) => {
|
it('should generate text and normalize whitespace', async ({ page }) => {
|
||||||
|
|
@ -50,7 +50,7 @@ it.describe('selector generator', () => {
|
||||||
|
|
||||||
it('should generate text for <input type=button>', async ({ page }) => {
|
it('should generate text for <input type=button>', async ({ page }) => {
|
||||||
await page.setContent(`<input type=button value="Click me">`);
|
await page.setContent(`<input type=button value="Click me">`);
|
||||||
expect(await generate(page, 'input')).toBe('role=button[name=\"Click me\"]');
|
expect(await generate(page, 'input')).toBe('internal:role=button[name=\"Click me\"]');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should trim text', async ({ page }) => {
|
it('should trim text', async ({ page }) => {
|
||||||
|
|
@ -319,7 +319,7 @@ it.describe('selector generator', () => {
|
||||||
|
|
||||||
await page.setContent(`<button><span></span></button><button></button>`);
|
await page.setContent(`<button><span></span></button><button></button>`);
|
||||||
await page.$eval('button', button => button.setAttribute('aria-label', `!#'!?:`));
|
await page.$eval('button', button => button.setAttribute('aria-label', `!#'!?:`));
|
||||||
expect(await generate(page, 'button')).toBe(`role=button[name="!#'!?:"]`);
|
expect(await generate(page, 'button')).toBe(`internal:role=button[name="!#'!?:"]`);
|
||||||
expect(await page.$(`role=button[name="!#'!?:"]`)).toBeTruthy();
|
expect(await page.$(`role=button[name="!#'!?:"]`)).toBeTruthy();
|
||||||
|
|
||||||
await page.setContent(`<div><span></span></div>`);
|
await page.setContent(`<div><span></span></div>`);
|
||||||
|
|
@ -343,7 +343,7 @@ it.describe('selector generator', () => {
|
||||||
|
|
||||||
it('should accept valid aria-label for candidate consideration', async ({ page }) => {
|
it('should accept valid aria-label for candidate consideration', async ({ page }) => {
|
||||||
await page.setContent(`<button aria-label="ariaLabel" id="buttonId"></button>`);
|
await page.setContent(`<button aria-label="ariaLabel" id="buttonId"></button>`);
|
||||||
expect(await generate(page, 'button')).toBe('role=button[name=\"ariaLabel\"]');
|
expect(await generate(page, 'button')).toBe('internal:role=button[name=\"ariaLabel\"]');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore empty role for candidate consideration', async ({ page }) => {
|
it('should ignore empty role for candidate consideration', async ({ page }) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue