Spec-Zone.ru › Playwright

Локаторы

Введение​

Локаторы — ключевой компонент автоматического ожидания и повторных попыток в Playwright. Проще говоря, локаторы позволяют находить элемент или элементы на странице в любой момент.

Краткое руководство​

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

  • page.getByRole() для поиска по явным и неявным атрибутам доступности.
  • page.getByText() для поиска по тексту.
  • page.getByLabel() для поиска элемента управления формы по тексту связанной с ним метки.
  • page.getByPlaceholder() для поиска поля ввода по тексту-подсказке.
  • page.getByAltText() для поиска элемента, обычно изображения, по альтернативному тексту.
  • page.getByTitle() для поиска элемента по атрибуту title.
  • page.getByTestId() для поиска элемента по его атрибуту data-testid (можно настроить использование других атрибутов).
await page.getByLabel('User Name').fill('John');

await page.getByLabel('Password').fill('secret-password');

await page.getByRole('button', { name: 'Sign in' }).click();

await expect(page.getByText('Welcome, John!')).toBeVisible();

Поиск элементов​

В Playwright есть несколько встроенных локаторов. Чтобы тесты были устойчивыми, рекомендуем отдавать предпочтение атрибутам, видимым пользователю, и явным соглашениям, например page.getByRole().

Рассмотрим следующую структуру DOM.

http://localhost:3000
<button>Sign in</button>

Найдите элемент по его роли button и имени «Войти».

await page.getByRole('button', { name: 'Sign in' }).click();
Примечание

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

При каждом использовании локатора для выполнения действия на странице находится актуальный элемент DOM. В приведённом ниже фрагменте базовый элемент DOM будет найден дважды — перед каждым действием. Это означает, что если между вызовами DOM изменится из-за повторного рендеринга, будет использован новый элемент, соответствующий локатору.

const locator = page.getByRole('button', { name: 'Sign in' });

await locator.hover();
await locator.click();

Обратите внимание: все методы создания локаторов, например page.getByLabel(), также доступны в классах Locator и FrameLocator, поэтому их можно объединять в цепочки и постепенно уточнять локатор.

const locator = page
    .frameLocator('#my-frame')
    .getByRole('button', { name: 'Sign in' });

await locator.click();

Поиск по роли​

Локатор page.getByRole() отражает то, как пользователи и вспомогательные технологии воспринимают страницу, например считают ли они элемент кнопкой или флажком. При поиске по роли обычно следует также указывать доступное имя, чтобы локатор находил нужный элемент.

Рассмотрим следующую структуру DOM.

http://localhost:3000

Регистрация


<h3>Sign up</h3>
<label>
  <input type="checkbox" /> Subscribe
</label>
<br/>
<button>Submit</button>

Каждый элемент можно найти по его неявной роли:

await expect(page.getByRole('heading', { name: 'Sign up' })).toBeVisible();

await page.getByRole('checkbox', { name: 'Subscribe' }).check();

await page.getByRole('button', { name: /submit/i }).click();

Локаторы ролей включают кнопки, флажки, заголовки, ссылки, списки, таблицы и многое другое и соответствуют спецификациям W3C для ролей ARIA, атрибутов ARIA и доступных имён. Обратите внимание, что многие элементы HTML, например <button>, имеют неявно заданную роль, распознаваемую локатором ролей.

Обратите внимание: локаторы ролей не заменяют аудит доступности и тесты на соответствие стандартам, а лишь позволяют заранее выявлять несоответствия рекомендациям ARIA.

Когда использовать локаторы ролей

Рекомендуем в первую очередь использовать локаторы ролей для поиска элементов, поскольку они лучше всего отражают то, как страницу воспринимают пользователи и вспомогательные технологии.

Поиск по метке​

У большинства элементов управления формы есть специальные метки, которые удобно использовать при работе с формой. В этом случае можно найти элемент управления по связанной с ним метке с помощью page.getByLabel().

Рассмотрим следующую структуру DOM.

http://localhost:3000
<label>Password <input type="password" /></label>

Найдите поле ввода по тексту метки и введите в него значение:

await page.getByLabel('Password').fill('secret');
Когда использовать локаторы меток

Используйте этот локатор для поиска полей формы.

Поиск по тексту-подсказке​

У полей ввода может быть атрибут placeholder, подсказывающий пользователю, какое значение нужно ввести. Такое поле можно найти с помощью page.getByPlaceholder().

Рассмотрим следующую структуру DOM.

http://localhost:3000
<input type="email" placeholder="name@example.com" />

Найдите поле ввода по тексту-подсказке и введите в него значение:

await page
    .getByPlaceholder('name@example.com')
    .fill('playwright@microsoft.com');
Когда использовать локаторы текста-подсказки

Используйте этот локатор для поиска элементов формы без меток, но с текстом-подсказкой.

Поиск по тексту​

Найдите элемент по содержащемуся в нём тексту. С помощью page.getByText() можно искать по подстроке, точному совпадению или регулярному выражению.

Рассмотрим следующую структуру DOM.

http://localhost:3000
Добро пожаловать, Иван
<span>Welcome, John</span>

Найдите элемент по содержащемуся в нём тексту:

await expect(page.getByText('Welcome, John')).toBeVisible();

Укажите точное совпадение:

await expect(page.getByText('Welcome, John', { exact: true })).toBeVisible();

Выполните поиск с помощью регулярного выражения:

await expect(page.getByText(/welcome, [A-Za-z]+$/i)).toBeVisible();
Примечание

При поиске по тексту пробелы всегда нормализуются, даже при точном совпадении. Например, несколько пробелов заменяются одним, переносы строк — пробелами, а начальные и конечные пробелы игнорируются.

Когда использовать текстовые локаторы

Рекомендуем использовать текстовые локаторы для поиска неинтерактивных элементов, например div, span, p и т. д. Для интерактивных элементов, таких как button, a, input и т. д., используйте локаторы ролей.

Также можно отфильтровать элементы по тексту. Это может быть полезно, если нужно найти определённый элемент в списке.

Поиск по альтернативному тексту​

У всех изображений должен быть атрибут alt с описанием изображения. Изображение можно найти по альтернативному тексту с помощью page.getByAltText().

Рассмотрим следующую структуру DOM.

http://localhost:3000
playwright logo
<img alt="playwright logo" src="/img/playwright-logo.svg" width="100" />

Найдите изображение по альтернативному тексту и нажмите на него:

await page.getByAltText('playwright logo').click();
Когда использовать локаторы альтернативного текста

Используйте этот локатор для элементов, поддерживающих альтернативный текст, например img и area.

Поиск по заголовку​

Найдите элемент с соответствующим атрибутом title с помощью page.getByTitle().

Рассмотрим следующую структуру DOM.

http://localhost:3000
25 проблем
<span title='Issues count'>25 issues</span>

Найдите элемент по тексту заголовка и проверьте количество проблем:

await expect(page.getByTitle('Issues count')).toHaveText('25 issues');
Когда использовать локаторы заголовков

Используйте этот локатор, если у элемента есть атрибут title.

Поиск по идентификатору теста​

Поиск по идентификаторам тестов — самый надёжный способ тестирования: даже если текст или роль атрибута изменятся, тест всё равно пройдёт. Специалисты по обеспечению качества и разработчики должны задавать явные идентификаторы тестов и искать элементы с помощью page.getByTestId(). Однако идентификаторы тестов не видны пользователям. Если для вас важны роль или текстовое значение, рассмотрите пользовательские локаторы, например локаторы ролей и текстовые локаторы.

Рассмотрим следующую структуру DOM.

http://localhost:3000
<button data-testid="directions">Itinéraire</button>

Найдите элемент по его идентификатору теста:

await page.getByTestId('directions').click();
Когда использовать локаторы идентификаторов тестов

Идентификаторы тестов также можно использовать, если вы решили применять эту методику или не можете найти элемент по роли или тексту.

Настройка пользовательского атрибута идентификатора теста​

По умолчанию page.getByTestId() ищет элементы по атрибуту data-testid, но это можно настроить в конфигурации тестов или вызвав selectors.setTestIdAttribute().

Укажите пользовательский атрибут данных в качестве идентификатора теста.

import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    testIdAttribute: 'data-pw'
  }
});

Теперь в HTML можно использовать data-pw в качестве идентификатора теста вместо атрибута по умолчанию data-testid.

http://localhost:3000
<button data-pw="directions">Itinéraire</button>

Затем найдите элемент обычным способом:

await page.getByTestId('directions').click();

Поиск с помощью CSS или XPath​

Если вам совершенно необходимо использовать локаторы CSS или XPath, воспользуйтесь page.locator(), чтобы создать локатор с селектором, описывающим способ поиска элемента на странице. Playwright поддерживает селекторы CSS и XPath и автоматически определяет их, если опустить префикс css= или xpath=.

await page.locator('css=button').click();
await page.locator('xpath=//button').click();

await page.locator('button').click();
await page.locator('//button').click();

Селекторы XPath и CSS могут зависеть от структуры DOM или реализации. Такие селекторы могут перестать работать при изменении структуры DOM. Приведённые ниже длинные цепочки CSS или XPath — пример плохой практики, которая приводит к нестабильным тестам:

await page.locator(
    '#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input'
).click();

await page
    .locator('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input')
    .click();
Когда это использовать

Не рекомендуем использовать CSS и XPath: DOM часто меняется, из-за чего тесты становятся неустойчивыми. Вместо этого попробуйте создать локатор, который соответствует тому, как пользователь воспринимает страницу, например локатор роли, или задайте явное соглашение для тестирования с помощью идентификаторов тестов.

Поиск в Shadow DOM​

По умолчанию все локаторы Playwright работают с элементами в Shadow DOM. Исключения:

  • При поиске с помощью XPath обход теневых корней не выполняется.
  • Теневые корни в закрытом режиме не поддерживаются.

Рассмотрим следующий пример с пользовательским веб-компонентом:

<x-details role=button aria-expanded=true aria-controls=inner-details>
  <div>Title</div>
  #shadow-root
    <div id=inner-details>Details</div>
</x-details>

Искать элементы можно так же, как если бы теневого корня вовсе не было.

Чтобы нажать <div>Details</div>:

await page.getByText('Details').click();
<x-details role=button aria-expanded=true aria-controls=inner-details>
  <div>Title</div>
  #shadow-root
    <div id=inner-details>Details</div>
</x-details>

Чтобы нажать <x-details>:

await page.locator('x-details', { hasText: 'Details' }).click();
<x-details role=button aria-expanded=true aria-controls=inner-details>
  <div>Title</div>
  #shadow-root
    <div id=inner-details>Details</div>
</x-details>

Чтобы убедиться, что <x-details> содержит текст «Подробности»:

await expect(page.locator('x-details')).toContainText('Details');

Фильтрация локаторов​

Рассмотрим следующую структуру DOM. Нам нужно нажать кнопку покупки на карточке второго товара. Есть несколько способов отфильтровать локаторы и найти нужный элемент.

http://localhost:3000
  • Товар 1

  • Товар 2

<ul>
  <li>
    <h3>Product 1</h3>
    <button>Add to cart</button>
  </li>
  <li>
    <h3>Product 2</h3>
    <button>Add to cart</button>
  </li>
</ul>

Фильтрация по тексту​

Локаторы можно фильтровать по тексту с помощью метода locator.filter(). Он ищет указанную строку внутри элемента, в том числе в дочерних элементах, без учёта регистра. Также можно передать регулярное выражение.

await page
    .getByRole('listitem')
    .filter({ hasText: 'Product 2' })
    .getByRole('button', { name: 'Add to cart' })
    .click();

Используйте регулярное выражение:

await page
    .getByRole('listitem')
    .filter({ hasText: /Product 2/ })
    .getByRole('button', { name: 'Add to cart' })
    .click();

Фильтрация по отсутствию текста​

Также можно отфильтровать элементы, не содержащие текст:

// 5 in-stock items
await expect(page.getByRole('listitem').filter({ hasNotText: 'Out of stock' })).toHaveCount(5);

Фильтрация по дочернему или вложенному элементу​

Локаторы позволяют выбирать только те элементы, у которых есть или нет вложенный элемент, соответствующий другому локатору. Поэтому фильтровать можно по любому другому локатору, например locator.getByRole(), locator.getByTestId(), locator.getByText() и т. д.

http://localhost:3000
  • Товар 1

  • Товар 2

<ul>
  <li>
    <h3>Product 1</h3>
    <button>Add to cart</button>
  </li>
  <li>
    <h3>Product 2</h3>
    <button>Add to cart</button>
  </li>
</ul>
await page
    .getByRole('listitem')
    .filter({ has: page.getByRole('heading', { name: 'Product 2' }) })
    .getByRole('button', { name: 'Add to cart' })
    .click();

Также можно проверить карточку товара, чтобы убедиться, что она только одна:

await expect(page
    .getByRole('listitem')
    .filter({ has: page.getByRole('heading', { name: 'Product 2' }) }))
    .toHaveCount(1);

Фильтрующий локатор должен быть относительным к исходному локатору; поиск начинается с элемента, найденного исходным локатором, а не с корня документа. Поэтому следующий вариант не сработает: поиск фильтрующего локатора начнётся с элемента списка <ul>, который находится вне элемента списка <li>, найденного исходным локатором:

// ✖ WRONG
await expect(page
    .getByRole('listitem')
    .filter({ has: page.getByRole('list').getByText('Product 2') }))
    .toHaveCount(1);

Фильтрация по отсутствию дочернего или вложенного элемента​

Также можно отфильтровать элементы, внутри которых нет соответствующего элемента.

await expect(page
    .getByRole('listitem')
    .filter({ hasNot: page.getByText('Product 2') }))
    .toHaveCount(1);

Обратите внимание: внутренний локатор ищет элементы внутри внешнего, а не от корня документа.

Операторы локаторов​

Поиск внутри локатора​

Можно объединять в цепочки методы, создающие локатор, например page.getByText() или locator.getByRole(), чтобы ограничить поиск определённой частью страницы.

В этом примере сначала создаётся локатор с именем product, который находит элемент по роли listitem. Затем мы фильтруем его по тексту. Локатор product можно использовать повторно: найти кнопку по роли и нажать на неё, а затем проверить с помощью утверждения, что существует только один товар с текстом «Товар 2».

const product = page.getByRole('listitem').filter({ hasText: 'Product 2' });

await product.getByRole('button', { name: 'Add to cart' }).click();

await expect(product).toHaveCount(1);

Также можно объединить два локатора в цепочку, например, чтобы найти кнопку «Сохранить» внутри определённого диалогового окна:

const saveButton = page.getByRole('button', { name: 'Save' });
// ...
const dialog = page.getByTestId('settings-dialog');
await dialog.locator(saveButton).click();

Одновременное соответствие двум локаторам​

Метод locator.and() уточняет существующий локатор, добавляя соответствие ещё одному локатору. Например, можно объединить page.getByRole() и page.getByTitle(), чтобы найти элемент по роли и заголовку.

const button = page.getByRole('button').and(page.getByTitle('Subscribe'));

Соответствие одному из двух альтернативных локаторов​

Если нужно найти один из двух или более элементов, но неизвестно, какой именно появится, используйте locator.or(), чтобы создать локатор, соответствующий любому одному или обоим вариантам.

Например, вы хотите нажать кнопку «Новое письмо», но иногда вместо неё появляется диалоговое окно настроек безопасности. В этом случае можно дождаться появления либо кнопки «Новое письмо», либо диалогового окна и действовать соответственно.

Примечание

Если на экране появятся и кнопка «Новое письмо», и диалоговое окно безопасности, локатор «or» найдёт оба элемента, что может привести к ошибке нарушения строгого режима. В этом случае можно использовать locator.first(), чтобы найти только один из них.

const newEmail = page.getByRole('button', { name: 'New' });
const dialog = page.getByText('Confirm security settings');
await expect(newEmail.or(dialog).first()).toBeVisible();
if (await dialog.isVisible())
  await page.getByRole('button', { name: 'Dismiss' }).click();
await newEmail.click();

Поиск только видимых элементов​

Примечание

Обычно лучше найти более надёжный способ однозначно определить элемент, чем проверять его видимость.

Рассмотрим страницу с двумя кнопками: первая невидима, а вторая видима.

<button style='display: none'>Invisible</button>
<button>Visible</button>
  • Этот вариант найдёт обе кнопки и вызовет ошибку нарушения строгого режима:

    await page.locator('button').click();
  • Этот вариант найдёт только вторую кнопку, поскольку она видима, и нажмёт на неё.

    await page.locator('button').visible().click();

Чтобы найти невидимые элементы, используйте locator.filter() и установите для параметра visible значение false.

Списки​

Подсчёт элементов в списке​

С помощью утверждений для локаторов можно подсчитать количество элементов в списке.

Рассмотрим следующую структуру DOM:

http://localhost:3000
  • яблоко
  • банан
  • апельсин
<ul>
  <li>apple</li>
  <li>banana</li>
  <li>orange</li>
</ul>

Используйте утверждение для подсчёта, чтобы проверить, что в списке 3 элемента.

await expect(page.getByRole('listitem')).toHaveCount(3);

Проверка всего текста в списке​

С помощью утверждений для локаторов можно проверить весь текст в списке.

Рассмотрим следующую структуру DOM:

http://localhost:3000
  • яблоко
  • банан
  • апельсин
<ul>
  <li>apple</li>
  <li>banana</li>
  <li>orange</li>
</ul>

Используйте expect(locator).toHaveText(), чтобы проверить, что в списке есть текст «яблоко», «банан» и «апельсин».

await expect(page
    .getByRole('listitem'))
    .toHaveText(['apple', 'banana', 'orange']);

Получение определённого элемента​

Есть несколько способов получить определённый элемент списка.

Поиск по тексту​

Используйте метод page.getByText(), чтобы найти элемент списка по его тексту, а затем нажмите на него.

Рассмотрим следующую структуру DOM:

http://localhost:3000
  • яблоко
  • банан
  • апельсин
<ul>
  <li>apple</li>
  <li>banana</li>
  <li>orange</li>
</ul>

Найдите элемент по его тексту и нажмите на него.

await page.getByText('orange').click();

Фильтрация по тексту​

Используйте метод locator.filter(), чтобы найти определённый элемент в списке.

Рассмотрим следующую структуру DOM:

http://localhost:3000
  • яблоко
  • банан
  • апельсин
<ul>
  <li>apple</li>
  <li>banana</li>
  <li>orange</li>
</ul>

Найдите элемент по роли «элемент списка», затем отфильтруйте по тексту «апельсин» и нажмите на него.

await page
    .getByRole('listitem')
    .filter({ hasText: 'orange' })
    .click();

Поиск по идентификатору теста​

Используйте метод page.getByTestId(), чтобы найти элемент в списке. Если идентификатор теста ещё не задан, возможно, потребуется изменить HTML и добавить его.

Рассмотрим следующую структуру DOM:

http://localhost:3000
  • яблоко
  • банан
  • апельсин
<ul>
  <li data-testid='apple'>apple</li>
  <li data-testid='banana'>banana</li>
  <li data-testid='orange'>orange</li>
</ul>

Найдите элемент по идентификатору теста «orange» и нажмите на него.

await page.getByTestId('orange').click();

Поиск по порядковому номеру элемента​

Если список состоит из одинаковых элементов и различить их можно только по порядку, выберите определённый элемент с помощью locator.first(), locator.last() или locator.nth().

const banana = await page.getByRole('listitem').nth(1);

Однако используйте этот метод с осторожностью. Страница может измениться, и тогда локатор укажет на совсем другой элемент. Вместо этого постарайтесь создать уникальный локатор, соответствующий критериям строгого режима.

Объединение фильтров в цепочки​

Если элементы похожи друг на друга, используйте метод locator.filter(), чтобы выбрать нужный. Можно также объединить несколько фильтров в цепочку и уточнить выбор.

Рассмотрим следующую структуру DOM:

http://localhost:3000
  • Иван
  • Мария
  • Иван
  • Мария
<ul>
  <li>
    <div>John</div>
    <div><button>Say hello</button></div>
  </li>
  <li>
    <div>Mary</div>
    <div><button>Say hello</button></div>
  </li>
  <li>
    <div>John</div>
    <div><button>Say goodbye</button></div>
  </li>
  <li>
    <div>Mary</div>
    <div><button>Say goodbye</button></div>
  </li>
</ul>

Чтобы сделать снимок строки с текстом «Мария» и кнопкой «Попрощаться»:

const rowLocator = page.getByRole('listitem');

await rowLocator
    .filter({ hasText: 'Mary' })
    .filter({ has: page.getByRole('button', { name: 'Say goodbye' }) })
    .screenshot({ path: 'screenshot.png' });

В корневом каталоге проекта должен появиться файл «screenshot.png».

Редкие случаи использования​

Выполнение действия для каждого элемента списка​

Перебор элементов:

for (const row of await page.getByRole('listitem').all())
  console.log(await row.textContent());

Перебор с помощью обычного цикла for:

const rows = page.getByRole('listitem');
const count = await rows.count();
for (let i = 0; i < count; ++i)
  console.log(await rows.nth(i).textContent());

Выполнение кода на странице​

Код внутри locator.evaluateAll() выполняется на странице; там можно вызывать любые API DOM.

const rows = page.getByRole('listitem');
const texts = await rows.evaluateAll(
    list => list.map(element => element.textContent));

Строгий режим​

Локаторы работают в строгом режиме. Это означает, что любая операция с локатором, предполагающая наличие целевого элемента DOM, вызовет исключение, если найдено более одного элемента. Например, следующий вызов вызовет исключение, если в DOM есть несколько кнопок:

Вызывает ошибку, если найдено более одного элемента​

await page.getByRole('button').click();

При этом Playwright понимает, когда выполняется операция с несколькими элементами, поэтому следующий вызов работает, даже если локатор соответствует нескольким элементам.

Корректно работает с несколькими элементами​

await page.getByRole('button').count();

Можно явно отключить проверку строгого режима и указать Playwright, какой элемент использовать, если найдено несколько совпадений: locator.first(), locator.last() и locator.nth(). Не рекомендуем использовать эти методы: при изменении страницы Playwright может нажать не на тот элемент. Вместо этого следуйте приведённым выше рекомендациям и создайте локатор, однозначно определяющий целевой элемент.

Другие локаторы​

О менее распространённых локаторах читайте в руководстве «Другие локаторы».

© 2026 Microsoft
Licensed under the Apache License, Version 2.0.
https://playwright.dev/docs/locators

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API