Skip to main content

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

Введение

note

Обратите внимание на основное руководство по локаторам для наиболее распространенных и рекомендуемых локаторов.

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

CSS локатор

note

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

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

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

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

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

CSS: соответствие по тексту

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

  • article:has-text("Playwright") - :has-text() соответствует любому элементу, содержащему указанный текст где-то внутри, возможно, в дочернем или потомке. Соответствие нечувствительно к регистру, обрезает пробелы и ищет подстроку.

    Например, article:has-text("Playwright") соответствует <article><div>Playwright</div></article>.

    Обратите внимание, что :has-text() следует использовать вместе с другими CSS спецификаторами, иначе он будет соответствовать всем элементам, содержащим указанный текст, включая <body>.

    // Неправильно, будет соответствовать многим элементам, включая <body>
    await page.locator(':has-text("Playwright")').click();
    // Правильно, соответствует только элементу <article>
    await page.locator('article:has-text("Playwright")').click();
  • #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>, потому что <button> содержит один текстовый узел "Log in", который не равен "Log". Однако, :text-is("Log") соответствует <button> Log <span>in</span></button>, потому что <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>.

note

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

note

Элементы ввода типа button и submit соответствуют по их value, а не по текстовому содержимому. Например, :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 псевдоклассом. Он возвращает элемент, если любой из селекторов, переданных в качестве параметров относительно :scope данного элемента, соответствует хотя бы одному элементу.

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

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

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

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

// Нажимает на <button>, который имеет текст "Log in" или "Sign in".
await page.locator('button:has-text("Log in"), button:has-text("Sign in")').click();

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

CSS: соответствие элементов на основе расположения

note

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

Иногда сложно придумать хороший селектор для целевого элемента, если у него нет отличительных черт. В этом случае использование 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>, или элемент, который прокручен и в данный момент не виден.

// Заполнить поле ввода справа от "Username".
await page.locator('input:right-of(:text("Username"))').fill('value');

// Нажать на кнопку рядом с промо-картой.
await page.locator('button:near(.promo-card)').click();

// Нажать на радио-кнопку в списке, ближайшую к "Label 3".
await page.locator('[type=radio]:left-of(:text("Label 3"))').first().click();

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

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

note

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

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

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

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

// Нажать на третью кнопку "Buy"
await page.locator(':nth-match(:text("Buy"), 3)').click();

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

// Ожидать, пока все три кнопки не станут видимыми
await page.locator(':nth-match(:text("Buy"), 3)').waitFor();
note

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

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

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

// Нажать на первую кнопку
await page.locator('button').locator('nth=0').click();

// Нажать на последнюю кнопку
await page.locator('button').locator('nth=-1').click();

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

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

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

Если вы хотите нацелиться на родительский <li> элемента label с текстом "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 локатор

note

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']
  • соответствие по регулярному выражению значения свойства: _react=[author = /Steven(\\s+King)?/i]

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

note

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

note

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

Vue локатор

note

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

Vue локатор позволяет находить элементы по имени их компонента и значениям свойств. Синтаксис очень похож на CSS селекторы атрибутов и поддерживает все операторы селекторов атрибутов 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=[author *= "King"]
  • соответствие по компоненту и нескольким свойствам: _vue=book-item[author *= "king" i][year = 1990]
  • соответствие по вложенному значению свойства: _vue=[some.nested.value = 12]
  • соответствие по компоненту и префиксу значения свойства: _vue=book-item[author ^= "Steven"]
  • соответствие по компоненту и суффиксу значения свойства: _vue=book-item[author $= "Steven"]
  • соответствие по регулярному выражению значения свойства: _vue=[author = /Steven(\\s+King)?/i]

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

note

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

note

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

XPath локатор

warning

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

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

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

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

note

XPath не проникает в теневые корни.

XPath объединение

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

// Ожидает либо диалог подтверждения, либо индикатор загрузки.
await page.locator(
`//span[contains(@class, 'spinner__loading')]|//div[@id='confirmation']`
).waitFor();

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

warning

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

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

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

// Заполнить поле ввода, нацелившись на метку.
await page.getByText('Password').fill('secret');

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

// Заполнить поле ввода, нацелившись на метку.
await expect(page.locator('label')).toHaveText('Password');

Устаревший текстовый локатор

warning

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

Устаревший текстовый локатор соответствует элементам, содержащим переданный текст.

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>, потому что <button> содержит один текстовый узел "Log in", который не равен "Log". Однако, text="Log" соответствует <button> Log <span>in</span></button>, потому что <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();
note

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

note

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

note

Элементы ввода типа button и submit соответствуют по их value, а не по текстовому содержимому. Например, text=Log in соответствует <input type=button value="Log in">.

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

warning

Мы рекомендуем находить по тестовому id вместо этого.

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

  • id
  • data-testid
  • data-test-id
  • data-test
// Заполнить поле ввода с id "username"
await page.locator('id=username').fill('value');

// Нажать на элемент с data-test-id "submit"
await page.locator('data-test-id=submit').click();
note

Атрибутные селекторы не являются CSS селекторами, поэтому все, что специфично для CSS, например, :enabled, не поддерживается. Для получения дополнительных возможностей используйте правильный css селектор, например, css=[data-test="login"]:enabled.

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

warning

Мы рекомендуем цепочку локаторов вместо этого.

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

Например,

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

эквивалентно

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

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

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

warning

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

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

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