Перейти к основному содержанию

Селекторы

Протокол 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')✅ ХорошоХорошо. Отражает, как пользователь взаимодействует со страницей. Рекомендуется использовать файлы перевода, чтобы ваши тесты не ломались при обновлении переводов. Примечание: Этот селектор может работать медленнее других на больших страницах.
$('button=Submit')✅ ВсегдаЛучший. Отражает, как пользователь взаимодействует со страницей и работает быстро. Рекомендуется использовать файлы перевода, чтобы ваши тесты не ломались при обновлении переводов.

CSS селектор запроса

Если не указано иное, WebdriverIO будет запрашивать элементы с использованием шаблона CSS селектора, например:

selectors/example.js
loading...

Текст ссылки

Чтобы получить элемент привязки с определенным текстом, запрашивайте текст, начинающийся со знака равенства (=).

Например:

selectors/example.html
loading...

Вы можете запросить этот элемент, вызвав:

selectors/example.js
loading...

Частичный текст ссылки

Чтобы найти элемент привязки, чей видимый текст частично совпадает с вашим значением поиска, запросите его, используя *= перед строкой запроса (например, *=driver).

Вы также можете запросить элемент из примера выше, вызвав:

selectors/example.js
loading...

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

const elem = await $('header h1*=Welcome') // не работает!!!
// используйте вместо этого
const elem = await $('header').$('*=driver')

Элемент с определенным текстом

Та же техника может быть применена и к элементам. Кроме того, также возможно выполнить сопоставление без учета регистра с использованием .= или .*= в запросе.

Например, вот запрос для заголовка первого уровня с текстом "Welcome to my Page":

selectors/example.html
loading...

Вы можете запросить этот элемент, вызвав:

selectors/example.js
loading...

Или используя запрос частичного текста:

selectors/example.js
loading...

То же самое работает для имен id и class:

selectors/example.html
loading...

Вы можете запросить этот элемент, вызвав:

selectors/example.js
loading...

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

const elem = await $('header h1*=Welcome') // не работает!!!
// используйте вместо этого
const elem = await $('header').$('h1*=Welcome')

Имя тега

Чтобы запросить элемент с определенным именем тега, используйте <tag> или <tag />.

selectors/example.html
loading...

Вы можете запросить этот элемент, вызвав:

selectors/example.js
loading...

Атрибут Name

Для запроса элементов с определенным атрибутом name вы можете использовать обычный селектор CSS3 или предоставленную стратегию name из JSONWireProtocol, передав что-то вроде [name="some-name"] в качестве параметра селектора:

selectors/example.html
loading...
selectors/example.js
loading...

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

xPath

Также возможно запрашивать элементы с помощью определенного xPath.

Селектор xPath имеет формат типа //body/div[6]/div[1]/span[1].

selectors/xpath.html
loading...

Вы можете запросить второй параграф, вызвав:

selectors/example.js
loading...

Вы можете использовать xPath для перемещения вверх и вниз по дереву DOM:

selectors/example.js
loading...

Селектор доступного имени

Запрос элементов по их доступному имени. Доступное имя - это то, что объявляется программой чтения с экрана, когда этот элемент получает фокус. Значение доступного имени может быть как визуальным контентом, так и скрытыми текстовыми альтернативами.

информация

Вы можете прочитать больше об этом селекторе в нашем блоге о релизе

Получение по aria-label

selectors/aria.html
loading...
selectors/example.js
loading...

Получение по aria-labelledby

selectors/aria.html
loading...
selectors/example.js
loading...

Получение по содержимому

selectors/aria.html
loading...
selectors/example.js
loading...

Получение по заголовку

selectors/aria.html
loading...
selectors/example.js
loading...

Получение по свойству alt

selectors/aria.html
loading...
selectors/example.js
loading...

ARIA - Атрибут Role

Для запроса элементов на основе ролей ARIA, вы можете напрямую указать роль элемента, например, [role=button] в качестве параметра селектора:

selectors/aria.html
loading...
selectors/example.js
loading...

Атрибут ID

Стратегия локатора "id" не поддерживается в протоколе WebDriver, вместо этого следует использовать стратегии селекторов CSS или xPath для поиска элементов по ID.

Однако некоторые драйверы (например, Appium You.i Engine Driver) могут поддерживать этот селектор.

Текущие поддерживаемые синтаксисы селектора для ID:

//css локатор
const button = await $('#someid')
//xpath локатор
const button = await $('//*[@id="someid"]')
//стратегия id
// Примечание: работает только в Appium или подобных фреймворках, поддерживающих стратегию локатора "ID"
const button = await $('id=resource-id/iosname')

JS функция

Вы также можете использовать функции JavaScript для получения элементов с использованием нативных веб-API. Конечно, вы можете делать это только в веб-контексте (например, browser или веб-контекст на мобильных устройствах).

Учитывая следующую структуру HTML:

selectors/js.html
loading...

Вы можете запросить соседний элемент #elem следующим образом:

selectors/example.js
loading...

Глубокие селекторы

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

Начиная с v9 WebdriverIO, нет необходимости в этом специальном селекторе, так как WebdriverIO автоматически проникает через Shadow DOM для вас. Рекомендуется отказаться от этого селектора, удалив >>> перед ним.

Многие фронтенд-приложения сильно зависят от элементов с shadow DOM. Технически невозможно запрашивать элементы внутри shadow DOM без обходных путей. Функции shadow$ и shadow$$ были такими обходными путями, которые имели свои ограничения. С помощью глубокого селектора теперь вы можете запрашивать все элементы внутри любого shadow DOM, используя общую команду запроса.

Предположим, у нас есть приложение со следующей структурой:

Chrome Example

С этим селектором вы можете запросить элемент <button />, который вложен в другой shadow DOM, например:

selectors/example.js
loading...

Мобильные селекторы

Для гибридного мобильного тестирования важно, чтобы сервер автоматизации находился в правильном контексте перед выполнением команд. Для автоматизации жестов драйвер в идеале должен быть установлен в нативный контекст. Но для выбора элементов из 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 и ViewMatcher (только Espresso)

Стратегия 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)

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

const elem = await $('-android viewtag:tag_identifier')
await elem.click()

iOS UIAutomation

При автоматизации приложения iOS можно использовать UI Automation framework от Apple для поиска элементов.

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

  • Для iOS это полное имя класса UIAutomation, и оно будет начинаться с UIA-, например, UIATextField для текстового поля. Полный справочник можно найти здесь.
  • Для Android это полностью квалифицированное имя класса UI Automator class, такое как android.widget.EditText для текстового поля. Полный справочник можно найти здесь.
  • Для Youi.tv это полное имя класса Youi.tv, и оно будет начинаться с CYI-, например, CYIPushButtonView для элемента кнопки push. Полный справочник можно найти на странице GitHub You.i Engine Driver
// пример iOS
await $('UIATextField').click()
// пример Android
await $('android.widget.DatePicker').click()
// пример Youi.tv
await $('CYIPushButtonView').click()

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

Если вы хотите быть более конкретным в своем запросе, вы можете объединять селекторы в цепочку, пока не найдете нужный элемент. Если вы вызываете 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, можно отправить Appium файл изображения, представляющий элемент, к которому вы хотите получить доступ.

Поддерживаемые форматы файлов: jpg,png,gif,bmp,svg

Полный справочник можно найти здесь

const elem = await $('./file/path/of/image/test.jpg')
await elem.click()

Примечание: То, как Appium работает с этим селектором, заключается в том, что он внутренне делает (app)скриншот и использует предоставленный селектор изображения для проверки, может ли элемент быть найден на этом (app)скриншоте.

Имейте в виду, что Appium может изменить размер сделанного (app)скриншота, чтобы он соответствовал CSS-размеру вашего (app)экрана (это произойдет на 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' }
})

Если вы хотите фильтровать наш выбор по состоянию, команда 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 для первого <div />
await browser.react$$('MyComponent') // возвращает элементы WebdriverIO для массива [<div />, <div />]

Примечание: Если у вас несколько экземпляров MyComponent и вы используете react$$ для выбора этих компонентов фрагментов, вам будет возвращен одномерный массив всех узлов. Другими словами, если у вас есть 3 экземпляра <MyComponent />, вам будет возвращен массив с шестью элементами WebdriverIO.

Пользовательские стратегии селекторов

Если ваше приложение требует определенного способа получения элементов, вы можете определить свою собственную стратегию селектора, которую можно использовать с custom$ и custom$$. Для этого зарегистрируйте свою стратегию один раз в начале теста, например, в хуке before:

queryElements/customStrategy.js
loading...

Учитывая следующий HTML-фрагмент:

queryElements/example.html
loading...

Затем используйте его, вызвав:

queryElements/customStrategy.js
loading...

Примечание: это работает только в веб-среде, в которой может быть запущена команда execute.

Welcome! How can I help?

WebdriverIO AI Copilot