Локаторы в Playwright
Locator — один из ключевых концептов в Playwright. Он представляет собой способ поиска элементов на странице.
В этой главе представлены рекомендуемые в Playwright методы поиска: поиск элементов по роли, тексту, метке и placeholder.
Что такое Locator
Locator — это абстрактный объект, который Playwright использует для поиска элементов на странице.
В отличие от традиционныхdocument.querySelector()способами, Locator в Playwright обладаетавтоматическим ожиданием и автоматическими повторными попыткамивозможностями.
Когда содержимое страницы динамически меняется, Locator будет постоянно заново находить элементы, пока не найдёт подходящий элемент или не истечёт время ожидания.
Пример
const button = page.getByRole('button', { name: 'Submit' });
// При выполнении действия Playwright автоматически ждёт, пока кнопка появится и станет доступной для действий
await button.click();
Как работают Locator
Когда вы создаёте Locator, Playwright только записывает правило поиска.
Когда вы вызываете.click()、.fill()действия илиexpect(locator)утверждения, Locator фактически начинает искать элементы.
Если элемент не удаётся найти, Locator будет повторять попытки до истечения времени ожидания операции.
Поиск по роли: getByRole()
getByRole()является наиболее рекомендуемым методом поиска в Playwright.
Он используетроль доступности элемента (ARIA Role)для поиска элементов, что наиболее близко к тому, как их воспринимают пользователи.
Базовое использование
Пример
const submitBtn = page.getByRole('button', { name: 'Submit' });
await submitBtn.click();
// Найти ссылку — по роли "link"
const homeLink = page.getByRole('link', { name: 'Главная' });
await homeLink.click();
// Найти заголовок — по роли "heading"
const mainTitle = page.getByRole('heading', { name: 'Добро пожаловать в EXAMPLE' });
// Найти текстовое поле — по роли "textbox"
const searchBox = page.getByRole('textbox', { name: 'Поиск' });
await searchBox.fill('Playwright');
Распространённые роли ARIA
| Роль | Соответствующие элементы HTML | Пример использования |
|---|---|---|
'button' | <button>、role="button" | getByRole('button', { name: '登录' }) |
'link' | <a> | getByRole('link', { name: '了解更多' }) |
'heading' | <h1>~<h6> | getByRole('heading', { name: 'EXAMPLE 教程' }) |
'textbox' | <input type="text">、<textarea> | getByRole('textbox', { name: '用户名' }) |
'checkbox' | <input type="checkbox"> | getByRole('checkbox', { name: '记住我' }) |
'radio' | <input type="radio"> | getByRole('radio', { name: '男' }) |
'combobox' | <select> | getByRole('combobox', { name: '城市' }) |
'img' | <img> | getByRole('img', { name: 'Logo' }) |
'list' | <ul>、<ol> | getByRole('list') |
'listitem' | <li> | getByRole('listitem') |
'navigation' | <nav> | getByRole('navigation') |
'table' | <table> | getByRole('table') |
Опция name
nameОпция используетдоступное имяэлемента, чтобы сузить область поиска.
Источники доступного имени включают: текстовое содержимое элемента,aria-labelатрибуты, связанные<label>и т. д.
Пример
page.getByRole('button', { name: 'Login' });
// Сопоставление по aria-label
page.getByRole('button', { name: 'Close dialog' });
// Сопоставление с помощью регулярного выражения
page.getByRole('link', { name: /EXAMPLE|rookie/ });
Параметры состояния
Кроме того,name,getByRoleон также поддерживает несколько параметров состояния:
Пример
page.getByRole('checkbox', { checked: true });
// Найти неактивную кнопку
page.getByRole('button', { disabled: true });
// Найти раскрытое выпадающее меню
page.getByRole('button', { expanded: true });
// Найти выбранную опцию
page.getByRole('option', { selected: true });
// Объединить несколько состояний
page.getByRole('checkbox', { name: 'Agree to the terms', checked: false });
Поиск по тексту: getByText()
getByText()Потекстовому содержимому элементадля поиска.
Пример
page.getByText('Welcome to EXAMPLE');
// Частичное совпадение (по умолчанию)
page.getByText('EXAMPLE');
// Принудительное точное совпадение
page.getByText('EXAMPLE', { exact: true });
// Совпадение по регулярному выражению
page.getByText(/EXAMPLE|rookie tutorial/);
getByTextОно сопоставляет полное текстовое содержимое элемента, поэтомуgetByText('EXAMPLE', { exact: true })оно будет сопоставлять только элементы, текстовое содержимое которых в точности равно "EXAMPLE".
По умолчанию, когда точное совпадение не используется, достаточно совпадения подстроки (например,getByText('EXAMPLE')оно сопоставит "Welcome to EXAMPLE").
Поиск по метке: getByLabel()
getByLabel()Используется для поиска<label>связанных элементов управления формы.
Пример
// <label for="username">Username</label>
// <input id="username" type="text" />
// Найти поле ввода по тексту метки
await page.getByLabel('Username').fill('example_user');
// Структура HTML:
// <label>Password <input type="password" /></label>
// Вложенные метки также допустимы
await page.getByLabel('Password').fill('password123');
Он также поддерживает{ exact: true }опции точного совпадения.
Поиск по плейсхолдеру: getByPlaceholder()
getByPlaceholder()С помощьюplaceholderатрибут placeholder для поиска полей ввода.
Пример
await page.getByPlaceholder('Please enter a search keyword').fill('Playwright');
// Структура HTML: <textarea placeholder="Tell us what you think..."></textarea>
await page.getByPlaceholder('Tell us what you think...').fill('The EXAMPLE tutorial is great!');
Выбор между getByLabel и getByPlaceholder
| Метод | Сценарий применения | Приоритет |
|---|---|---|
getByLabel() | Когда есть элемент <label> | Сначала используйте его |
getByPlaceholder() | Когда нет label, но есть placeholder | Второй вариант |
getByRole— самый рекомендуемый метод локализации в Playwright, потому что он имитирует, как пользователи определяют элементы по роли. Следующий —getByLabelиgetByPlaceholder. Рассматривайте его использование только когда нет семантических элементовgetByTestIdили CSS-селекторы.
getByAltText() для поиска изображений
getByAltText()С помощьюaltатрибута alt изображения для поиска<img>элемента <img>.
Пример
await expect(page.getByAltText('EXAMPLE Logo')).toBeVisible();
// Совпадение по регулярному выражению
await expect(page.getByAltText(/logo/i)).toBeVisible();
getByTitle() для поиска по атрибуту title
getByTitle()С помощьюtitleатрибута title элемента для поиска.
Пример
await page.getByTitle('Close current page').click();
getByAltTextиgetByTitleИспользуется реже; только когда у элемента есть явный атрибут alt или title и его нельзя найти другими способами.
getByTestId() для поиска по Test ID
getByTestId()ДаРезервный вариант, черезdata-testidатрибут для поиска элемента.
Пример
await page.getByTestId('submit-btn').click();
// Может содержать несколько совпадений (например, элементы списка)
const items = page.getByTestId('todo-item');
await expect(items).toHaveCount(3);
Настройка атрибута testId
Если в вашем проекте не используетсяdata-testid, вы можетеplaywright.config.tsнастройте имя атрибута в:
Пример
export default defineConfig({
использовать: {
// Используйте data-cy вместо data-testid (для миграции с Cypress)
testIdAttribute: 'data-cy',
// Или используйте id как testId
// testIdAttribute: 'id',
},
});
page.locator(): поиск по CSS и XPath
page.locator()Поддерживает CSS-селекторы и XPath, выступая в роли более гибкого запасного варианта.
Селекторы CSS
Пример
await page.locator('.submit-btn').click();
// По ID
await page.locator('#username').fill('example_user');
// По атрибуту
await page.locator('[data-type="primary"]').click();
// По комбинации тега + класса
await page.locator('button.primary').click();
// По связи родитель — потомок
await page.locator('nav a').first().click();
// CSS-селектор, содержащий определённый текст
await page.locator('button:has-text("Submit")').click();
Поиск по XPath
Пример
await page.locator('xpath=//button[@type="submit"]').click();
// Поиск по тексту
await page.locator('xpath=//h1[contains(text(), "EXAMPLE")]').click();
CSS-селекторы и XPath — крайняя мера; отдавайте предпочтение использованию
getByRole、getByTextсемантические методы локализации. CSS-селекторы склонны к поломке из-за рефакторинга стилей, а XPath обладает плохой читаемостью и более низкой производительностью.
Цепочка Locator
Locator в Playwright поддерживает цепочки, позволяя дополнительно фильтровать текущие результаты сопоставления.
.first()、.last()、.nth()
Пример
const firstItem = page.getByRole('listitem').first();
// Получить последний
const lastItem = page.getByRole('listitem').last();
// Получить второй (индекс начинается с 0)
const secondItem = page.getByRole('listitem').nth(1);
.filter() Фильтрация
Пример
page.getByRole('button').filter({ hasText: 'Сохранить' });
// Отфильтровать те, которые не содержат указанный текст
page.getByRole('listitem').filter({ hasNotText: 'Удалено' });
// Отфильтровать те, которые содержат указанный дочерний элемент
page.getByRole('listitem').filter({ has: page.getByRole('button') });
// Отфильтровать те, которые не содержат указанный дочерний элемент
page.getByRole('listitem').filter({ hasNot: page.getByTestId('badge') });
.locator() для поиска дочернего элемента
Пример
const list = page.getByTestId('todo-list');
const deleteBtn = list.locator('button.delete');
await deleteBtn.click();
// Многоуровневая вложенность
page
.getByRole('dialog')
.locator('div.content')
.locator('button')
.filter({ hasText: 'Confirm' })
.click();
.and() и .or() Логические комбинации (1.63+)
Пример
const btn = page.getByRole('button')
.и(page.getByText('Submit'));
// Удовлетворить любое из условий
const target = page.getByText('EXAMPLE')
.или(page.getByTitle('EXAMPLE'));
Сводка по приоритету стратегий локаторов
Упорядочено от наиболее рекомендуемых к наименее рекомендуемым:
| Приоритет | Метод | Условие использования |
|---|---|---|
| 1 (Наиболее рекомендуемый) | getByRole() | Элемент имеет явную ARIA-роль |
| 2 | getByLabel() | У элемента формы есть связанная метка |
| 3 | getByPlaceholder() | Поле ввода имеет заполнитель |
| 4 | getByText() | Элемент имеет четкое текстовое содержимое |
| 5 | getByAltText() | У изображения есть атрибут alt |
| 6 | getByTitle() | У элемента есть атрибут title |
| 7 (Резервный вариант) | getByTestId() | Когда не удаётся найти с помощью вышеуказанных методов |
| 8 (последнее средство) | locator() | CSS-селекторы / XPath |