mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-13 17:14:02 +03:00
chore: generate getByLabel for inputs (#17845)
This commit is contained in:
parent
e8413264fa
commit
c168f5494f
@ -81,7 +81,7 @@ function generateSelectorFor(injectedScript: InjectedScript, targetElement: Elem
|
||||
// Do not use regex for parent elements (for performance).
|
||||
textCandidates = filterRegexTokens(textCandidates);
|
||||
}
|
||||
const noTextCandidates = buildCandidates(element, accessibleNameCache).map(token => [token]);
|
||||
const noTextCandidates = buildCandidates(injectedScript, element, accessibleNameCache).map(token => [token]);
|
||||
|
||||
// First check all text and non-text candidates for the element.
|
||||
let result = chooseFirstSelector(injectedScript, targetElement.ownerDocument, element, [...textCandidates, ...noTextCandidates], allowNthMatch, strict);
|
||||
@ -144,7 +144,7 @@ function generateSelectorFor(injectedScript: InjectedScript, targetElement: Elem
|
||||
return calculateCached(targetElement, true);
|
||||
}
|
||||
|
||||
function buildCandidates(element: Element, accessibleNameCache: Map<Element, boolean>): SelectorToken[] {
|
||||
function buildCandidates(injectedScript: InjectedScript, element: Element, accessibleNameCache: Map<Element, boolean>): SelectorToken[] {
|
||||
const candidates: SelectorToken[] = [];
|
||||
|
||||
if (element.getAttribute('data-testid'))
|
||||
@ -155,10 +155,15 @@ function buildCandidates(element: Element, accessibleNameCache: Map<Element, boo
|
||||
candidates.push({ engine: 'css', selector: `[${attr}=${quoteAttributeValue(element.getAttribute(attr)!)}]`, score: 2 });
|
||||
}
|
||||
|
||||
if (element.nodeName === 'INPUT') {
|
||||
const input = element as HTMLInputElement;
|
||||
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 });
|
||||
const label = input.labels?.[0];
|
||||
if (label) {
|
||||
const labelText = elementText(injectedScript._evaluator._cacheText, label).full.trim();
|
||||
candidates.push({ engine: 'internal:label', selector: escapeForTextSelector(labelText, false, true), score: 3 });
|
||||
}
|
||||
}
|
||||
|
||||
const ariaRole = getAriaRole(element);
|
||||
|
@ -259,6 +259,11 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
||||
}
|
||||
|
||||
function toCallWithExact(method: string, body: string, exact: boolean) {
|
||||
if (body.startsWith('/') && (body.endsWith('/') || body.endsWith('/i'))) {
|
||||
const regex = body.substring(1, body.lastIndexOf('/'));
|
||||
const suffix = body.endsWith('i') ? ', RegexOptions.IgnoreCase' : '';
|
||||
return `${method}(new Regex(${quote(regex)}${suffix}))`;
|
||||
}
|
||||
if (exact)
|
||||
return `${method}(${quote(body)}, new () { Exact: true })`;
|
||||
return `${method}(${quote(body)})`;
|
||||
|
@ -203,6 +203,11 @@ export class JavaLanguageGenerator implements LanguageGenerator {
|
||||
}
|
||||
|
||||
function toCallWithExact(clazz: string, method: string, body: string, exact: boolean) {
|
||||
if (body.startsWith('/') && (body.endsWith('/') || body.endsWith('/i'))) {
|
||||
const regex = body.substring(1, body.lastIndexOf('/'));
|
||||
const suffix = body.endsWith('i') ? ', Pattern.CASE_INSENSITIVE' : '';
|
||||
return `${method}(Pattern.compile(${quote(regex)}${suffix}))`;
|
||||
}
|
||||
if (exact)
|
||||
return `${method}(${quote(body)}, new ${clazz}.${toTitleCase(method)}Options().setExact(exact))`;
|
||||
return `${method}(${quote(body)})`;
|
||||
|
@ -240,6 +240,8 @@ ${useText ? '\ntest.use(' + useText + ');\n' : ''}
|
||||
}
|
||||
|
||||
function toCallWithExact(method: string, body: string, exact: boolean) {
|
||||
if (body.startsWith('/') && (body.endsWith('/') || body.endsWith('/i')))
|
||||
return `${method}(${body})`;
|
||||
return exact ? `${method}(${quote(body)}, { exact: true })` : `${method}(${quote(body)})`;
|
||||
}
|
||||
|
||||
|
@ -104,6 +104,11 @@ export function asLocator(generator: LanguageGenerator, selector: string, isFram
|
||||
tokens.push(generator.generateLocator(base, 'text', text, { exact }));
|
||||
continue;
|
||||
}
|
||||
if (part.name === 'internal:label') {
|
||||
const { exact, text } = detectExact(part.body as string);
|
||||
tokens.push(generator.generateLocator(base, 'label', text, { exact }));
|
||||
continue;
|
||||
}
|
||||
if (part.name === 'role') {
|
||||
const attrSelector = parseAttributeSelector(part.body as string, true);
|
||||
const attrs: Record<string, boolean | string> = {};
|
||||
|
@ -258,6 +258,11 @@ with sync_playwright() as playwright:
|
||||
}
|
||||
|
||||
function toCallWithExact(method: string, body: string, exact: boolean) {
|
||||
if (body.startsWith('/') && (body.endsWith('/') || body.endsWith('/i'))) {
|
||||
const regex = body.substring(1, body.lastIndexOf('/'));
|
||||
const suffix = body.endsWith('i') ? ', re.IGNORECASE' : '';
|
||||
return `${method}(re.compile(r${quote(regex)}${suffix}))`;
|
||||
}
|
||||
if (exact)
|
||||
return `${method}(${quote(body)}, exact=true)`;
|
||||
return `${method}(${quote(body)})`;
|
||||
|
@ -319,4 +319,61 @@ test.describe('cli codegen', () => {
|
||||
await page.GetByAltText("Country").ClickAsync();`);
|
||||
});
|
||||
|
||||
test('should generate getByLabel', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<label for=target>Country</label><input id=target>`);
|
||||
|
||||
const selector = await recorder.hoverOverElement('input');
|
||||
expect(selector).toBe('internal:label=Country');
|
||||
|
||||
const [sources] = await Promise.all([
|
||||
recorder.waitForOutput('JavaScript', 'click'),
|
||||
page.dispatchEvent('input', 'click', { detail: 1 })
|
||||
]);
|
||||
|
||||
expect.soft(sources.get('JavaScript').text).toContain(`
|
||||
await page.getByLabel('Country').click();`);
|
||||
|
||||
expect.soft(sources.get('Python').text).toContain(`
|
||||
page.get_by_label("Country").click()`);
|
||||
|
||||
expect.soft(sources.get('Python Async').text).toContain(`
|
||||
await page.get_by_label("Country").click()`);
|
||||
|
||||
expect.soft(sources.get('Java').text).toContain(`
|
||||
page.getByLabel("Country").click()`);
|
||||
|
||||
expect.soft(sources.get('C#').text).toContain(`
|
||||
await page.GetByLabel("Country").ClickAsync();`);
|
||||
});
|
||||
|
||||
test('should generate getByLabel with regex', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<label for=target>Coun"try</label><input id=target>`);
|
||||
|
||||
const selector = await recorder.hoverOverElement('input');
|
||||
expect(selector).toBe('internal:label=/Coun"try/');
|
||||
|
||||
const [sources] = await Promise.all([
|
||||
recorder.waitForOutput('JavaScript', 'click'),
|
||||
page.dispatchEvent('input', 'click', { detail: 1 })
|
||||
]);
|
||||
|
||||
expect.soft(sources.get('JavaScript').text).toContain(`
|
||||
await page.getByLabel(/Coun"try/).click();`);
|
||||
|
||||
expect.soft(sources.get('Python').text).toContain(`
|
||||
page.get_by_label(re.compile(r"Coun\\\"try")).click()`);
|
||||
|
||||
expect.soft(sources.get('Python Async').text).toContain(`
|
||||
await page.get_by_label(re.compile(r"Coun\\\"try")).click()`);
|
||||
|
||||
expect.soft(sources.get('Java').text).toContain(`
|
||||
page.getByLabel(Pattern.compile("Coun\\\"try")).click()`);
|
||||
|
||||
expect.soft(sources.get('C#').text).toContain(`
|
||||
await page.GetByLabel(new Regex("Coun\\\"try")).ClickAsync();`);
|
||||
});
|
||||
});
|
||||
|
@ -366,4 +366,11 @@ it.describe('selector generator', () => {
|
||||
expect(await generate(page, 'button')).toBe('[data-test-id="testId"]');
|
||||
});
|
||||
|
||||
it('should generate label selector', async ({ page }) => {
|
||||
await page.setContent(`<label for=target>Country</label><input id=target>`);
|
||||
expect(await generate(page, 'input')).toBe('internal:label=Country');
|
||||
|
||||
await page.setContent(`<label for=target>Coun"try</label><input id=target>`);
|
||||
expect(await generate(page, 'input')).toBe('internal:label=/Coun"try/');
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user