I would add a test id to the <td>
with the error in it so you can select it directly. This is probably the easiest approach.
If it's not feasible to change the markup, I'd select by the text:
const playwright = require("playwright"); // ^1.42.1
const html = `<!DOCTYPE html><html><body><table>
<tr data-testid="sign-up-error" class="formvalidate_error password_error" style="display: table-row;">
<td></td>
<td class="error_msg">Password must be at least 10 characters long</td>
</tr></table></body></html>`;
let browser;
(async () => {
browser = await playwright.firefox.launch();
const page = await browser.newPage();
await page.setContent(html);
await page
.getByTestId("sign-up-error")
.getByRole("cell")
.getByText("Password must be at least 10 characters long")
.waitFor();
})()
.catch(err => console.error(err))
.finally(() => browser?.close());
Or by the child class, if you can't specify the text (even partially):
await page
.getByTestId("sign-up-error")
.locator("td.error_msg")
.waitFor();
Or using the :scope
pseudoselector to include the cell <td>
itself:
await page
.getByTestId("sign-up-error")
.getByRole("cell")
.locator(":scope.error_msg")
.waitFor();
A selector like "[style='display: table-row']"
is really brittle and strongly discouraged. Even adding a semicolon to the inline style attribute causes the selector to fail the ultra-strict exact match:
import {expect, test} from "@playwright/test"; // ^1.42.1
const htmlWithoutSemi = `<!DOCTYPE html><html><body><table>
<tr style="display: table-row"><td>asdf</td></tr>
</table></body></html>`;
const htmlWithSemi = `<!DOCTYPE html><html><body><table>
<tr style="display: table-row;"><td>asdf</td></tr>
</table></body></html>`;
test("works when there's no semicolon", async ({page}) => {
await page.setContent(htmlWithoutSemi);
await expect(page.locator("[style='display: table-row']")).toBeVisible();
});
test("fails when there's a semicolon", async ({page}) => {
test.fail(); // we expect this to fail, unfortunately
await page.setContent(htmlWithSemi);
await expect(page.locator("[style='display: table-row']")).toBeVisible();
});
You can use attribute selector variants like *=
and ~=
to avoid these failures, but these can open up other problems. Generally speaking, it's better to avoid CSS attributes regardless of whether you're testing or web scraping, particularly style=
attributes, as these can be very free-text, tend to change often and can be (correctly) moved to stylesheets without notice.