Read a table row by its text
Find the one row that contains a known value, then act inside it.
You know a value in a table and want the row that holds it. Match every row by role, narrow to the one that contains the text, then scope further actions to that row.
Narrow rows with filter
getByRole('row') matches every row. filter(['hasText' => ...]) keeps only the rows whose text contains the string. Pair it with a value unique to the row you want.
use function Playwright\Testing\expect;
$row = $page->getByRole('row')->filter(['hasText' => 'ada@example.com']);
expect($row)->toHaveCount(1);
hasText is a substring match, so the value has to be specific enough to hit one row. Asserting toHaveCount(1) proves the filter isolated a single row before you act on it.
Act inside the matched row
Once you hold the row locator, scope the next locator to it. The button lookup only searches within that row.
use function Playwright\Testing\expect;
$row = $page->getByRole('row')->filter(['hasText' => 'ada@example.com']);
$row->getByRole('button', ['name' => 'Edit'])->click();
expect($page->getByRole('dialog'))->toBeVisible();
Scoping to $row avoids clicking the wrong Edit button in another row.
Read a cell from the row
To pull a value out of the row instead of acting on it, target the cell by role and read it.
$row = $page->getByRole('row')->filter(['hasText' => 'ada@example.com']);
$status = $row->getByRole('cell')->last()->textContent();
Pitfalls
- A
hasTextvalue that appears in several rows matches several rows. AsserttoHaveCount(1)first, or filter on a more specific value. filter()returns a new locator. It does not act by itself: chain an action or an assertion.
Expected result
The row locator resolves to exactly one row before you click or read inside it. The following action should affect that row only, not another row with the same button label.
Go next
- Extract text from matching elements: read every row instead of one
- Core concepts: role locators and scoping