mirror of
https://github.com/microsoft/playwright.git
synced 2024-11-24 14:55:38 +03:00
feat(fill): allow filling based on the label selector (#4342)
This enables filling the input based on the connected label: ```html <label for=target>Name</label><input id=target> ``` ```js await page.fill('text=Name', 'Alice'); ```
This commit is contained in:
parent
5d39eae509
commit
c384313058
@ -2,7 +2,7 @@
|
||||
|
||||
<!-- GEN:toc-top-level -->
|
||||
- [Text input](#text-input)
|
||||
- [Checkboxes](#checkboxes)
|
||||
- [Checkboxes and radio buttons](#checkboxes-and-radio-buttons)
|
||||
- [Select options](#select-options)
|
||||
- [Mouse click](#mouse-click)
|
||||
- [Type characters](#type-characters)
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
## Text input
|
||||
|
||||
This is the easiest way to fill out the form fields. It focuses the element and triggers an `input` event with the entered text. It works for `<input>`, `<textarea>` and `[contenteditable]` elements.
|
||||
This is the easiest way to fill out the form fields. It focuses the element and triggers an `input` event with the entered text. It works for `<input>`, `<textarea>`, `[contenteditable]` and `<label>` associated with an input or textarea.
|
||||
|
||||
```js
|
||||
// Text input
|
||||
@ -29,6 +29,9 @@ await page.fill('#time', '13-15');
|
||||
|
||||
// Local datetime input
|
||||
await page.fill('#local', '2020-03-02T05:15');
|
||||
|
||||
// Input through label
|
||||
await page.fill('text=First Name', 'Peter');
|
||||
```
|
||||
|
||||
#### API reference
|
||||
@ -39,9 +42,9 @@ await page.fill('#local', '2020-03-02T05:15');
|
||||
|
||||
<br/>
|
||||
|
||||
## Checkboxes
|
||||
## Checkboxes and radio buttons
|
||||
|
||||
This is the easiest way to check and uncheck a checkbox. This method can be used on the `input[type=checkbox]` and on the `label` associated with that input.
|
||||
This is the easiest way to check and uncheck a checkbox or a radio button. This method can be used with `input[type=checkbox]`, `input[type=radio]`, `[role=checkbox]` or `label` associated with checkbox or radio button.
|
||||
|
||||
```js
|
||||
// Check the checkbox
|
||||
@ -49,6 +52,9 @@ await page.check('#agree');
|
||||
|
||||
// Uncheck by input <label>.
|
||||
await page.uncheck('#subscribe-label');
|
||||
|
||||
// Select the radio button
|
||||
await page.check('text=XL');
|
||||
```
|
||||
|
||||
#### API reference
|
||||
|
@ -261,10 +261,10 @@ export class InjectedScript {
|
||||
return this.pollRaf((progress, continuePolling) => {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE)
|
||||
return 'error:notelement';
|
||||
const element = node as Element;
|
||||
if (!element.isConnected)
|
||||
const element = this.findLabelTarget(node as Element);
|
||||
if (element && !element.isConnected)
|
||||
return 'error:notconnected';
|
||||
if (!this.isVisible(element)) {
|
||||
if (!element || !this.isVisible(element)) {
|
||||
progress.logRepeating(' element is not visible - waiting...');
|
||||
return continuePolling;
|
||||
}
|
||||
@ -438,27 +438,22 @@ export class InjectedScript {
|
||||
return 'done';
|
||||
}
|
||||
|
||||
findLabelTarget(element: Element): Element | undefined {
|
||||
return element.nodeName === 'LABEL' ? (element as HTMLLabelElement).control || undefined : element;
|
||||
}
|
||||
|
||||
isCheckboxChecked(node: Node) {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE)
|
||||
throw new Error('Not a checkbox or radio button');
|
||||
|
||||
let element: Element | undefined = node as Element;
|
||||
const element = node as Element;
|
||||
if (element.getAttribute('role') === 'checkbox')
|
||||
return element.getAttribute('aria-checked') === 'true';
|
||||
|
||||
if (element.nodeName === 'LABEL') {
|
||||
const forId = element.getAttribute('for');
|
||||
if (forId && element.ownerDocument)
|
||||
element = element.ownerDocument.querySelector(`input[id="${forId}"]`) || undefined;
|
||||
else
|
||||
element = element.querySelector('input[type=checkbox],input[type=radio]') || undefined;
|
||||
}
|
||||
if (element && element.nodeName === 'INPUT') {
|
||||
const type = element.getAttribute('type');
|
||||
if (type && (type.toLowerCase() === 'checkbox' || type.toLowerCase() === 'radio'))
|
||||
return (element as HTMLInputElement).checked;
|
||||
}
|
||||
throw new Error('Not a checkbox');
|
||||
const input = this.findLabelTarget(element);
|
||||
if (!input || input.nodeName !== 'INPUT')
|
||||
throw new Error('Not a checkbox or radio button');
|
||||
if (!['radio', 'checkbox'].includes((input as HTMLInputElement).type.toLowerCase()))
|
||||
throw new Error('Not a checkbox or radio button');
|
||||
return (input as HTMLInputElement).checked;
|
||||
}
|
||||
|
||||
setInputFiles(node: Node, payloads: { name: string, mimeType: string, buffer: string }[]) {
|
||||
|
@ -58,6 +58,23 @@ it('should check the box inside label w/o id', async ({page}) => {
|
||||
expect(await page.evaluate(() => window['checkbox'].checked)).toBe(true);
|
||||
});
|
||||
|
||||
it('should check the box outside shadow dom label', async ({page}) => {
|
||||
await page.setContent('<div></div>');
|
||||
await page.$eval('div', div => {
|
||||
const root = div.attachShadow({ mode: 'open' });
|
||||
const label = document.createElement('label');
|
||||
label.setAttribute('for', 'target');
|
||||
label.textContent = 'Click me';
|
||||
root.appendChild(label);
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('type', 'checkbox');
|
||||
input.setAttribute('id', 'target');
|
||||
root.appendChild(input);
|
||||
});
|
||||
await page.check('label');
|
||||
expect(await page.$eval('input', input => input.checked)).toBe(true);
|
||||
});
|
||||
|
||||
it('should check radio', async ({page}) => {
|
||||
await page.setContent(`
|
||||
<input type='radio'>one</input>
|
||||
|
@ -34,6 +34,24 @@ it('should fill input', async ({page, server}) => {
|
||||
expect(await page.evaluate(() => window['result'])).toBe('some value');
|
||||
});
|
||||
|
||||
it('should fill input with label', async ({page}) => {
|
||||
await page.setContent(`<label for=target>Fill me</label><input id=target>`);
|
||||
await page.fill('text=Fill me', 'some value');
|
||||
expect(await page.$eval('input', input => input.value)).toBe('some value');
|
||||
});
|
||||
|
||||
it('should fill input with label 2', async ({page}) => {
|
||||
await page.setContent(`<label>Fill me<input id=target></label>`);
|
||||
await page.fill('text=Fill me', 'some value');
|
||||
expect(await page.$eval('input', input => input.value)).toBe('some value');
|
||||
});
|
||||
|
||||
it('should fill textarea with label', async ({page}) => {
|
||||
await page.setContent(`<label for=target>Fill me</label><textarea id=target>hey</textarea>`);
|
||||
await page.fill('text=Fill me', 'some value');
|
||||
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('some value');
|
||||
});
|
||||
|
||||
it('should throw on unsupported inputs', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
for (const type of ['color', 'file']) {
|
||||
|
Loading…
Reference in New Issue
Block a user