Локаторы
Введение
[Локаторы] являются центральной частью автоматического ожидания и возможности повторных попыток в Playwright. Вкратце, локаторы представляют собой способ найти элемент(ы) на странице в любой момент времени.
Быстрый гайд
Это рекомендуемые встроенные локаторы.
- page.get_by_role() для поиска по явным и неявным атрибутам доступности.
- page.get_by_text() для поиска по текстовому содержимому.
- page.get_by_label() для поиска элемента управления формой по тексту связанной метки.
- page.get_by_placeholder() для поиска ввода по заполнителю.
- page.get_by_alt_text() для поиска элемента, обычно изображения, по его текстовой альтернативе.
- page.get_by_title() для поиска элемента по его атрибуту title.
- page.get_by_test_id() для поиска элемента на основе его атрибута
data-testid
(другие атрибуты могут быть настроены).
- Sync
- Async
page.get_by_label("User Name").fill("John")
page.get_by_label("Password").fill("secret-password")
page.get_by_role("button", name="Sign in").click()
expect(page.get_by_text("Welcome, John!")).to_be_visible()
await page.get_by_label("User Name").fill("John")
await page.get_by_label("Password").fill("secret-password")
await page.get_by_role("button", name="Sign in").click()
await expect(page.get_by_text("Welcome, John!")).to_be_visible()
Поиск элементов
Playwright предоставляет несколько встроенных локаторов. Чтобы сделать тесты устойчивыми, мы рекомендуем отдавать приоритет атрибутам, ориентированным на пользователя, и явным контрактам, таким как page.get_by_role().
Например, рассмотрим следующую структуру DOM.
<button>Sign in</button>
Найдите элемент по его роли button
с именем "Sign in".
- Sync
- Async
page.get_by_role("button", name="Sign in").click()
await page.get_by_role("button", name="Sign in").click()
Используйте генератор кода для генерации локатора, а затем отредактируйте его по своему усмотрению.
Каждый раз, когда локатор используется для действия, актуальный элемент DOM находится на странице. В приведенном ниже фрагменте кода базовый элемент DOM будет найден дважды, один раз перед каждым действием. Это означает, что если DOM изменится между вызовами из-за повторного рендеринга, будет использован новый элемент, соответствующий локатору.
- Sync
- Async
locator = page.get_by_role("button", name="Sign in")
locator.hover()
locator.click()
locator = page.get_by_role("button", name="Sign in")
await locator.hover()
await locator.click()
Обратите внимание, что все методы, создающие локатор, такие как page.get_by_label(), также доступны в классах Locator и FrameLocator, поэтому вы можете объединять их и постепенно уточнять ваш локатор.
- Sync
- Async
locator = page.frame_locator("my-frame").get_by_role("button", name="Sign in")
locator.click()
locator = page.frame_locator("#my-frame").get_by_role("button", name="Sign in")
await locator.click()
Поиск по роли
Локатор page.get_by_role() отражает, как пользователи и вспомогательные технологии воспринимают страницу, например, является ли элемент кнопкой или флажком. При поиске по роли обычно следует также указывать доступное имя, чтобы локатор точно указывал на нужный элемент.
Например, рассмотрим следующую структуру DOM.
Sign up
<h3>Sign up</h3>
<label>
<input type="checkbox" /> Subscribe
</label>
<br/>
<button>Submit</button>
Вы можете найти каждый элемент по его неявной роли:
- Sync
- Async
expect(page.get_by_role("heading", name="Sign up")).to_be_visible()
page.get_by_role("checkbox", name="Subscribe").check()
page.get_by_role("button", name=re.compile("submit", re.IGNORECASE)).click()
await expect(page.get_by_role("heading", name="Sign up")).to_be_visible()
await page.get_by_role("checkbox", name="Subscribe").check()
await page.get_by_role("button", name=re.compile("submit", re.IGNORECASE)).click()
Локаторы ролей включают кнопки, флажки, заголовки, ссылки, списки, таблицы и многие другие и следуют спецификациям W3C для роли ARIA, атрибутов ARIA и доступного имени. Обратите внимание, что многие HTML-элементы, такие как <button>
, имеют неявно определенную роль, которая распознается локатором роли.
Обратите внимание, что локаторы ролей не заменяют аудиты доступности и тесты на соответствие, но дают раннюю обратную связь о руководствах ARIA.
Мы рекомендуем отдавать приоритет локаторам ролей для поиска элементов, так как это наиболее близкий способ к тому, как пользователи и вспомогательные технологии воспринимают страницу.
Поиск по метке
Большинство элементов управления формами обычно имеют выделенные метки, которые можно удобно использовать для взаимодействия с формой. В этом случае вы можете найти элемент управления по его связанной метке, используя page.get_by_label().
Например, рассмотрим следующую структуру DOM.
<label>Password <input type="password" /></label>
Вы можете заполнить ввод после его поиска по тексту метки:
- Sync
- Async
page.get_by_label("Password").fill("secret")
await page.get_by_label("Password").fill("secret")
Используйте этот локатор при поиске полей формы.
Поиск по заполнителю
Вводы могут иметь атрибут заполнителя, чтобы подсказать пользователю, какое значение следует ввести. Вы можете найти такой ввод, используя page.get_by_placeholder().
Например, рассмотрим следующую структуру DOM.
<input type="email" placeholder="name@example.com" />
Вы можете заполнить ввод после его поиска по тексту заполнителя:
- Sync
- Async
page.get_by_placeholder("name@example.com").fill("playwright@microsoft.com")
await page.get_by_placeholder("name@example.com").fill("playwright@microsoft.com")
Используйте этот локатор при поиске элементов формы, у которых нет меток, но есть текст заполнителя.
Поиск по тексту
Найдите элемент по тексту, который он содержит. Вы можете сопоставить по подстроке, точной строке или регулярному выражению, используя page.get_by_text().
Например, рассмотрим следующую структуру DOM.
<span>Welcome, John</span>
Вы можете найти элемент по тексту, который он содержит:
- Sync
- Async
expect(page.get_by_text("Welcome, John")).to_be_visible()
await expect(page.get_by_text("Welcome, John")).to_be_visible()
Установите точное совпадение:
- Sync
- Async
expect(page.get_by_text("Welcome, John", exact=True)).to_be_visible()
await expect(page.get_by_text("Welcome, John", exact=True)).to_be_visible()
Совпадение с регулярным выражением:
- Sync
- Async
expect(page.get_by_text(re.compile("welcome, john", re.IGNORECASE))).to_be_visible()
await expect(
page.get_by_text(re.compile("welcome, john", re.IGNORECASE))
).to_be_visible()
Сопоставление по тексту всегда нормализует пробелы, даже при точном совпадении. Например, оно превращает несколько пробелов в один, превращает разрывы строк в пробелы и игнорирует начальные и конечные пробелы.
Мы рекомендуем использовать локаторы текста для поиска неинтерактивных элементов, таких как div
, span
, p
и т.д. Для интерактивных элементов, таких как button
, a
, input
и т.д., используйте локаторы ролей.
Вы также можете фильтровать по тексту, что может быть полезно при попытке найти конкретный элемент в списке.
Поиск по альтернативному тексту
Все изображения должны иметь атрибут alt
, который описывает изображение. Вы можете найти изображение на основе текстовой альтернативы, используя page.get_by_alt_text().
Например, рассмотрим следующую структуру DOM.
<img alt="playwright logo" src="/img/playwright-logo.svg" width="100" />
Вы можете кликнуть на изображение после его нахождения по текстовой альтернативе:
- Sync
- Async
page.get_by_alt_text("playwright logo").click()
await page.get_by_alt_text("playwright logo").click()
Используйте этот локатор, когда ваш элемент поддерживает альтернативный текст, например, элементы img
и area
.
Поиск по заголовку
Найдите элемент с соответствующим атрибутом title, используя page.get_by_title().
Например, рассмотрим следующую структуру DOM.
<span title='Issues count'>25 issues</span>
Вы можете проверить количество проблем после его нахождения по тексту заголовка:
- Sync
- Async
expect(page.get_by_title("Issues count")).to_have_text("25 issues")
await expect(page.get_by_title("Issues count")).to_have_text("25 issues")
Используйте этот локатор, когда ваш элемент имеет атрибут title
.
Поиск по тестовому идентификатору
Тестирование по тестовым идентификаторам является наиболее устойчивым способом тестирования, так как даже если ваш текст или роль атрибута изменится, тест все равно пройдет. QA и разработчики должны определять явные тестовые идентификаторы и запрашивать их с помощью page.get_by_test_id(). Однако тестирование по тестовым идентификаторам не ориентировано на пользователя. Если роль или текстовое значение важны для вас, рассмотрите возможность использования локаторов, ориентированных на пользователя, таких как role и text locators.
Например, рассмотрим следующую структуру DOM.
<button data-testid="directions">Itinéraire</button>
Вы можете найти элемент по его тестовому идентификатору:
- Sync
- Async
page.get_by_test_id("directions").click()
await page.get_by_test_id("directions").click()
Установите пользовательский атрибут тестового идентификатора
По умолчанию, page.get_by_test_id() будет находить элементы на основе атрибута data-testid
, но вы можете настроить его в конфигурации теста или вызвав selectors.set_test_id_attribute().
Установите тестовый идентификатор для использования пользовательского атрибута данных в ваших тестах.
- Sync
- Async
playwright.selectors.set_test_id_attribute("data-pw")
playwright.selectors.set_test_id_attribute("data-pw")
В вашем HTML теперь вы можете использовать data-pw
в качестве тестового идентификатора вместо стандартного data-testid
.
<button data-pw="directions">Itinéraire</button>
А затем найдите элемент, как обычно:
- Sync
- Async
page.get_by_test_id("directions").click()
await page.get_by_test_id("directions").click()
Поиск по CSS или XPath
Если вам абсолютно необходимо использовать локаторы CSS или XPath, вы можете использовать page.locator() для создания локатора, который принимает селектор, описывающий, как найти элемент на странице. Playwright поддерживает селекторы CSS и XPath и автоматически определяет их, если вы опускаете префикс css=
или xpath=
.
- Sync
- Async
page.locator("css=button").click()
page.locator("xpath=//button").click()
page.locator("button").click()
page.locator("//button").click()
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 ниже являются примером плохой практики, которая приводит к нестабильным тестам:
- Sync
- Async
page.locator(
"#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input"
).click()
page.locator('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input').click()
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 не проникает в shadow roots.
- Закрытые shadow roots не поддерживаются.
Рассмотрим следующий пример с пользовательским веб-компонентом:
<x-details role=button aria-expanded=true aria-controls=inner-details>
<div>Title</div>
#shadow-root
<div id=inner-details>Details</div>
</x-details>
Вы можете найти элемент так же, как если бы shadow root не существовал.
Чтобы кликнуть <div>Details</div>
:
- Sync
- Async
page.get_by_text("Details").click()
await page.get_by_text("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>
:
- Sync
- Async
page.locator("x-details", has_text="Details").click()
await page.locator("x-details", has_text="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>
содержит текст "Details":
- Sync
- Async
expect(page.locator("x-details")).to_contain_text("Details")
await expect(page.locator("x-details")).to_contain_text("Details")
Фильтрация локаторов
Рассмотрим следующую структуру DOM, где мы хотим кликнуть на кнопку покупки второй карточки продукта. У нас есть несколько вариантов, чтобы отфильтровать локаторы и получить нужный.
Product 1
Product 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(). Он будет искать определенную строку где-то внутри элемента, возможно, в дочернем элементе, без учета регистра. Вы также можете передать регулярное выражение.
- Sync
- Async
page.get_by_role("listitem").filter(has_text="Product 2").get_by_role(
"button", name="Add to cart"
).click()
await page.get_by_role("listitem").filter(has_text="Product 2").get_by_role(
"button", name="Add to cart"
).click()
Используйте регулярное выражение:
- Sync
- Async
page.get_by_role("listitem").filter(has_text=re.compile("Product 2")).get_by_role(
"button", name="Add to cart"
).click()
await page.get_by_role("listitem").filter(has_text=re.compile("Product 2")).get_by_role(
"button", name="Add to cart"
).click()
Фильтрация по отсутствию текста
В качестве альтернативы, фильтруйте по отсутствию текста:
- Sync
- Async
# 5 товаров в наличии
expect(page.get_by_role("listitem").filter(has_not_text="Out of stock")).to_have_count(5)
# 5 товаров в наличии
await expect(page.get_by_role("listitem").filter(has_not_text="Out of stock")).to_have_count(5)
Фильтрация по дочернему/потомку
Локаторы поддерживают опцию выбора только тех элементов, которые имеют или не имеют потомка, соответствующего другому локатору. Таким образом, вы можете фильтровать по любому другому локатору, такому как locator.get_by_role(), locator.get_by_test_id(), locator.get_by_text() и т.д.
Product 1
Product 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>
- Sync
- Async
page.get_by_role("listitem").filter(
has=page.get_by_role("heading", name="Product 2")
).get_by_role("button", name="Add to cart").click()
await page.get_by_role("listitem").filter(
has=page.get_by_role("heading", name="Product 2")
).get_by_role("button", name="Add to cart").click()
Мы также можем проверить карточку продукта, чтобы убедиться, что она только одна:
- Sync
- Async
expect(
page.get_by_role("listitem").filter(
has=page.get_by_role("heading", name="Product 2")
)
).to_have_count(1)
await expect(
page.get_by_role("listitem").filter(
has=page.get_by_role("heading", name="Product 2")
)
).to_have_count(1)
Фильтрующий локатор должен быть относительным к исходному локатору и запрашивается начиная с совпадения исходного локатора, а не с корня документа. Поэтому следующее не сработает, потому что фильтрующий локатор начинает совпадать с элементом списка <ul>
, который находится вне элемента списка <li>
, совпадающего с исходным локатором:
- Sync
- Async
# ✖ НЕПРАВИЛЬНО
expect(
page.get_by_role("listitem").filter(
has=page.get_by_role("list").get_by_role("heading", name="Product 2")
)
).to_have_count(1)
# ✖ НЕПРАВИЛЬНО
await expect(
page.get_by_role("listitem").filter(
has=page.get_by_role("list").get_by_role("heading", name="Product 2")
)
).to_have_count(1)
Фильтрация по отсутствию дочернего/потомка
Мы также можем фильтровать по отсутствию соответствующего элемента внутри.
- Sync
- Async
expect(
page.get_by_role("listitem").filter(
has_not=page.get_by_role("heading", name="Product 2")
)
).to_have_count(1)
await expect(
page.get_by_role("listitem").filter(
has_not=page.get_by_role("heading", name="Product 2")
)
).to_have_count(1)
Обратите внимание, что внутренний локатор совпадает, начиная с внешнего, а не с корня документа.
Операторы локаторов
Совпадение внутри локатора
Вы можете объединять методы, создающие локатор, такие как page.get_by_text() или locator.get_by_role(), чтобы сузить поиск до определенной части страницы.
В этом примере мы сначала создаем локатор под названием product, находя его роль listitem
. Затем мы фильтруем по тексту. Мы можем снова использовать локатор product, чтобы получить роль кнопки и нажать на нее, а затем использовать утверждение, чтобы убедиться, что есть только один продукт с текстом "Product 2".
- Sync
- Async
product = page.get_by_role("listitem").filter(has_text="Product 2")
product.get_by_role("button", name="Add to cart").click()
product = page.get_by_role("listitem").filter(has_text="Product 2")
await product.get_by_role("button", name="Add to cart").click()
Вы также можете объединить два локатора вместе, например, чтобы найти кнопку "Сохранить" внутри определенного диалога:
- Sync
- Async
save_button = page.get_by_role("button", name="Save")
# ...
dialog = page.get_by_test_id("settings-dialog")
dialog.locator(save_button).click()
save_button = page.get_by_role("button", name="Save")
# ...
dialog = page.get_by_test_id("settings-dialog")
await dialog.locator(save_button).click()
Совпадение двух локаторов одновременно
Метод locator.and_() сужает существующий локатор, сопоставляя дополнительный локатор. Например, вы можете объединить page.get_by_role() и page.get_by_title(), чтобы сопоставить как по роли, так и по заголовку.
- Sync
- Async
button = page.get_by_role("button").and_(page.getByTitle("Subscribe"))
button = page.get_by_role("button").and_(page.getByTitle("Subscribe"))
Совпадение одного из двух альтернативных локаторов
Если вы хотите нацелиться на один из двух или более элементов, и вы не знаете, какой из них это будет, используйте locator.or_(), чтобы создать локатор, который соответствует любому из них или обоим альтернативам.
Например, рассмотрим сценарий, в котором вы хотите нажать на кнопку "Новое письмо", но иногда вместо этого появляется диалог настроек безопасности. В этом случае вы можете ожидать либо кнопку "Новое письмо", либо диалог и действовать соответственно.
Если и кнопка "Новое письмо", и диалог безопасности появляются на экране, локатор "или" будет соответствовать обоим из них, возможно, вызывая ошибку "strict mode violation". В этом случае вы можете использовать locator.first, чтобы соответствовать только одному из них.
- Sync
- Async
new_email = page.get_by_role("button", name="New")
dialog = page.get_by_text("Confirm security settings")
expect(new_email.or_(dialog).first).to_be_visible()
if (dialog.is_visible()):
page.get_by_role("button", name="Dismiss").click()
new_email.click()
new_email = page.get_by_role("button", name="New")
dialog = page.get_by_text("Confirm security settings")
await expect(new_email.or_(dialog).first).to_be_visible()
if (await dialog.is_visible()):
await page.get_by_role("button", name="Dismiss").click()
await new_email.click()
Поиск только видимых элементов
Обычно лучше найти более надежный способ уникально идентифицировать элемент, чем проверять видимость.
Рассмотрим страницу с двумя кнопками: первая невидима, а вторая видима.
<button style='display: none'>Invisible</button>
<button>Visible</button>
-
Это найдет обе кнопки и вызовет ошибку строгости:
- Sync
- Async
page.locator("button").click()
await page.locator("button").click()
-
Это найдет только вторую кнопку, так как она видима, и затем кликнет по ней.
- Sync
- Async
page.locator("button").filter(visible=True).click()
await page.locator("button").filter(visible=True).click()
Списки
Подсчет элементов в списке
Вы можете использовать локаторы, чтобы подсчитать количество элементов в списке.
Например, рассмотрим следующую структуру DOM:
- apple
- banana
- orange
<ul>
<li>apple</li>
<li>banana</li>
<li>orange</li>
</ul>
Используйте утверждение count, чтобы убедиться, что в списке 3 элемента.
- Sync
- Async
expect(page.get_by_role("listitem")).to_have_count(3)
await expect(page.get_by_role("listitem")).to_have_count(3)
Утверждение всех текстов в списке
Вы можете использовать локаторы, чтобы найти все тексты в списке.
Например, рассмотрим следующую структуру DOM:
- apple
- banana
- orange
<ul>
<li>apple</li>
<li>banana</li>
<li>orange</li>
</ul>
Используйте expect(locator).to_have_text(), чтобы убедиться, что в списке есть тексты "apple", "banana" и "orange".
- Sync
- Async
expect(page.get_by_role("listitem")).to_have_text(["apple", "banana", "orange"])
await expect(page.get_by_role("listitem")).to_have_text(["apple", "banana", "orange"])
Получение конкретного элемента
Существует множество способов получить конкретный элемент в списке.
Получение по тексту
Используйте метод page.get_by_text(), чтобы найти элемент в списке по его текстовому содержимому и затем кликнуть по нему.
Например, рассмотрим следующую структуру DOM:
- apple
- banana
- orange
<ul>
<li>apple</li>
<li>banana</li>
<li>orange</li>
</ul>
Найдите элемент по его текстовому содержимому и кликните по нему.
- Sync
- Async
page.get_by_text("orange").click()
await page.get_by_text("orange").click()
Фильтрация по тексту
Используйте locator.filter(), чтобы найти конкретный элемент в списке.
Например, рассмотрим следующую структуру DOM:
- apple
- banana
- orange
<ul>
<li>apple</li>
<li>banana</li>
<li>orange</li>
</ul>
Найдите элемент по роли "listitem", затем отфильтруйте по тексту "orange" и кликните по нему.
- Sync
- Async
page.get_by_role("listitem").filter(has_text="orange").click()
await page.get_by_role("listitem").filter(has_text="orange").click()
Получение по тестовому идентификатору
Используйте метод page.get_by_test_id(), чтобы найти элемент в списке. Возможно, вам потребуется изменить HTML и добавить тестовый идентификатор, если у вас его еще нет.
Например, рассмотрим следующую структуру DOM:
- apple
- banana
- orange
<ul>
<li data-testid='apple'>apple</li>
<li data-testid='banana'>banana</li>
<li data-testid='orange'>orange</li>
</ul>
Найдите элемент по его тестовому идентификатору "orange" и кликните по нему.
- Sync
- Async
page.get_by_test_id("orange").click()
await page.get_by_test_id("orange").click()
Получение по порядковому номеру
Если у вас есть список идентичных элементов, и единственный способ отличить их - это порядок, вы можете выбрать конкретный элемент из списка с помощью locator.first, locator.last или locator.nth().
- Sync
- Async
banana = page.get_by_role("listitem").nth(1)
banana = await page.get_by_role("listitem").nth(1)
Однако используйте этот метод с осторожностью. Часто страница может измениться, и локатор будет указывать на совершенно другой элемент, чем вы ожидали. Вместо этого постарайтесь придумать уникальный локатор, который пройдет критерии строгости.
Цепочка фильтров
Когда у вас есть элементы с различными сходствами, вы можете использовать метод locator.filter(), чтобы выбрать нужный. Вы также можете объединять несколько фильтров, чтобы сузить выбор.
Например, рассмотрим следующую структуру DOM:
- John
- Mary
- John
- Mary
<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>
Чтобы сделать скриншот строки с "Mary" и "Say goodbye":
- Sync
- Async
row_locator = page.get_by_role("listitem")
row_locator.filter(has_text="Mary").filter(
has=page.get_by_role("button", name="Say goodbye")
).screenshot(path="screenshot.png")
row_locator = page.get_by_role("listitem")
await row_locator.filter(has_text="Mary").filter(
has=page.get_by_role("button", name="Say goodbye")
).screenshot(path="screenshot.png")
Теперь у вас должен быть файл "screenshot.png" в корневом каталоге вашего проекта.
Редкие случаи использования
Выполнение действий с каждым элементом в списке
Итерация по элементам:
- Sync
- Async
for row in page.get_by_role("listitem").all():
print(row.text_content())
for row in await page.get_by_role("listitem").all():
print(await row.text_content())
Итерация с использованием обычного цикла for:
- Sync
- Async
rows = page.get_by_role("listitem")
count = rows.count()
for i in range(count):
print(rows.nth(i).text_content())
rows = page.get_by_role("listitem")
count = await rows.count()
for i in range(count):
print(await rows.nth(i).text_content())
Выполнение кода на странице
Код внутри locator.evaluate_all() выполняется на странице, там можно вызывать любые DOM API.
- Sync
- Async
rows = page.get_by_role("listitem")
texts = rows.evaluate_all("list => list.map(element => element.textContent)")
rows = page.get_by_role("listitem")
texts = await rows.evaluate_all("list => list.map(element => element.textContent)")
Строгость
Локаторы являются строгими. Это означает, что все операции с локаторами, которые подразумевают некоторый целевой элемент DOM, вызовут исключение, если будет найдено более одного элемента. Например, следующий вызов вызовет ошибку, если в DOM несколько кнопок:
Вызывает ошибку, если более одного элемента
- Sync
- Async
page.get_by_role("button").click()
await page.get_by_role("button").click()
С другой стороны, Playwright понимает, когда вы выполняете операцию с несколькими элементами, поэтому следующий вызов работает нормально, когда локатор разрешается в несколько элементов.
Работает нормально с несколькими элементами
- Sync
- Async
page.get_by_role("button").count()
await page.get_by_role("button").count()
Вы можете явно отказаться от проверки строгости, указав Playwright, какой элемент использовать, когда несколько элементов совпадают, через locator.first, locator.last и locator.nth(). Эти методы не рекомендуются, потому что, когда ваша страница изменяется, Playwright может нажать на элемент, который вы не планировали. Вместо этого следуйте лучшим практикам, чтобы создать локатор, который уникально идентифицирует целевой элемент.
Дополнительные локаторы
Для менее часто используемых локаторов ознакомьтесь с руководством другие локаторы.