Локаторы: ищем как пользователь

Главный приём Playwright: находить элементы по их роли и тексту, а не по хрупкой разметке.

Локатор — это способ указать Playwright, какой элемент на странице нас интересует. Локатор не ищет элемент сразу, а описывает «как его найти, когда понадобится».

Философия: как пользователь

Пользователь не знает про CSS-классы и id. Он видит кнопку «Войти», поле «Email», ссылку «Подробнее». Playwright предлагает искать элементы так же — по роли и видимому тексту. Такие локаторы устойчивы: если дизайнер поменяет CSS-класс, тест не сломается, ведь кнопка по-прежнему называется «Войти».

Рекомендуемые локаторы по приоритету

Playwright советует выбирать локаторы в таком порядке предпочтения.

ЛокаторЧто находитПример элемента
getByRoleпо ARIA-роли и доступному именикнопка, ссылка, заголовок, чекбокс
getByLabelполе формы по тексту его меткиinput с label «Email»
getByTextэлемент по видимому текстуабзац, span с текстом
getByTestIdпо атрибуту data-testidкогда нет роли и текста

getByRole — главный инструмент

Роль — это семантика элемента: кнопка, ссылка, заголовок, поле ввода. getByRole находит элемент по роли и его доступному имени (тексту, который читает скринридер). Это самый надёжный способ, и он заодно проверяет доступность: если элемента нет в дереве доступности, его не «увидит» и скринридер.

// кнопка с текстом «Отправить»
await page.getByRole('button', { name: 'Отправить' }).click();

// ссылка «Главная»
await page.getByRole('link', { name: 'Главная' }).click();

// заголовок h1/h2 с текстом «Корзина»
await expect(page.getByRole('heading', { name: 'Корзина' })).toBeVisible();

getByLabel, getByText, getByTestId

Поля ввода удобно искать по их метке. Если в HTML есть <label>, связанная с <input>, то getByLabel найдёт именно это поле.

<label for="email">Email</label>
<input id="email" type="email">
await page.getByLabel('Email').fill('user@example.com');
await expect(page.getByText('Заказ оформлен')).toBeVisible();

getByTestId — запасной вариант, когда у элемента нет ни внятной роли, ни стабильного текста. Разработчик заранее добавляет в HTML атрибут data-testid:

<div data-testid="cart-total">1500 ₽</div>
await expect(page.getByTestId('cart-total')).toHaveText('1500 ₽');

CSS и XPath: почему избегать

CSS-селектор и XPath находят элемент по его положению в дереве разметки, а не по тому, что видит пользователь.

Playwright умеет искать и по CSS, и по XPath — это привычно тем, кто пришёл из Selenium. Но классы и структура DOM — это детали реализации: дизайнер переименовал класс .btn-primary, разработчик обернул кнопку в лишний <div> — и локатор ломается. Пользователь при этом ничего не заметил.

// Хрупко: завязано на классы и структуру
await page.locator('div.modal > div.footer > button.btn.btn-primary').click();

// Устойчиво: завязано на смысл
await page.getByRole('button', { name: 'Сохранить' }).click();

Особенно опасен XPath по индексам вида //table/tr[3]/td[2] — «третья строка, вторая ячейка». Такой локатор завязан на порядок: добавили строку — и тест проверяет уже не то, что нужно. Это один из самых частых источников флаки.

ЛокаторОценка
getByRole('button', { name: '...' })лучший выбор
input[name="email"]приемлемо (стабильный атрибут)
.btn.btn-primaryхрупко (классы стилей)
//div[2]/button[1]очень хрупко (позиция)

Полностью отказываться от CSS не нужно: если у элемента нет роли, текста и data-testid, CSS по осмысленному атрибуту (name, aria-label) приемлем. Правило простое: цепляйтесь за то, что отражает намерение, а не за оформление.

Итог

  • Локатор описывает, как найти элемент; приоритет: getByRolegetByLabelgetByTextgetByTestId.
  • Семантические локаторы устойчивы к смене CSS и заодно проверяют доступность.
  • CSS и XPath завязаны на детали разметки — это делает тесты хрупкими.
  • XPath по индексам (порядку) — частый источник флаки; CSS уместен лишь по стабильным смысловым атрибутам.
Проверьте себя
1. Какой локатор Playwright рекомендует использовать в первую очередь?
AgetByTestId
BgetByRole
Cлокатор по CSS-классу
Dлокатор по XPath
2. Почему CSS- и XPath-локаторы делают тесты хрупкими?
AОни слишком медленно выполняются
BPlaywright их не поддерживает
CОни завязаны на детали разметки, которые часто меняются
DОни не умеют находить кнопки
3. Когда уместно применять getByTestId?
AВсегда, это самый предпочтительный локатор
BТолько для кнопок и ссылок
CДля проверки заголовка вкладки
DКогда у элемента нет внятной роли и стабильного текста
4. Какой XPath особенно опасен с точки зрения флаки?
AXPath по индексам и порядку элементов, например //tr[3]/td[2]
BXPath по тексту элемента
CXPath по атрибуту aria-label
DЛюбой XPath работает одинаково надёжно