Цепочки и фильтрация локаторов
Когда на странице много похожих элементов, локаторы нужно уточнять — сужать поиск до одного нужного.
Фильтрация локатора — это уточнение, какой именно из похожих элементов нам нужен: по тексту внутри, по вложенности или по позиции.
Проблема: много одинаковых элементов
Представьте список товаров, где у каждого есть кнопка «В корзину». Простой локатор getByRole('button', { name: 'В корзину' }) найдёт сразу несколько кнопок — Playwright не поймёт, какую кликать, и тест упадёт с ошибкой «strict mode violation» (нашлось больше одного элемента). Это не баг, а защита: лучше явная ошибка, чем клик по случайной кнопке.
// ошибка, если таких кнопок несколько
await page.getByRole('button', { name: 'В корзину' }).click();Нужно сузить поиск. Есть несколько способов.
Вложенность: ищем внутри родителя
Если у каждого товара есть контейнер с названием, можно сначала найти нужный товар, а внутри него — кнопку. Локаторы соединяются цепочкой, и поиск второго идёт уже только внутри первого.
// карточка товара «Ноутбук», внутри неё — кнопка
await page
.getByRole('listitem')
.filter({ hasText: 'Ноутбук' })
.getByRole('button', { name: 'В корзину' })
.click();filter с hasText
filter({ hasText: '...' }) оставляет только те элементы, внутри которых есть нужный текст. Это самый частый способ выбрать нужную строку из списка или таблицы.
const row = page.getByRole('row').filter({ hasText: '[email protected]' });
await expect(row).toBeVisible();
await row.getByRole('button', { name: 'Удалить' }).click();Можно фильтровать и по наличию вложенного локатора — filter({ has: ... }), и даже исключать через hasNotText:
// строки, в которых есть отмеченный чекбокс
const selected = page
.getByRole('row')
.filter({ has: page.getByRole('checkbox', { checked: true }) });
// строки без слова «архив»
const active = page.getByRole('row').filter({ hasNotText: 'архив' });По позиции: first, last, nth
Когда нужен элемент по порядку, есть first(), last() и nth(i) (нумерация с нуля). Этим пользуются осторожно — позиция менее устойчива, чем текст: если порядок элементов изменится, тест начнёт проверять не то.
await page.getByRole('listitem').first().click(); // первый
await page.getByRole('listitem').last().click(); // последний
await page.getByRole('listitem').nth(2).click(); // третий (индекс 2)Проверка количества
Локатор, указывающий на несколько элементов, удобно проверять целиком — например, сколько товаров в списке. Это тоже web-first assertion: проверка дождётся нужного числа.
await expect(page.getByRole('listitem')).toHaveCount(5);| Приём | Зачем |
filter({ hasText }) | выбрать по тексту внутри — предпочтительно |
filter({ has }) | выбрать по вложенному элементу |
| цепочка локаторов | искать внутри родителя |
nth/first/last | по позиции — как крайний случай |
Итог
- Если локатор находит несколько элементов, Playwright требует уточнения (strict mode) — это защита от клика по случайному.
filter({ hasText })— главный способ выбрать нужный элемент по тексту внутри.- Цепочка локаторов ищет элемент внутри найденного родителя.
first/last/nth— по позиции, применять осторожно.