Playwright Python — справочник На главный сайт

Философия локаторов Playwright

Локаторы в Playwright — это центральная концепция для поиска элементов на странице. В отличие от Selenium, где используются «сырые» CSS/XPath селекторы, Playwright предлагает высокоуровневый API на основе семантических и атрибутивных методов. Такой подход делает тесты более читаемыми, устойчивыми к изменениям вёрстки и соответствующими лучшим практикам тестирования.

Ключевой принцип: Локатор в Playwright — это не просто строка-селектор, а объект Locator, который обладает собственным набором методов для действий, ожиданий и фильтрации. Локаторы вычисляются лениво (lazy evaluation) — реальный поиск по DOM происходит только при выполнении действия.

Playwright рекомендует использовать локаторы в следующем порядке приоритета:

  1. get_by_role() — по ARIA-роли (самый предпочтительный)
  2. get_by_label() — по label для полей ввода
  3. get_by_placeholder() — по placeholder
  4. get_by_text() — по текстовому содержимому
  5. get_by_test_id() — по тестовому идентификатору
  6. get_by_alt_text() — по alt-тексту для изображений
  7. 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 — только когда семантические методы не подходят. Комбинируйте локаторы через фильтрацию и логические операторы для точного выбора элементов.