Playwright: альтернативные локаторы

Примечание

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

Помимо рекомендуемых локаторов, таких как page.getByRole() и page.getByText(), Playwright поддерживает множество других локаторов, описанных в этом руководстве.

Друзья, поддержите нас вступлением в наш телеграм канал QaRocks. Там много туториалов, задач по автоматизации и книг по QA.

Локатор CSS

Примечание

Мы рекомендуем отдавать предпочтение видимым для пользователя локаторам, таким как текст или роль, вместо использования CSS, который привязан к реализации и может измениться при обновлении страницы.

Playwright может находить элемент, используя его CSS-селектор.

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

Playwright расширяет стандартные селекторы CSS двумя способами:

  • CSS-селекторы могут работать с открытым Shadow DOM.
  • Playwright добавляет псевдоклассы, такие как :visible, :has-text(), :has(), :is(), :nth-match() и другие.

CSS: совпадение по тексту

Playwright включает несколько CSS-псевдоклассов для сопоставления элементов по их содержимому текста.

  • #nav-bar :text("Home") — псевдокласс :text() находит наименьший элемент, содержащий указанный текст. Поиск не чувствителен к регистру, игнорирует пробелы и ищет подстроку.
    Приведенный ниже пример найдет элемент, содержащий текст “Home” где-то внутри блока #nav-bar:
    await page.locator('#nav-bar :text("Home")').click();
  • #nav-bar :text-is("Home") — псевдокласс :text-is() находит наименьший элемент с точным соответствием текста. Совпадение чувствительно к регистру, игнорирует пробелы и ищет полное совпадение строки.
    Пример: :text-is("Log") не найдёт <button>Log in</button>, так как содержимое узла текста "Log in" не равно "Log". Однако :text-is("Log") найдёт элемент <button> Log <span>in</span></button>, потому что текст " Log " соответствует.
    Аналогично, :text-is("Download") не найдет элемент <button>download</button>, поскольку он чувствителен к регистру.
  • #nav-bar :text-matches("reg?ex", "i") — псевдокласс :text-matches() находит наименьший элемент с текстом, который соответствует регулярному выражению в стиле JavaScript.
    Пример: :text-matches("Log\s*in", "i") найдёт <button>Login</button> и <button>log IN</button>.
  • article:has-text("Playwright") — псевдокласс :has-text() находит любой элемент, содержащий указанный текст где-то внутри, возможно, в дочернем элементе.Поиск не чувствителен к регистру, игнорирует лишние пробелы и ищет подстроку.
    Например, article:has-text("Playwright") найдёт следующий элемент:<article><div>Playwright</div></article>.
    Обратите внимание, что :has-text() должен использоваться вместе с другими CSS-спецификаторами, иначе он может найти все элементы с указанным текстом, включая <body>
// Неправильно, будет соответствовать многим элементам, включая
await page.locator(':has-text("Playwright")').click();
// Правильно, соответствует только элементу <article>
await page.locator('article:has-text("Playwright")').click();

Примечание

Сопоставление текста всегда нормализует пробелы: удаляет лишние пробелы, заменяет разрывы строк на пробелы и игнорирует начальные и конечные пробелы.

Примечание

Элементы ввода типа button и submit сопоставляются по значению, а не по тексту. Например, :text("Log in") найдёт <input type=button value="Log in">.

CSS: сопоставление только видимых элементов

Playwright поддерживает псевдокласс :visible в CSS-селекторах. Например, css=button соответствует всем кнопкам на странице, а css=button:visible находит только видимые кнопки. Это полезно для разграничения похожих элементов, различающихся по видимости.

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

<button style='display: none'>Invisible</button>
<button>Visible</button>
  • Этот код выберет обе кнопки и вызовет ошибку строгого соответствия
    await page.locator('button').click();
  • Этот код найдёт только видимую кнопку и нажмёт на неё:
    await page.locator('button:visible').click();

CSS: элементы, содержащие другие элементы

Псевдокласс :has() – это экспериментальный псевдокласс CSS, который возвращает элемент, если внутри него находится другой элемент, указанный в селекторе.

Пример: следующий фрагмент кода вернёт текстовое содержимое элемента <article>, который содержит внутри <div class="promo">:

await page.locator('article:has(div.promo)').textContent();

CSS: элементы, соответствующие одному из условий

Список селекторов, разделённых запятыми, находит элементы, которые соответствуют хотя бы одному из указанных условий.

// Clicks a <button> that has either a "Log in" or "Sign in" text.
await page.locator('button:has-text("Log in"), button:has-text("Sign in")').click();

Псевдокласс :is() – это экспериментальный псевдокласс CSS, который помогает задавать набор дополнительных условий для элемента.

CSS: сопоставление элементов на основе макета

Примечание

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

Иногда бывает трудно создать хороший селектор для целевого элемента, если у него нет явных отличительных признаков. В таких случаях могут помочь псевдоклассы CSS, используемые в Playwright для работы с макетами. Эти псевдоклассы можно комбинировать с обычным CSS, чтобы точно выделить один из нескольких вариантов.

Например, input:right-of(:text("Password")) сопоставит поле ввода, которое находится справа от текста “Password”. Это особенно полезно, если на странице несколько похожих полей ввода, которые трудно различить.

Однако важно отметить, что псевдоклассы компоновки лучше использовать в сочетании с другими элементами, такими как input. Если использовать только псевдокласс, например :right-of(:text("Password")), скорее всего, будет выбрано не поле ввода, а какой-то пустой элемент между текстом и целевым элементом.

Псевдоклассы макетов используют ограничивающий прямоугольник (bounding client rect) для вычисления расстояний и относительных позиций элементов.

  • :right-of(div > button) — выбирает элементы, расположенные справа от любого элемента, соответствующего вложенному селектору, на любой вертикальной позиции.
  • :left-of(div > button) — выбирает элементы, расположенные слева от любого элемента, соответствующего вложенному селектору, на любой вертикальной позиции.
  • :above(div > button) — выбирает элементы, находящиеся выше любого элемента, соответствующего вложенному селектору, на любой горизонтальной позиции.
  • :below(div > button) — выбирает элементы, находящиеся ниже любого элемента, соответствующего вложенному селектору, на любой горизонтальной позиции.
  • :near(div > button) — выбирает элементы, находящиеся рядом (в пределах 50 CSS-пикселей) с любым элементом, соответствующим вложенному селектору.

Обратите внимание, что результаты сортируются по расстоянию до опорного элемента, поэтому можно использовать locator.first(), чтобы выбрать ближайший элемент. Это полезно, если у вас есть список похожих элементов, где ближайший элемент, скорее всего, будет правильным. Однако использование locator.first() в других случаях может привести к неожиданным результатам — он может выбрать случайный пустой <div> или элемент, который в данный момент прокручен и невидим.

// Fill an input to the right of "Username".
await page.locator('input:right-of(:text("Username"))').fill('value');

// Click a button near the promo card.
await page.locator('button:near(.promo-card)').click();

// Click the radio input in the list closest to the "Label 3".
await page.locator('[type=radio]:left-of(:text("Label 3"))').first().click();

Все псевдоклассы макетов поддерживают опциональное указание максимального расстояния в пикселях в качестве последнего аргумента. Например, button:near(:text("Username"), 120) выберет кнопку, которая находится не далее 120 CSS-пикселей от элемента с текстом “Username”.

CSS: Выбор n-го совпадения в результатах запроса

Примечание

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

Иногда на странице присутствует несколько одинаковых элементов, и выбрать нужный бывает сложно. Например:

<section> <button>Buy</button> </section>
<article><div> <button>Buy</button> </div></article>
<div><div> <button>Buy</button> </div></div>

В данном случае селектор :nth-match(:text("Buy"), 3) выберет третью кнопку. Обратите внимание, что индекс начинается с 1.

// Click the third "Buy" buttonawait page.locator(':nth-match(:text("Buy"), 3)').click();

Псевдокласс :nth-match() также полезен для ожидания появления заданного количества элементов с помощью метода locator.waitFor().

// Wait until all three buttons are visibleawait page.locator(':nth-match(:text("Buy"), 3)').waitFor();

Примечание

В отличие от :nth-child(), элементы, соответствующие :nth-match(), не обязательно должны быть соседями — они могут располагаться в любом месте страницы. В приведенном выше примере все три кнопки соответствуют селектору :text("Buy"), а :nth-match() выберет третью кнопку.

Локатор N-го элемента

Вы можете уточнить запрос до n-го совпадения, используя локатор nth=, где индекс начинается с нуля.

// Click first button
await page.locator('button').locator('nth=0').click();
// Click last buttonawait page.locator('button').locator('nth=-1').click();

Локатор родительского элемента

Когда вам нужно нацелиться на родительский элемент какого-либо другого элемента, чаще всего следует использовать locator.filter() по дочернему локатору. Например, рассмотрим следующую структуру DOM:

<li><label>Hello</label></li>
<li><label>World</label></li>

Если вам нужно выбрать родительский элемент <li> ярлыка с текстом "Hello", лучше всего использовать locator.filter():

const child = page.getByText('Hello');const parent = page.getByRole('listitem').filter({ has: child });

В качестве альтернативы, если вы не можете найти подходящий локатор для родительского элемента, используйте xpath=... Обратите внимание, что этот метод не так надежен, поскольку любые изменения в структуре DOM приведут к упавшим тестам. По возможности предпочтительнее использовать locator.filter().

const parent = page.getByText('Hello').locator('xpath=..');

Локатор React

Примечание

Локатор React является экспериментальным и имеет префикс _. Функциональность может измениться в будущем.

Локатор React позволяет находить элементы по имени компонента и значениям его свойств. Синтаксис очень похож на селекторы атрибутов CSS и поддерживает все CSS для работы с атрибутами.

В локаторе React имена компонентов записываются в стиле CamelCase.

await page.locator('_react=BookItem').click();

Другие примеры:

  • совпадение по компоненту: _react=BookItem
  • совпадение по компоненту и точному значению свойства, с учетом регистра: _react=BookItem[author = "Steven King"]
  • совпадение только по значению свойства, без учета регистра: _react=[author = "steven king" i]
  • совпадение по компоненту и истинному значению свойства: _react=MyButton[enabled]
  • соответствие по компоненту и булевому значению: _react=MyButton[enabled = false]
  • совпадение по подстроке значения свойства : _react=[author *= "King"]
  • совпадение по компоненту и нескольким свойствам: _react=BookItem[author *= "king" i][year = 1990]
  • совпадение по значению вложенного свойства: _react=[some.nested.value = 12]
  • совпадение по компоненту и префиксу значения свойства: _react=BookItem[author ^= "Steven"]
  • совпадение по компоненту и суффиксу значения свойства : _react=BookItem[author $= "Steven"]
  • совпадение по компоненту и ключу: _react=BookItem[key = '2']
  • совпадение по значению свойства regex: _react=[author = /Steven(\\\s+King)?/i]

Чтобы найти имена элементов React в дереве, используйте React DevTools.

Примечание

Локатор React поддерживает версии React 15 и выше.

Примечание

Локатор React, как и React DevTools, работают только с неминифицированными сборками приложений.

Локатор Vue

Примечание

Локатор Vue является экспериментальным и имеет префикс _. Функциональность может измениться в будущем.

Локатор Vue позволяет находить элементы по имени компонента и значениям его свойств. Синтаксис очень похож на селекторы атрибутов CSS и поддерживает все операторы, применимые к атрибутам.

Имена компонентов Vue в локаторе записываются в kebab-case.

await page.locator('_vue=book-item').click();

Примеры:

  • совпадение по компоненту: _vue=book-item
  • совпадение по компоненту и точному значению свойства, с учетом регистра: _vue=book-item[author = "Steven King"]
  • совпадение только по значению свойства, без учета регистра: _vue=[author = "steven king" i]
  • совпадение по компоненту и истинному значению свойства: _vue=my-button[enabled]
  • совпадение по компоненту и булевому значению: _vue=my-button[enabled = false]
  • совпадение по подстроке значения свойства : _vue=[автор *= "Кинг"]
  • совпадение по компоненту и нескольким свойствам: _vue=book-item[author *= "king" i][year = 1990]
  • совпадение по вложенному значению свойства: _vue=[some.nested.value = 12]
  • совпадение по компоненту и префиксу значения свойства: _vue=book-item[author ^= "Steven"]
  • совпадение по компоненту и суффиксу значения свойства : _vue=book-item[author $= "Steven"]
  • совпадение по значению свойства с использованием регулярного выражения (regex): _vue=[author = /Steven(\\s+King)?/i]

Чтобы найти имена элементов Vue в дереве, используйте Vue DevTools.

Примечание

Локатор Vue поддерживает Vue2 и выше.

Примечание

Vue locator, как и Vue DevTools, работают только с неминифицированными сборками приложений.

Локатор XPath

Предупреждение

Рекомендуется использовать локаторы, видимые для пользователя (например, текстовые или по доступной роли), а не XPath. XPath привязан к структуре страницы, что делает его уязвимым к изменениям.

Локаторы XPath эквивалентны вызову Document.evaluate.

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

Примечание

Любая строка селектора, начинающаяся с // или .., считается селектором Xpath. Например, Playwright преобразует '//html/body' в 'xpath=//html/body'.

Примечание

XPath не может проникать в shadow DOM.

Объединение XPath

Оператор Pipe (|) можно использовать для указания нескольких селекторов в XPath. Он будет соответствовать всем элементам, которые могут быть выбраны одним из селекторов в этом списке.

// Waits for either confirmation dialog or load spinner.
await page.locator(
    `//span[contains(@class, 'spinner__loading')]|//div[@id='confirmation']`
).waitFor();

Перенаправление меток на элементы управления формами

Предупреждение

Рекомендуется определять местоположение по тексту метки, а не полагаться на перенаправление “метка – элемент управления”.

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

Например, рассмотрим следующую структуру DOM: <label for="password">Password:</label><input id="password" type="password">, можно ориентироваться на текст метки “Password” с помощью page.getByText(). Однако следующие действия будут выполняться для ввода, а не для метки:

  • locator.click() клик по метке автоматически сфокусирует связанный с ней элемент управления (например, поле ввода);
  • locator.fill() — заполнит связанное поле ввода;
  • locator.inputValue() — вернет текущее значение поля ввода;
  • locator.selectText() — выделит текст в поле ввода;
  • locator.setInputFiles() — установит файлы для элемента ввода с типом type=file;
  • locator.selectOption() — выберет опцию в выпадающем списке.
// Fill the input by targeting the label.
await page.getByText('Password').fill('secret');

Однако методы, такие как expect(locator).toHaveText(), будут проверять содержимое самой метки, а не связанного элемента управления.

// Fill the input by targeting the label.
await expect(page.locator('label')).toHaveText('Password');

Наследственный текстовый локатор

Предупреждение

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

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

await page.locator('text=Log in').click();

Основные варианты использования наследственного текстового локатора:

  • text=Log in – стандартное поведение локатора: регистронезависимый поиск по подстроке. Пробелы обрезаются. Например, text=Log найдет элемент <button>Log in</button>.

    await page.locator('text=Log in').click();
  • text="Log in" – поиск текста с точным совпадением с учетом регистра и пробелов..

    Например, text="Log" не найдет <button>Log in</button>, потому что это точный поиск. Однако text="Log" подойдет для элемента <button> Log <span>in</span></button>, так как текст “Log” является отдельным узлом. Данный точный режим подразумевает совпадение с учетом регистра, поэтому text="Download" не будет соответствовать <button>download</button>.

    Например, используйте \" для экранирования двойной кавычки в строке с двойными кавычками: text="foo\"bar".

    await page.locator('text="Log in"').click();
  • /Log\s*in/i — использование JavaScript регулярных выражений, чтобы задать гибкие критерии поиска. Например, text=/Log\s*in/i найдет как <button>Login</button>, так и <button>log IN</button>.

    await page.locator('text=/Log\\\s*in/i').click();

Примечание

Локаторы, строка которых начинается и заканчивается кавычками (одинарными или двойными), автоматически преобразуются в наследственные текстовые локаторы. Например, "Log in" преобразуется в text="Log in".

Примечание

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

Примечание

Для элементов ввода с типами button и submit поиск осуществляется по значению атрибута value, а не по текстовому содержимому. Например, text=Log in найдет следующий элемент <input type=button value="Log in">.

Селекторы id, data-testid, data-test-id, data-test

Предупреждение

Мы настоятельно рекомендуем использовать идентификаторы теста для поиска элементов на странице.

Playwright поддерживает упрощенные способы выбора элементов с использованием определённых атрибутов. На данный момент поддерживаются только следующие атрибуты:

  • id
  • data-testid
  • data-test-id
  • data-test
// Fill an input with the id "username"
await page.locator('id=username').fill('value');
// Click an element with data-test-id "submit"
await page.locator('data-test-id=submit').click();
  • Примечание
  • Селекторы атрибутов не являются селекторами CSS, поэтому специфические для CSS возможности, такие как псевдоклассы :enabled, не поддерживаются. Чтобы использовать дополнительные возможности, применяйте полноценный CSS-селектор, например css=[data-test="login"]:enabled.

Цепочка селекторов

Предупреждение

Рекомендуется использовать цепочки локаторов.

Селекторы, определенные как engine=body или в краткой форме, могут быть объединены с помощью оператора >>, например, selector1 >> selector2 >> selectors3. Это позволяет связывать селекторы так, чтобы каждый последующий выполнялся относительно результата предыдущего.

Например,

css=article >> css=.bar > .baz >> css=span[attr=value]

это эквивалентно следующему JavaScript-коду

document
    .querySelector('article')
    .querySelector('.bar > .baz')
    .querySelector('span[attr=value]');

Если необходимо, чтобы внутри тела селектора был символ >>, его нужно экранировать, чтобы избежать путаницы с разделителем цепочек, например, text="some >> text".

Промежуточные совпадения

Предупреждение

Рекомендуется фильтровать результаты через другие локаторы, чтобы найти элементы, содержащие вложенные элементы.

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

Например, css=article >> text=Hello выберет элемент с текстом Hello, а *css=article >> text=Hello (обратите внимание на *) выберет элемент article, который содержит элемент с текстом Hello.

Перевод статьи «Other locators».

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *