Селектори
The WebDriver Protocol provides several selector strategies to query an element. WebdriverIO simplifies them to keep selecting elements simple. Please note that even though the command to query elements is called $
and $$
, they have nothing to do with jQuery or the Sizzle Selector Engine.
While there are so many different selectors available, only a few of them provide a resilient way to find the right element. For example, given the following button:
<button
id="main"
class="btn btn-large"
name="submission"
role="button"
data-testid="submit"
>
Submit
</button>
Ми рекомендуємо і не рекомендуємо наступні селектори:
Селектор | Рекомендовано | Примітки |
---|---|---|
$('button') | 🚨 Ніколи | Найгірший - занадто загальний, без контексту. |
$('.btn.btn-large') | 🚨 Ніколи | Погано. Прив'язаний до стилів. Дуже схильний до змін. |
$('#main') | ⚠️ Зрідка | Краще. Але все ще прив'язаний до стилів або слухачів подій JS. |
$(() => document.queryElement('button')) | ⚠️ Зрідка | Ефективний запит, складний для написання. |
$('button[name="submission"]') | ⚠️ Зрідка | Прив'язаний до атрибуту name , який має семантику HTML. |
$('button[data-testid="submit"]') | ✅ Добре | Потрібен додатковий атрибут, не пов'язаний з a11y. |
$('aria/Submit') або $('button=Submit') | ✅ Завжди | Найкраще. Нагадує, як користувач взаємодіє зі сторінкою. Рекомендується використовувати файли перекладу вашого фронтенду, щоб ваші тести ніколи не падали при оновленні перекладів |
CSS Query Selector
If not indicated otherwise, WebdriverIO will query elements using the CSS selector pattern, e.g.:
loading...
Link Text
Щоб отримати елемент посилання з певним текстом, запитуйте текст, починаючи зі знаку рівності (=
).
Наприклад:
loading...
Ви можете запросити цей елемент, викликавши:
loading...
Partial Link Text
Щоб знайти елемент-посилання, видимий текст якого частково відповідає вашому пошуковому значенню, використовуйте *=
перед рядком запиту (наприклад, *=driver
).
Ви також можете запросити елемент з прикладу вище, викликавши:
loading...
Примітка: Ви не можете змішувати кілька стратегій селекторів в одному селекторі. Використовуйте декілька ланцюжкових запитів елементів для досягнення тієї ж мети, наприклад:
const elem = await $('header h1*=Welcome') // не працює!!!
// використовуйте натомість
const elem = await $('header').$('*=driver')
Element with certain text
Така ж техніка може бути застосована і до елементів. Крім того, також можливо виконати пошук без урахування регістру за допомогою .=
або .*=
в запиті.
Наприклад, ось запит для заголовка 1 рівня з текстом "Welcome to my Page":
loading...
Ви можете запросити цей елемент, викликавши:
loading...
Або використовуючи частковий текст запиту:
loading...
Те саме працює для імен id
та class
:
loading...
Ви можете запросити цей елемент, викликавши:
loading...
Примітка: Ви не можете змішувати кілька стратегій селекторів в одному селекторі. Використовуйте декілька ланцюжкових запитів елементів для досягнення тієї ж мети, наприклад:
const elem = await $('header h1*=Welcome') // не працює!!!
// використовуйте натомість
const elem = await $('header').$('h1*=Welcome')
Tag Name
Щоб запитати елемент з певною назвою тегу, використовуйте <tag>
або <tag />
.
loading...
Ви можете запросити цей елемент, викликавши:
loading...
Name Attribute
Для запиту елементів з певним атрибутом name ви можете використовувати або звичайний селектор CSS3, або надану стратегію name з JSONWireProtocol, передаючи щось на зразок [name="some-name"] як параметр селектора:
loading...
loading...
Примітка: Ця стратегія селектора застаріла і працює лише в старих браузерах, які працюють за протоколом JSONWireProtocol, або при використанні Appium.
xPath
Також можливо запитувати елементи через конкретний xPath.
Селектор xPath має формат на зразок //body/div[6]/div[1]/span[1]
.
loading...
Ви можете запитати другий абзац, викликавши:
loading...
Ви можете використовувати xPath також для переміщення вгору та вниз по DOM-дереву:
loading...
Accessibility Name Selector
Запит елементів за їх доступною назвою. Доступна назва - це те, що оголошується програмою зчитування з екрану, коли цей елемент отримує фокус. Значення доступної назви може бути як візуальним вмістом, так і прихованими текстовими альтернативами.
Ви можете прочитати більше про цей селектор у нашому блозі про випуск
Fetch by aria-label
loading...
loading...
Fetch by aria-labelledby
loading...
loading...
Fetch by content
loading...
loading...
Fetch by title
loading...
loading...
Fetch by alt
property
loading...
loading...
ARIA - Role Attribute
Для запиту елементів на основі ролей ARIA, ви можете безпосередньо вказати роль елемента, наприклад [role=button]
як параметр селектора:
loading...
loading...
ID Attribute
Стратегія локатора "id" не підтримується в протоколі WebDriver, замість цього слід використовувати стратегії селекторів CSS або xPath для пошуку елементів за допомогою ID.
Однак деякі драйвери (наприклад, Appium You.i Engine Driver) все ще можуть підтримувати цей селектор.
Поточні підтримувані синтаксиси селектора для ID:
//css locator
const button = await $('#someid')
//xpath locator
const button = await $('//*[@id="someid"]')
//id strategy
// Note: works only in Appium or similar frameworks which supports locator strategy "ID"
const button = await $('id=resource-id/iosname')
JS Function
Ви також можете використовувати функції JavaScript для отримання елементів за допомогою веб-нативних API. Звісно, ви можете робити це лише всередині веб-контексту (наприклад, browser
або веб-контекст на мобільному).
Маючи наступну HTML-структуру:
loading...
Ви можете запитати сусідній елемент #elem
наступним чином:
loading...
Deep Selectors
Починаючи з v9
WebdriverIO немає потреби в цьому спеціальному селекторі, оскільки WebdriverIO автоматично проникає через Shadow DOM за вас. Рекомендується відмовитися від цього селектора, видаливши >>>
перед ним.
Багато фронтенд-додатків сильно покладаються на елементи з shadow DOM. Технічно неможливо запитувати елементи всередині shadow DOM без обхідних шляхів. shadow$
та shadow$$
були такими обхідними шляхами, які мали свої обмеження. З глибоким селектором ви тепер можете запитувати всі елементи в будь-якому shadow DOM, використовуючи звичайну команду запиту.
Якщо у нас є додаток з наступною структурою:
За допомогою цього селектора ви можете запитати елемент <button />
, який вкладений в інший shadow DOM, наприклад:
loading...
Mobile Selectors
Для гібридного мобільного тестування важливо, щоб сервер автоматизації знаходився в правильному контексті перед виконанням команд. Для автоматизації жестів драйвер в ідеалі повинен бути встановлений у нативний контекст. Але для вибору елементів з DOM драйверу потрібно встановити контекст webview платформи. Тільки потім можуть бути використані методи, згадані вище.
Для нативного мобільного тестування немає перемикання між контекстами, оскільки вам доводиться використовувати мобільні стратегії та безпосередньо використовувати технологію автоматизації пристрою. Це особливо корисно, коли тест потребує певного контролю над пошуком елементів.
Android UiAutomator
Фреймворк Android UI Automator надає кілька способів пошуку елементів. Ви можете використовувати API UI Automator, зокрема клас UiSelector для пошуку елементів. В Appium ви надсилаєте код Java як рядок на сервер, який виконує його в середовищі додатка, повертаючи елемент або елементи.
const selector = 'new UiSelector().text("Cancel").className("android.widget.Button")'
const button = await $(`android=${selector}`)
await button.click()
Android DataMatcher and ViewMatcher (Espresso only)
Стратегія Android DataMatcher надає спосіб пошуку елементів за допомогою Data Matcher
const menuItem = await $({
"name": "hasEntry",
"args": ["title", "ViewTitle"]
})
await menuItem.click()
І аналогічно View Matcher
const menuItem = await $({
"name": "hasEntry",
"args": ["title", "ViewTitle"],
"class": "androidx.test.espresso.matcher.ViewMatchers"
})
await menuItem.click()
Android View Tag (Espresso only)
Стратегія view tag надає зручний спосіб пошуку елементів за їх тегом.
const elem = await $('-android viewtag:tag_identifier')
await elem.click()
iOS UIAutomation
При автоматизації iOS-додатку можна використовувати фреймворк UI Automation від Apple для пошуку елементів.
Це API JavaScript має методи для доступу до подання та всього, що на ньому знаходиться.
const selector = 'UIATarget.localTarget().frontMostApp().mainWindow().buttons()[0]'
const button = await $(`ios=${selector}`)
await button.click()
Ви також можете використовувати пошук предикатів у iOS UI Automation в Appium, щоб ще більше уточнити вибір елементів. Детальніше див. тут.
iOS XCUITest predicate strings and class chains
З iOS 10 і вище (використовуючи драйвер XCUITest
), ви можете використовувати рядки предикатів:
const selector = `type == 'XCUIElementTypeSwitch' && name CONTAINS 'Allow'`
const switch = await $(`-ios predicate string:${selector}`)
await switch.click()
const selector = '**/XCUIElementTypeCell[`name BEGINSWITH "D"`]/**/XCUIElementTypeButton'
const button = await $(`-ios class chain:${selector}`)
await button.click()
Accessibility ID
Стратегія локатора accessibility id
розроблена для зчитування унікального ідентифікатора елемента UI. Це має перевагу в тому, що він не змінюється під час локалізації або будь-якого іншого процесу, який може змінити текст. Крім того, це може допомогти в створенні крос-платформенних тестів, якщо елементи, які функціонально однакові, мають однаковий accessibility id.
- Для iOS це
accessibility identifier
, визначений Apple тут. - Для Android
accessibility id
відповідаєcontent-description
для елемента, як описано тут.
Для обох платформ отримання елемента (або кількох елементів) за їх accessibility id
зазвичай є найкращим методом. Це також більш переважний спосіб у порівнянні з застарілою стратегією name
.
const elem = await $('~my_accessibility_identifier')
await elem.click()
Class Name
Стратегія class name
- це рядок
, що представляє елемент UI в поточному вигляді.
- Для iOS це повна назва класу UIAutomation, яка починається з
UIA-
, наприклад,UIATextField
для текстового поля. Повний довідник можна знайти тут. - Для Android це повністю кваліфікована назва класу UI Automator, наприклад,
android.widget.EditText
для текстового поля. Повний довідник можна знайти тут. - Для Youi.tv це повна назва класу Youi.tv, яка починається з
CYI-
, наприклад,CYIPushButtonView
для елемента кнопки. Повний довідник можна знайти на сторінці GitHub драйвера You.i Engine
// iOS example
await $('UIATextField').click()
// Android example
await $('android.widget.DatePicker').click()
// Youi.tv example
await $('CYIPushButtonView').click()
Chain Selectors
Якщо ви хочете бути більш конкретним у своєму запиті, ви можете ланцюжком з'єднувати селектори, поки не знайдете потрібний елемент. Якщо ви викликаєте element
перед фактичною командою, WebdriverIO починає запит з цього елемента.
Наприклад, якщо у вас є структура DOM як:
<div class="row">
<div class="entry">
<label>Product A</label>
<button>Add to cart</button>
<button>More Information</button>
</div>
<div class="entry">
<label>Product B</label>
<button>Add to cart</button>
<button>More Information</button>
</div>
<div class="entry">
<label>Product C</label>
<button>Add to cart</button>
<button>More Information</button>
</div>
</div>
І ви хочете додати продукт B у кошик, було б складно зробити це, просто використовуючи CSS-селектор.
З ланцюжком селекторів це набагато простіше. Просто звужуйте бажаний елемент крок за кроком:
await $('.row .entry:nth-child(2)').$('button*=Add').click()
Appium Image Selector
Використовуючи стратегію локатора -image
, можна надіслати Appium файл зображення, що представляє елемент, до якого ви хочете отримати доступ.
Підтримувані формати файлів: jpg,png,gif,bmp,svg
Повний довідник можна знайти тут
const elem = await $('./file/path/of/image/test.jpg')
await elem.click()
Примітка: Принцип роботи Appium з цим селектором полягає в тому, що він внутрішньо зробить (app)screenshot і використовуватиме наданий селектор зображення для перевірки, чи можна знайти елемент на цьому (app)screenshot.
Майте на увазі, що Appium може змінити розмір зробленого (app)screenshot, щоб він відповідав CSS-розміру вашого (app)screen (це відбувається на iPhone, а також на Mac-машинах з дисплеєм Retina, оскільки DPR більше 1). Це призведе до того, що співпадіння не буде знайдено, оскільки наданий селектор зображення міг бути взятий з оригінального знімка екрана. Ви можете виправити це, оновивши налаштування сервера Appium, див. документацію Appium для налаштувань та цей коментар з детальним поясненням.
React Selectors
WebdriverIO надає спосіб вибору компонентів React на основі імені компонента. Для цього у вас є вибір двох команд: react$
та react$$
.
Ці команди дозволяють вибирати компоненти з React VirtualDOM і повертати або один елемент WebdriverIO, або масив елементів (залежно від того, яка функція використовується).
Примітка: Команди react$
та react$$
схожі за функціональністю, за винятком того, що react$$
повертає всі співпадаючі екземпляри як масив елементів WebdriverIO, а react$
повертає перший знайдений екземпляр.
Базовий приклад
// index.jsx
import React from 'react'
import ReactDOM from 'react-dom'
function MyComponent() {
return (
<div>
MyComponent
</div>
)
}
function App() {
return (<MyComponent />)
}
ReactDOM.render(<App />, document.querySelector('#root'))
У наведеному вище коді є простий екземпляр MyComponent
всередині додатку, який React рендерить у HTML-елементі з id="root"
.
За допомогою команди browser.react$
ви можете вибрати екземпляр MyComponent
:
const myCmp = await browser.react$('MyComponent')
Тепер, коли у вас є елемент WebdriverIO, збережений у змінній myCmp
, ви можете виконувати команди елементів проти нього.
Фільтрація компонентів
Бібліотека, яку WebdriverIO використовує внутрішньо, дозволяє фільтрувати ваш вибір за властивостями та/або станом компонента. Для цього вам потрібно передати другий аргумент для властивостей та/або третій аргумент для стану в команду браузера.
// index.jsx
import React from 'react'
import ReactDOM from 'react-dom'
function MyComponent(props) {
return (
<div>
Hello { props.name || 'World' }!
</div>
)
}
function App() {
return (
<div>
<MyComponent name="WebdriverIO" />
<MyComponent />
</div>
)
}
ReactDOM.render(<App />, document.querySelector('#root'))
Якщо ви хочете вибрати екземпляр MyComponent
, який має властивість name
зі значенням WebdriverIO
, ви можете виконати команду таким чином:
const myCmp = await browser.react$('MyComponent', {
props: { name: 'WebdriverIO' }
})
Якщо ви хочете фільтрувати вибір за станом, команда browser
буде виглядати приблизно так:
const myCmp = await browser.react$('MyComponent', {
state: { myState: 'some value' }
})
Робота з React.Fragment
При використанні команди react$
для вибору React фрагментів, WebdriverIO поверне першу дочірню компоненту цього компонента як вузол компонента. Якщо ви використовуєте react$$
, ви отримаєте масив, що містить всі HTML-вузли всередині фрагментів, які відповідають селектору.
// index.jsx
import React from 'react'
import ReactDOM from 'react-dom'
function MyComponent() {
return (
<React.Fragment>
<div>
MyComponent
</div>
<div>
MyComponent
</div>
</React.Fragment>
)
}
function App() {
return (<MyComponent />)
}
ReactDOM.render(<App />, document.querySelector('#root'))
На основі наведеного вище прикладу, ось як працюватимуть команди:
await browser.react$('MyComponent') // повертає WebdriverIO Element для першого <div />
await browser.react$$('MyComponent') // повертає WebdriverIO Elements для масиву [<div />, <div />]
Примітка: Якщо у вас кілька екземплярів MyComponent
і ви використовуєте react$$
для вибору цих компонентів фрагментів, вам буде повернено одновимірний масив усіх вузлів. Іншими словами, якщо у вас є 3 екземпляри <MyComponent />
, вам буде повернено масив з шістьма елементами WebdriverIO.
Custom Selector Strategies
Якщо ваш додаток вимагає специфічного способу отримання елементів, ви можете самостійно визначити спеціальну стратегію селектора, яку ви можете використовувати з custom$
та custom$$
. Для цього зареєструйте свою стратегію один раз на початку тесту, наприклад, у гачку before
:
loading...
Дано наступний HTML-фрагмент:
loading...
Потім використовуйте його, викликавши:
loading...
Примітка: це працює лише в веб-середовищі, в якому можна запустити команду execute
.