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

Селекторы

Протокол WebDriver предоставляет несколько стратегий селекторов для запроса элементов. WebdriverIO упрощает их, чтобы выбор элементов оставался простым. Обратите внимание, что хотя команды для запроса элементов называются $ и $$, они не имеют ничего общего с jQuery или движком селекторов Sizzle.

Хотя доступно так много различных селекторов, только некоторые из них обеспечивают надежный способ найти правильный элемент. Например, для следующей кнопки:

<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')

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

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

Например, вот запрос для заголовка уровня 1 с текстом "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, либо предоставленную стратегию имени из 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 за вас. Рекомендуется перейти с этого селектора, удалив >>> перед ним.

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

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

Chrome Example

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

selectors/example.js
loading...

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

Для гибридного мобильного тестирования важно, чтобы сервер автоматизации находился в правильном контексте перед выполнением команд. Для автоматизации жестов драйвер в идеале должен быть установлен в нативный контекст. Но для выбора элементов из DOM драйвер должен быть установлен в контекст webview платформы. Только после этого можно использовать упомянутые выше методы.

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

Android UiAutomator

Фреймворк UI Automator для Android предоставляет несколько способов поиска элементов. Вы можете использовать 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)

Стратегия DataMatcher Android предоставляет способ поиска элементов по 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 от 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

Стратегия class name - это строка, представляющая элемент пользовательского интерфейса в текущем представлении.

  • Для iOS это полное имя класса UIAutomation, и будет начинаться с UIA-, например, UIATextField для текстового поля. Полную справку можно найти здесь.
  • Для Android это полностью квалифицированное имя класса UI Automator class, например, android.widget.EditText для текстового поля. Полную справку можно найти здесь.
  • Для Youi.tv это полное имя класса Youi.tv, и будет начинаться с CYI-, например, CYIPushButtonView для элемента кнопки. Полную справку можно найти на странице GitHub драйвера You.i Engine
// пример 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