Локаторы (Locators)
Полное руководство по локаторам Playwright: get_by_role, get_by_text, get_by_label, get_by_test_id, фильтрация, цепочки и комбинации локаторов.
Философия локаторов Playwright
Локаторы в Playwright — это центральная концепция для поиска элементов на странице. В отличие от Selenium, где используются «сырые» CSS/XPath селекторы, Playwright предлагает высокоуровневый API на основе семантических и атрибутивных методов. Такой подход делает тесты более читаемыми, устойчивыми к изменениям вёрстки и соответствующими лучшим практикам тестирования.
Ключевой принцип: Локатор в Playwright — это не просто строка-селектор, а объект Locator, который обладает собственным набором методов для действий, ожиданий и фильтрации. Локаторы вычисляются лениво (lazy evaluation) — реальный поиск по DOM происходит только при выполнении действия.
Playwright рекомендует использовать локаторы в следующем порядке приоритета:
- get_by_role() — по ARIA-роли (самый предпочтительный)
- get_by_label() — по label для полей ввода
- get_by_placeholder() — по placeholder
- get_by_text() — по текстовому содержимому
- get_by_test_id() — по тестовому идентификатору
- get_by_alt_text() — по alt-тексту для изображений
- get_by_title() — по атрибуту title
page.get_by_role() — Поиск по ARIA-роли
Метод get_by_role() находит элементы по их ARIA-роли. Это самый рекомендуемый способ поиска, так как он опирается на семантику страницы, а не на её вёрстку. Playwright поддерживает все стандартные ARIA-роли.
# Поиск по роли button page.get_by_role("button").click() # Поиск по роли button с фильтром по имени (accessible name) page.get_by_role("button", name="Submit").click() # Поиск текстового поля page.get_by_role("textbox", name="Email").fill("user@example.com") # Поиск ссылки page.get_by_role("link", name="Подробнее").click() # Поиск заголовка heading = page.get_by_role("heading", name="Добро пожаловать") # Поиск изображения page.get_by_role("img", name="Логотип") # Поиск элемента списка items = page.get_by_role("listitem") # Поиск выпадающего списка page.get_by_role("combobox", name="Страна").select_option("RU")
Основные ARIA-роли
| Роль | HTML-элементы | Описание |
|---|---|---|
button | <button>, <input type="button">, role="button" | Кнопки |
textbox | <input>, <textarea> | Поля ввода текста |
link | <a>, role="link" | Ссылки |
heading | <h1>–<h6>, role="heading" | Заголовки |
img | <img>, role="img" | Изображения |
checkbox | <input type="checkbox">, role="checkbox" | Чекбоксы |
radio | <input type="radio">, role="radio" | Радиокнопки |
combobox | <select>, role="combobox" | Выпадающие списки |
listitem | <li>, role="listitem" | Элементы списка |
list | <ul>, <ol>, role="list" | Списки |
table | <table>, role="table" | Таблицы |
dialog | <dialog>, role="dialog" | Диалоговые окна |
Фильтр по имени (name)
Параметр name позволяет отфильтровать элементы по их доступному имени (accessible name), которое формируется из:
- Текстового содержимого элемента (для кнопок, ссылок, заголовков)
- Атрибута
aria-label - Атрибута
aria-labelledby - Связанного
<label>для полей ввода - Атрибута
altдля изображений - Атрибута
title
# Точное совпадение по имени (по умолчанию — подстрока) page.get_by_role("button", name="Submit", exact=True)
page.get_by_text() — Поиск по тексту
Метод для поиска элементов по их текстовому содержимому. По умолчанию ищет как подстроку (текст элемента содержит указанный текст).
# Поиск по тексту (подстрока) page.get_by_text("Welcome") # Поиск по тексту внутри кнопки page.get_by_text("Submit form").click() # Точное совпадение текста page.get_by_text("Submit", exact=True) # Поиск по регулярному выражению page.get_by_text(re.compile(r"Цена:\s*\d+ руб")) # Поиск всех элементов, содержащих текст "Item" items = page.get_by_text("Item").all()
Совет: get_by_text() часто используется в цепочках с другими локаторами для фильтрации. Например: page.get_by_role("listitem").get_by_text("Важный") найдёт элемент списка, содержащий текст «Важный».
page.get_by_label() — Поиск по label
Находит элементы по тексту связанного <label>. Идеально подходит для полей ввода в формах.
# Поиск поля ввода по тексту label page.get_by_label("Email").fill("user@example.com") # Поиск поля по label с точным совпадением page.get_by_label("Имя пользователя", exact=True) # Поиск чекбокса по label page.get_by_label("Я согласен с условиями").check() # Поиск radio по label page.get_by_label("Вариант 1").check()
page.get_by_placeholder() — Поиск по placeholder
# Поиск поля по placeholder page.get_by_placeholder("Введите email").fill("email@example.com") # Точное совпадение placeholder page.get_by_placeholder("Поиск...", exact=True)
page.get_by_alt_text() — Поиск по alt-тексту
# Поиск изображения по alt-тексту page.get_by_alt_text("Логотип компании").click() # Поиск с точным совпадением page.get_by_alt_text("Фото профиля", exact=True)
page.get_by_title() — Поиск по атрибуту title
# Поиск элемента по атрибуту title page.get_by_title("Закрыть").click() # Поиск с точным совпадением page.get_by_title("Настройки аккаунта", exact=True)
page.get_by_test_id() — Поиск по data-testid
Метод для поиска элементов по тестовому идентификатору. По умолчанию ищет по атрибуту data-testid, но атрибут можно настроить через BrowserContext или тестовую конфигурацию.
# Поиск по data-testid page.get_by_test_id("submit-button").click() # Поиск по кастомному атрибуту (если настроено) # В конфигурации: set_default_test_id_attribute("data-cy") page.get_by_test_id("login-form") # Пример с кастомным атрибутом через контекст context = browser.new_context() context.set_default_test_id_attribute("data-qa") page = context.new_page() page.get_by_test_id("header") # Ищет по data-qa="header"
Позиционирование: first, last, nth()
Когда несколько элементов соответствуют локатору, можно выбрать конкретный элемент по позиции.
# Первый элемент page.get_by_role("listitem").first.click() # Последний элемент page.get_by_role("listitem").last.click() # N-ный элемент (0-based) page.get_by_role("listitem").nth(2).click() # Третий элемент # Первый элемент в коллекции page.get_by_role("button").first.click()
Важно: Использование first, last и nth() может сделать тест хрупким, если порядок элементов изменится. Предпочитайте более специфичные локаторы с фильтрацией.
Фильтрация: filter(has_text) и filter(has)
Метод filter() позволяет уточнить локатор, отфильтровав элементы по тексту или вложенным локаторам.
# Фильтрация по тексту (подстрока) page.get_by_role("listitem").filter(has_text="Важный") # Фильтрация по тексту с точным совпадением page.get_by_role("listitem").filter(has_text="Важный элемент", exact=True) # Фильтрация по вложенному локатору page.get_by_role("row").filter( has=page.get_by_role("cell", name="Активен") ) # Комбинация фильтрации page.get_by_role("listitem").filter( has_text="2024", has=page.get_by_role("checkbox") )
Логические комбинации: and_() и or_()
Позволяют комбинировать несколько локаторов с логическими операциями И и ИЛИ.
# Локатор И (элемент должен соответствовать обоим условиям) page.get_by_role("button").and_(page.get_by_title("Отправить")) # Локатор ИЛИ (элемент может соответствовать любому из условий) page.get_by_role("button").or_(page.get_by_role("link")) # Сложная комбинация: кнопка с текстом "Save" ИЛИ "Submit" (page.get_by_role("button", name="Save") .or_(page.get_by_role("button", name="Submit")) .click())
locator.all() — Все совпадения
Возвращает список всех элементов, соответствующих локатору. Полезно для проверки количества элементов или итерации.
# Получение всех элементов списка items = page.get_by_role("listitem").all() print(f"Найдено {len(items)} элементов") # Проверка количества элементов expect(page.get_by_role("listitem")).to_have_count(5) # Итерация по элементам for item in page.get_by_role("listitem").all(): print(item.text_content()) # all() с фильтрацией active_items = page.get_by_role("listitem").filter( has=page.get_by_role("checkbox", name="Активен") ).all()
Цепочки локаторов
Локаторы можно объединять в цепочки для более точного поиска. Каждый последующий метод уточняет выборку в контексте предыдущего локатора.
# Цепочка: элемент списка, содержащий определённый текст page.get_by_role("listitem").filter(has_text="Проект Alpha") # Цепочка: внутри определённого раздела sidebar = page.get_by_role("region", name="Навигация") sidebar.get_by_role("link", name="Настройки").click() # Цепочка: строка таблицы с определённым значением, затем ячейка row = page.get_by_role("row").filter(has_text="Заказ #42") row.get_by_role("cell", name="Статус") # Глубокая вложенность modal = page.get_by_role("dialog", name="Подтверждение") modal.get_by_role("button", name="Да").click()
Практические примеры
Пример: работа со списком задач
def test_todo_list(page): page.goto("https://example.com/todos") # Найти задачу "Купить продукты" и отметить её todo_item = page.get_by_role("listitem").filter(has_text="Купить продукты") todo_item.get_by_role("checkbox").check() # Проверить, что задача отмечена expect(todo_item.get_by_role("checkbox")).to_be_checked() # Найти все активные (невыполненные) задачи active_items = page.get_by_role("listitem").filter( has=page.get_by_role("checkbox").and_(page.locator(":not(:checked)")) ) expect(active_items).to_have_count(3)
Пример: форма регистрации
def test_registration_form(page): page.goto("https://example.com/register") # Заполнение формы с использованием разных локаторов page.get_by_label("Имя").fill("Иван") page.get_by_label("Email").fill("ivan@example.com") page.get_by_label("Пароль").fill("StrongPass1!") page.get_by_placeholder("Повторите пароль").fill("StrongPass1!") page.get_by_label("Страна").select_option("RU") page.get_by_role("checkbox", name="Согласен").check() # Отправка page.get_by_role("button", name="Зарегистрироваться").click() # Проверка успешной регистрации expect(page.get_by_text("Регистрация завершена")).to_be_visible()
Пример: проверка таблицы
def test_data_table(page): page.goto("https://example.com/data") # Найти строку с определённым ID target_row = page.get_by_role("row").filter(has_text="ORD-0042") # Получить значение ячейки в этой строке status_cell = target_row.get_by_role("cell", name="Статус") expect(status_cell).to_have_text("Доставлено") # Проверить количество строк в таблице rows = page.get_by_role("row").all() assert len(rows) == 11 # 10 строк данных + заголовок
Заключение
Локаторы Playwright — это мощный и гибкий инструмент для поиска элементов. Используйте семантические методы (get_by_role, get_by_label, get_by_text) как основной способ поиска, а CSS/XPath — только когда семантические методы не подходят. Комбинируйте локаторы через фильтрацию и логические операторы для точного выбора элементов.