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

Главный приём 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('[email protected]');
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
Bлокатор по CSS-классу
CgetByRole
Dлокатор по XPath
2. Почему CSS- и XPath-локаторы делают тесты хрупкими?
AОни слишком медленно выполняются
BОни завязаны на детали разметки, которые часто меняются
CPlaywright их не поддерживает
DОни не умеют находить кнопки
3. Когда уместно применять getByTestId?
AВсегда, это самый предпочтительный локатор
BКогда у элемента нет внятной роли и стабильного текста
CТолько для кнопок и ссылок
DДля проверки заголовка вкладки
4. Какой XPath особенно опасен с точки зрения флаки?
AXPath по тексту элемента
BXPath по индексам и порядку элементов, например //tr[3]/td[2]
CXPath по атрибуту aria-label
DЛюбой XPath работает одинаково надёжно
Поддержать проект