Reputation: 14418
Given the following markup:
<label for="name">Name</label>
<input type="text" id="name" />
I want to fill the input value in a Playwright test. However, I prefer to follow a paradigm similar to Testing Library, which is using selectors like a real user would (usually visible text content). Therefore, I would like my Playwright selector to use the label text.
When I attempt the following I get an error element is not an <input>
because it selects the actual label element:
await page.fill('"Name"')
Is there another way to achieve this?
Note: I'm not keen on using the Playwright Testing Library package because it simply isn't mature enough. Also, my understanding is that Puppeteer and Playwright are virtually the same, hence why this question applies to both frameworks.
Upvotes: 6
Views: 15261
Reputation: 57135
You could .click()
the label to set focus on the input, then use page.keyboard.type
to type into the input.
Here's a minimal example in Puppeteer:
const puppeteer = require("puppeteer");
let browser;
(async () => {
browser = await puppeteer.launch({headless: true});
const [page] = await browser.pages();
await page.setContent(`
<label for="name">Name</label>
<input type="text" id="name" />
`);
await page.click("label[for='name']");
await page.keyboard.type("hello world");
console.log(await page.$eval("#name", e => e.value)); // => hello world
console.log(await page.evaluate(() => document.activeElement.value)); // same
})()
.catch(err => console.error(err))
.finally(() => browser?.close());
Note that although they seem superficially similar, Playwright and Puppeteer have diverged a fair bit as of the time of writing. Playwright has deprecated most of its Puppeteer-style API in favor of locators, so selection and manipulation are now quite different between the two libraries.
The above Puppeteer code pretty much works in Playwright at the time of writing, but an idiomatic, locator-based Playwright approach would be:
const playwright = require("playwright"); // ^1.30.1
let browser;
(async () => {
browser = await playwright.chromium.launch();
const page = await browser.newPage();
await page.setContent(`
<label for="name">Name</label>
<input type="text" id="name" />
`);
const input = page.getByLabel("name");
await input.click();
await page.keyboard.type("hello world");
console.log(await input.inputValue()); // => hello world
console.log(await page.evaluate(() => document.activeElement.value)); // same
})()
.catch(err => console.error(err))
.finally(() => browser?.close());
Addendum, August 2023: Puppeteer has now added support for their own experimental locators.
Upvotes: 5
Reputation: 31
Playwright has an exact locator for this page.getByLabel
Allows locating input elements by the text of the associated label. For example, this method will find the input by label text Password in the following DOM:
<label for="password-input">Password:</label>
<input id="password-input">
@paramtext — Text to locate the element for.
Upvotes: 3
Reputation: 1378
To expand on @ggorlen answer, and to make it even more similar to Testing Library, you can use the :has-text() pseudo-class to select the label by its accessible name:
await page.click('label:has-text("Name")');
await page.keyboard.type('hello world');
Upvotes: 2
Reputation: 235
When an input field and a label are correctly connected.
For example like this (using the for
attribute:
<label for="name">Name</label>
<input type="text" id="name" name="name" />
Then it's really easy to fill the input field using the label. It works like this for example:
await page.fill('label:has-text("name")', 'Peter');
The feature to fill an input via a selected label was added to Playwright in this pull request: https://github.com/microsoft/playwright/issues/3466
Upvotes: 2