Селектори
WebDriver протокол надає кілька типів селекторів для пошуку елемента. WebdriverIO спрощує їх, щоб зробити пошук елементів простішим. Зауважте, що попри те, що команди для пошуку елементів називаються $
та $$
, вони не мають нічого спільного з jQuery або Sizzle Selector Engine.
Зауважте, що не всі із великої кількості типів селекторів можуть забезпечити, надійний пошук потрібного вам елемента. Наприклад, маючи таку кнопку:
<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"]') | ✅ Можна | Вимагає додаткових атрибутів, не пов'язаних із доступністю. |
$('aria/Submit') or $('button=Submit') | ✅ Завжди | Найкращий. Демонструє те, як користувач взаємодіє зі сторінкою. |
CSS селектори
Якщо не вказано інше, WebdriverIO шукатиме елементи за допомогою CSS селекторів, наприклад:
loading...
Текст посилання
Щоб отримати елемент посилання із певним текстом у ньому, вкажіть текст, починаючи зі знака рівності (=
).
Наприклад:
loading...
Ви можете знайти цей елемент, викликавши:
loading...
Частковий текст посилання
Щоб знайти елемент посилання, текст якого частково містить текст що ви шукаєте, використайте *=
перед вашим текстом (наприклад *=driver
).
Ви можете знайти елемент із прикладу вище, викликавши:
loading...
Примітка: Ви не можете поєднувати кілька типів пошуку в одному селекторі. Використовуйте кілька послідовних пошуків елементів для досягнення цієї мети, наприклад:
const elem = await $('header h1*=Welcome') // doesn't work!!!
// use instead
const elem = await $('header').$('*=driver')
Елемент з певним текстом
Цю ж техніку можна застосувати і до елементів.
Наприклад, ось запит для заголовка рівня 1 із текстом "Welcome to my Page":
loading...
Ви можете знайти цей елемент, викликавши:
loading...
Або використовуючи пошук за частковим збігом тексту:
loading...
Те саме працює для атрибутів id
та class
:
loading...
Ви можете знайти цей елемент, викликавши:
loading...
Примітка: Ви не можете поєднувати кілька типів пошуку в одному селекторі. Використовуйте кілька послідовних пошуків елементів для досягнення цієї мети, наприклад:
const elem = await $('header h1*=Welcome') // doesn't work!!!
// use instead
const elem = await $('header').$('h1*=Welcome')
Назва тегу
Щоб знайти елемент із певною назвою тегу, використовуйте <tag>
або <tag />
.
loading...
Ви можете знайти цей елемент, викликавши:
loading...
Атрибут name
Для запиту елементів із певним атрибутом name ви можете використовувати звичайний CSS селектор або спеціальний тип пошуку за цим атрибутом, що реалізований у JSONWire протоколі, вказавши щось на зразок [name="some-name"]
у своєму селекторі:
loading...
loading...
Примітка: Цей тип пошуку застарів та працює лише в старих браузерах, які працюють із JSONWire протоколом або з Appium.
xPath
Отримати доступ до елемента можна також через xPath.
Селектор xPath має такий формат: //body/div[6]/div[1]/span[1]
.
loading...
Ви можете знайти другий абзац, викликавши:
loading...
Ви також можете використовувати xPath для переходу вгору та вниз DOM деревом:
loading...
Ім'я доступності
Шукайте елементи за їхніми іменами доступності. Ім'я доступності – це те, що озвучується програмою зчитування з екрана, коли цей елемент отримує фокус. Значенням імені доступності може бути як візуальний вміст, так і прихований текст.
Ви можете прочитати більше про цей селектор у нашому блозі
Отримати за aria-label
loading...
loading...
Отримати за aria-labelledby
loading...
loading...
Отримати за вмістом
loading...
loading...
Отримати за назвою
loading...
loading...
Отримати за alt
атрибутом
loading...
loading...
ARIA - атрибут ролі
Для пошуку елементів на основі ARIA ролейви можете безпосередньо вказати роль елемента, як [role=button]
у селекторі:
loading...
loading...
Атрибут ID
Тип пошуку «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 Функція
Ви також можете використовувати функції JavaScript для пошуку елементів використовуючи вбудоване API. Звичайно, ви можете зробити це лише всередині вебконтексту (наприклад у браузері або вебконтексті на мобільному пристрої).
Маючи наступну структуру HTML:
loading...
Ви можете запитати сусідній від #elem
елемент наступним чином:
loading...
Глибокі селектори
Багато вебзастосунків інтегрують елементи із тіньовим DOM. Без обхідних шляхів пошук елементів у тіньовому DOM є технічно неможливим. shadow$
і shadow$$
були такими обхідними шляхами, які мали свої обмеження. Але тепер за допомогою глибокого селектора ви можете шукати елементи всередині будь-якого тіньового DOM використовуючи стандартну функцію для пошуку.
Маючи вебзастосунок із такою структурою:
За допомогою цього селектора ви можете знайти елемент <button />
, який розташований в іншому тіньову DOM, наприклад:
loading...
Мобільні селектори
Для гібридного мобільного тестування важливо, щоб сервер автоматизації був у потрібному контексті перед виконанням команд. Для автоматизації жестів драйвер в ідеалі має бути налаштований на головний контекст. Але щоб знайти елементи у DOM, драйвер потрібно налаштувати на контекст вебпереглядача. Лише тоді можна використовувати методи, згадані вище.
Для нативного мобільного тестування не потрібно змі нювати контексти, оскільки вам потрібно використовувати спеціальні мобільні типи селекторів та використовувати безпосередньо базову технологію автоматизації пристрою. Це особливо корисно, коли тест потребує тонкого контролю над пошуком елементів.
Android UiAutomator
Платформа Android UI Automator надає кілька типів пошуку елементів. Ви можете використовувати UI Automator API, зокрема клас UiSelector для пошуку елементів. В Appium ви надсилаєте рядок із Java кодом на сервер, який виконує його в середовищі мобільного додатку, повертаючи елемент або декілька елементів.
const selector = 'new UiSelector().text("Cancel").className("android.widget.Button")'
const button = await $(`android=${selector}`)
await button.click()
Android DataMatcher і ViewMatcher (тільки Espresso)
Тип DataMatcher від Espresso забезпечує пошук елементів за DataMatcher
const menuItem = await $({
"name": "hasEntry",
"args": ["title", "ViewTitle"]
})
await menuItem.click()
І аналогічно ViewMatcher
const menuItem = await $({
"name": "hasEntry",
"args": ["title", "ViewTitle"],
"class": "androidx.test.espresso.matcher.ViewMatchers"
})
await menuItem.click()
Android View Tag (тільки Espresso)
Тип View Tag забезпечує зручний спосіб пошуку елементів за їхнім тегом.
const elem = await $('-android viewtag:tag_identifier')
await elem.click()
iOS UIAutomation
Під час автоматизації iOS застосунків для пошуку елементів можна використовувати Apple UI Automation фреймворк.
Це JavaScript API має методи доступу до представлення елемента та всього, що в ньому міститься.
const selector = 'UIATarget.localTarget().frontMostApp().mainWindow().buttons()[0]'
const button = await $(`ios=${selector}`)
await button.click()
Ви також можете використовувати предикатний пошук з iOS UI Automation в Appium, щоб ще більш точно вибирати елементи. Дивіться тут щоб дізнатися більше.
iOS XCUITest рядки предикатів і ланцюжки класів
З 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
призначена для пошуку за унікальним ідентифікатором елемента інтерфейсу користувача. Цей спосіб має перевагу, оскільки ідентифікатор не змінюється під час локалізації чи будь-якого іншого процесу. Крім того, це може бути корисно при створенні кросплатформних тестів, коли функціонально однакові елементи мають однаковий ідентифікатор доступності.
- Для iOS це
accessibility identifier
викладений Apple тут. - Для Android
accessibility id
відповідаєcontent-description
елемента, як описано тут.
Для обох платформ зазвичай найкращим методом є пошук елемента (або кількох елементів) за їхнім accessibility id
. Використання цього типу селекторів, також є більш бажаним за використання застарілого типу name
.
const elem = await $('~my_accessibility_identifier')
await elem.click()
Назва класу
Назва класу — це рядок, який представляє елемент інтерфейсу користувача у поточному контексті.
- Для iOS це повна назва класу UIAutomation, що починається з
UIA-
, наприкладUIATextField
для текстового поля. Повну довідку можна знайти тут. - Для Android це повна назва UI Automator класу, наприклад
android.widget.EditText
для текстового поля. Повну довідку можна знайти тут. - Для Youi.tv це повна назва класу Youi.tv і що починається з
CYI-
, наприкладCYIPushButtonView
для елемента кнопки. Повну довідку можна знайти на GitHub сторінці You.i Engine Driver
// iOS example
await $('UIATextField').click()
// Android example
await $('android.widget.DatePicker').click()
// Youi.tv example
await $('CYIPushButtonView').click()
Ланцюжок селекторів
Якщо ви хочете більше конкретизувати свій пошук, ви можете об'єднувати селектори, доки не дійдете до потрібного елементу. Якщо ви маєте інший елемент перед командою пошуку, 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
, можна надіслати Appium файл зображення, що представляє елемент, до якого ви хочете отримати доступ.
Підтримувані формати файлів jpg,png,gif,bmp,svg
Повну довідку можна знайти тут
const elem = await $('./file/path/of/image/test.jpg')
await elem.click()
Примітка: Спосіб, у який Appium працює з цим селектором, полягає в тому, що він створює знімок екрана (застосунку) і використовує надане зображення, щоб перевірити, чи можна знайти елемент на знімку екрана (застосунку).
Майте на увазі, що Appium може змінити розмір зробленого знімка екрана (застосунку), щоб він відповідав CSS-розміру вашого екрана (застосунку) (це обов'язково станеться на iPhone, а також на комп’ютерах Mac із дисплеєм Retina, оскільки DPR більший ніж 1). Це призведе до того, що збіг не буде знайдено, оскільки наданий селектор зображення міг бути взятий з оригінального знімк а екрана. Ви можете виправити це, оновивши налаштування сервера Appium, перегляньте документацію Appium для налаштувань і цей коментар з докладним поясненням.
Селектори React
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' }
})
Якщо ви хочете відфільтрувати компоненти за станом, команда виглядатиме приблизно так:
const myCmp = await browser.react$('MyComponent', {
state: { myState: 'some value' }
})
Робота з React.Fragment
У разі використання команди react$
для вибору React fragments 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') // returns the WebdriverIO Element for the first <div />
await browser.react$$('MyComponent') // returns the WebdriverIO Elements for the array [<div />, <div />]
Примітка: Якщо у вас є кілька екземплярів MyComponent
і ви використовуєте react$$
для пошуку цих компонентів-фрагментів, вам буде повернено масив усіх елементів. Іншими словами, якщо у вас є 3 екземпляри <MyComponent />
, вам буде повернено масив із шістьма елементами WebdriverIO.
Користувацькі селектори
Якщо для вашого застосунку потрібен особливий спосіб пошуку елементів, ви можете визначити власний тип селектора, який можна вик ористовувати за допомогою custom$
і custom$$
. Для цього зареєструйте свій тип селектора один раз на початку тесту:
browser.addLocatorStrategy('myCustomStrategy', (selector, root) => {
/**
* scope should be document if called on browser object
* and `root` if called on an element object
*/
const scope = root ? root : document
return scope.querySelectorAll(selector)
})
Маючи наступну структуру HTML:
<div class="foobar" id="first">
<div class="foobar" id="second">
barfoo
</div>
</div>
Потім використовуйте його, викликавши:
const elem = await browser.custom$('myCustomStrategy', '.foobar')
console.log(await elem.getAttribute('id')) // returns "first"
const nestedElem = await elem.custom$('myCustomStrategy', '.foobar')
console.log(await elem.getAttribute('id')) // returns "second"
Примітка: це працюватиме лише у вебсередовищі, де можна запустити команду execute
.