📖 Теория: локаторы и проверки

← К оглавлению урока

⚡ Суть

  • Локатор — «ленивая» ссылка: ищется заново при каждом действии, поэтому устойчив к перерисовке DOM.
  • Приоритет: get_by_roleget_by_label/placeholder/textget_by_test_id → CSS/XPath (крайний случай).
  • Действия и expect() сами ждут готовности элемента.
  • Strict mode: если локатор нашёл больше одного элемента — действие падает с ошибкой (это защита, а не баг).

Что такое локатор

В Selenium find_element сразу возвращает элемент — «снимок» на момент вызова. В Playwright page.get_by_role(...) возвращает локатор — описание того, как найти элемент. Реальный поиск происходит в момент действия (click, fill) или проверки (expect). Поэтому локатор не «протухает»: если страница перерисовалась, элемент будет найден заново.

Встроенные локаторы (рекомендуемые)

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

# По роли (кнопки, ссылки, поля) — самый рекомендуемый
page.get_by_role("button", name="Войти")
page.get_by_role("link", name="Документация")
page.get_by_role("checkbox", name="Согласен")

# По связанному label (поля форм)
page.get_by_label("Пароль")

# По placeholder
page.get_by_placeholder("name@example.com")

# По видимому тексту
page.get_by_text("Заказ оформлен")
page.get_by_text("Войти", exact=True)   # точное совпадение

# По alt (картинки) и title
page.get_by_alt_text("Логотип")
page.get_by_title("Количество задач")

# По data-testid (когда роли/текста недостаточно)
page.get_by_test_id("submit-btn")
Почему именно так: роль и текст редко меняются при рефакторинге CSS-классов, поэтому такие тесты ломаются реже, чем тесты на .btn.btn-primary > span.

CSS и XPath — крайний случай

page.locator("css=button.primary")
page.locator(".product-card")        # CSS определяется автоматически
page.locator("//button[@type='submit']")  # XPath

Их используют, когда нет ни роли, ни лейбла, ни стабильного текста.

Фильтрация и сцепление

# Сцепление: внутри найденного элемента ищем дальше
page.get_by_role("listitem").filter(has_text="Молоко").get_by_role("button").click()

# По индексу
page.get_by_role("listitem").nth(1)
page.get_by_role("listitem").first
page.get_by_role("listitem").last

# has / has_not_text
page.get_by_role("listitem").filter(has_not_text="Нет в наличии")

Действия и actionability

Перед действием Playwright проверяет, что элемент готов: существует, видим, стабилен (не двигается), включён, не перекрыт. Эти проверки повторяются до таймаута — отсюда «авто-ожидание».

ДействиеЧто делает
click()Клик
fill("текст")Очистить и ввести в поле
check() / uncheck()Чекбокс/радио
select_option("value")Выбор в <select>
press("Enter")Нажатие клавиши
hover()Навести курсор

Web-first проверки expect()

from playwright.sync_api import expect

expect(page.get_by_text("Готово")).to_be_visible()
expect(page.locator(".count")).to_have_text("3")
expect(page.get_by_role("listitem")).to_have_count(5)
expect(page.get_by_label("Email")).to_have_value("a@b.com")

Каждая такая проверка повторяется до таймаута, пока условие не станет истинным. Это убирает «мигающие» падения.

Strict mode

Если локатор находит несколько элементов, а вы вызываете действие на одном — Playwright бросает ошибку strict mode violation. Это намеренно: тест должен однозначно указывать на элемент. Уточните локатор (name=, .filter(), .nth()).

⚠️ Полный список матчеров expect и опций локаторов — в официальной документации по assertions.