Selektory
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>
Oto, czego zalecamy i czego nie zalecamy używać jako selektorów:
Selektor | Zalecane | Uwagi |
---|---|---|
$('button') | 🚨 Nigdy | Najgorzej - zbyt ogólny, brak kontekstu. |
$('.btn.btn-large') | 🚨 Nigdy | Źle. Powiązane ze stylami. Wysoce podatne na zmiany. |
$('#main') | ⚠️ Oszczędnie | Lepiej. Ale nadal powiązane ze stylami lub nasłuchiwaczami zdarzeń JS. |
$(() => document.queryElement('button')) | ⚠️ Oszczędnie | Skuteczne zapytanie, złożone do napisania. |
$('button[name="submission"]') | ⚠️ Oszczędnie | Związane z atrybutem name , który ma semantykę HTML. |
$('button[data-testid="submit"]') | ✅ Dobrze | Wymaga dodatkowego atrybutu, niezwiązanego z a11y. |
$('aria/Submit') lub $('button=Submit') | ✅ Zawsze | Najlepiej. Odzwierciedla, jak użytkownik wchodzi w interakcję ze stroną. Zaleca się używanie plików tłumaczeń frontendu, aby testy nigdy nie zawiodły, gdy tłumaczenia zostaną zaktualizowane |
Selektor zapytań CSS
Jeśli nie wskazano inaczej, WebdriverIO będzie wyszukiwać elementy za pomocą wzorca selektora CSS, np.:
loading...
Tekst łącza
Aby uzyskać element kotwicy z określonym tekstem, wyszukaj tekst zaczynający się od znaku równości (=
).
Na przykład:
loading...
Możesz zapytać o ten element, wywołując:
loading...
Częściowy tekst łącza
Aby znaleźć element kotwicy, którego widoczny tekst częściowo pasuje do szukanej wartości,
wyszukaj go, używając *=
przed ciągiem zapytania (np. *=driver
).
Możesz zapytać o element z powyższego przykładu, wywołując również:
loading...
Uwaga: Nie można mieszać wielu strategii selektorów w jednym selektorze. Użyj wielu łańcuchowych zapytań o elementy, aby osiągnąć ten sam cel, np.:
const elem = await $('header h1*=Welcome') // nie działa!!!
// zamiast tego użyj
const elem = await $('header').$('*=driver')
Element z określonym tekstem
Ta sama technika może być zastosowana również do elementów. Dodatkowo możliwe jest również dopasowanie bez uwzględniania wielkości liter za pomocą .=
lub .*=
w zapytaniu.
Na przykład, oto zapytanie o nagłówek poziomu 1 z tekstem "Welcome to my Page":
loading...
Możesz zapytać o ten element, wywołując:
loading...
Lub używając częściowego tekstu zapytania:
loading...
To samo działa dla nazw id
i class
:
loading...
Możesz zapytać o ten element, wywołując:
loading...
Uwaga: Nie można mieszać wielu strategii selektorów w jednym selektorze. Użyj wielu łańcuchowych zapytań o elementy, aby osiągnąć ten sam cel, np.:
const elem = await $('header h1*=Welcome') // nie działa!!!
// zamiast tego użyj
const elem = await $('header').$('h1*=Welcome')
Nazwa znacznika
Aby zapytać o element z określoną nazwą znacznika, użyj <tag>
lub <tag />
.
loading...
Możesz zapytać o ten element, wywołując:
loading...
Atrybut nazwy
Do zapytania o elementy z określonym atrybutem nazwy możesz użyć normalnego selektora CSS3 lub dostarczonej strategii nazwy z JSONWireProtocol, przekazując coś takiego jak [name="some-name"] jako parametr selektora:
loading...
loading...
Uwaga: Ta strategia selektora jest przestarzała i działa tylko w starych przeglądarkach, które są uruchamiane przez protokół JSONWireProtocol lub przy użyciu Appium.
xPath
Możliwe jest również zapytanie o elementy za pomocą określonego xPath.
Selektor xPath ma format jak //body/div[6]/div[1]/span[1]
.
loading...
Możesz zapytać o drugi paragraf, wywołując:
loading...
Możesz użyć xPath, aby przemieszczać się w górę i w dół drzewa DOM:
loading...
Selektor nazwy dostępności
Wyszukiwanie elementów według ich dostępnej nazwy. Dostępna nazwa jest tym, co jest ogłaszane przez czytnik ekranu, gdy ten element otrzymuje fokus. Wartość dostępnej nazwy może być zarówno zawartością wizualną, jak i ukrytymi alternatywami tekstowymi.
Więcej informacji o tym selektorze znajdziesz w naszym poście na blogu o wydaniu
Pobieranie według aria-label
loading...
loading...
Pobieranie według aria-labelledby
loading...
loading...
Pobieranie według zawartości
loading...
loading...
Pobieranie według tytułu
loading...
loading...
Pobieranie według właściwości alt
loading...
loading...
ARIA - Atrybut roli
Do zapytania o elementy na podstawie ról ARIA, możesz bezpośrednio określić rolę elementu, np. [role=button]
jako parametr selektora:
loading...
loading...
Atrybut ID
Strategia lokalizatora "id" nie jest obsługiwana w protokole WebDriver, należy zamiast tego używać strategii selektorów CSS lub xPath, aby znaleźć elementy za pomocą ID.
Jednak niektóre sterowniki (np. Appium You.i Engine Driver) mogą nadal obsługiwać ten selektor.
Obecnie obsługiwane składnie selektorów dla ID to:
//lokalizator css
const button = await $('#someid')
//lokalizator xpath
const button = await $('//*[@id="someid"]')
//strategia id
// Uwaga: działa tylko w Appium lub podobnych frameworkach, które obsługują strategię lokalizatora "ID"
const button = await $('id=resource-id/iosname')
Funkcja JS
Możesz również używać funkcji JavaScript do pobierania elementów za pomocą natywnych interfejsów API sieci. Oczywiście możesz to zrobić tylko w kontekście sieci (np. browser
lub kontekst sieci w urządzeniach mobilnych).
Mając następującą strukturę HTML:
loading...
Możesz zapytać o element sąsiadujący #elem
w następujący sposób:
loading...
Selektory głębokie
Począwszy od wersji v9
WebdriverIO nie ma potrzeby stosowania tego specjalnego selektora, ponieważ WebdriverIO automatycznie przechodzi przez Shadow DOM. Zaleca się migrację z tego selektora poprzez usunięcie >>>
przed nim.
Wiele aplikacji frontendowych mocno polega na elementach z shadow DOM. Z technicznego punktu widzenia niemożliwe jest zapytanie o elementy w obrębie shadow DOM bez obejścia. Metody shadow$
i shadow$$
były takimi obejściami, które miały swoje ograniczenia. Dzięki głębokiemu selektorowi możesz teraz wyszukiwać wszystkie elementy w dowolnym shadow DOM za pomocą wspólnego polecenia zapytania.
Załóżmy, że mamy aplikację o następującej strukturze:
Za pomocą tego selektora możesz wyszukać element <button />
, który jest zagnieżdżony w innym shadow DOM, np.:
loading...
Selektory mobilne
W przypadku hybrydowego testowania mobilnego ważne jest, aby serwer automatyzacji znajdował się we właściwym kontekście przed wykonaniem poleceń. Do automatyzacji gestów sterownik powinien być idealnie ustawiony na natywny kontekst. Ale aby wybrać elementy z DOM, sterownik będzie musiał być ustawiony na kontekst webview platformy. Dopiero wtedy można zastosować metody wymienione powyżej.
W przypadku natywnego testowania mobilnego nie ma przełączania między kontekstami, ponieważ musisz używać strategii mobilnych i bezpośrednio korzystać z podstawowej technologii automatyzacji urządzeń. Jest to szczególnie przydatne, gdy test wymaga pewnej dokładnej kontroli nad znajdowaniem elementów.
Android UiAutomator
Framework UI Automator Androida zapewnia wiele sposobów znajdowania elementów. Możesz użyć API UI Automator, w szczególności klasy UiSelector do lokalizowania elementów. W Appium wysyłasz kod Java jako ciąg znaków do serwera, który wykonuje go w środowisku aplikacji, zwracając element lub elementy.
const selector = 'new UiSelector().text("Cancel").className("android.widget.Button")'
const button = await $(`android=${selector}`)
await button.click()
Android DataMatcher i ViewMatcher (tylko Espresso)
Strategia DataMatcher Androida zapewnia sposób znajdowania elementów za pomocą Data Matcher
const menuItem = await $({
"name": "hasEntry",
"args": ["title", "ViewTitle"]
})
await menuItem.click()
I podobnie View Matcher
const menuItem = await $({
"name": "hasEntry",
"args": ["title", "ViewTitle"],
"class": "androidx.test.espresso.matcher.ViewMatchers"
})
await menuItem.click()
Android View Tag (tylko Espresso)
Strategia znacznika widoku zapewnia wygodny sposób znajdowania elementów według ich tagu.
const elem = await $('-android viewtag:tag_identifier')
await elem.click()
iOS UIAutomation
Podczas automatyzacji aplikacji iOS można użyć frameworka UI Automation firmy Apple do znajdowania elementów.
To API JavaScript zawiera metody dostępu do widoku i wszystkiego, co się na nim znajduje.
const selector = 'UIATarget.localTarget().frontMostApp().mainWindow().buttons()[0]'
const button = await $(`ios=${selector}`)
await button.click()
Możesz również użyć wyszukiwania predikatów w iOS UI Automation w Appium, aby jeszcze bardziej udoskonalić wybór elementów. Szczegóły znajdziesz tutaj.
iOS XCUITest ciągi predykatów i łańcuchy klas
W systemie iOS 10 i nowszych (używając sterownika XCUITest
) możesz używać ciągów predykatów:
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
Strategia lokalizatora accessibility id
jest zaprojektowana do odczytu unikalnego identyfikatora dla elementu UI. Ma to tę zaletę, że nie zmienia się podczas lokalizacji lub jakiegokolwiek innego procesu, który może zmienić tekst. Dodatkowo może to być pomocne w tworzeniu testów międzyplatformowych, jeśli elementy, które są funkcjonalnie takie same, mają ten sam identyfikator dostępności.
- Dla iOS jest to
accessibility identifier
określony przez Apple tutaj. - Dla Androida
accessibility id
mapuje się nacontent-description
dla elementu, jak opisano tutaj.
Dla obu platform uzyskanie elementu (lub wielu elementów) według ich accessibility id
jest zazwyczaj najlepszą metodą. Jest to również preferowany sposób niż przestarzała strategia name
.
const elem = await $('~my_accessibility_identifier')
await elem.click()
Nazwa klasy
Strategia class name
to string
reprezentujący element UI w bieżącym widoku.
- Dla iOS jest to pełna nazwa klasy UIAutomation i będzie zaczynać się od
UIA-
, takie jakUIATextField
dla pola tekstowego. Pełną referencję można znaleźć tutaj. - Dla Androida jest to w pełni kwalifikowana nazwa klasy UI Automator class, takie jak
android.widget.EditText
dla pola tekstowego. Pełną referencję można znaleźć tutaj. - Dla Youi.tv jest to pełna nazwa klasy Youi.tv i będzie zaczynać się od
CYI-
, takie jakCYIPushButtonView
dla elementu przycisku. Pełną referencję można znaleźć na stronie GitHub You.i Engine Driver
// przykład iOS
await $('UIATextField').click()
// przykład Android
await $('android.widget.DatePicker').click()
// przykład Youi.tv
await $('CYIPushButtonView').click()
Selektory łańcuchowe
Jeśli chcesz być bardziej precyzyjny w swoim zapytaniu, możesz łączyć selektory, aż znajdziesz właściwy element. Jeśli wywołasz element
przed właściwym poleceniem, WebdriverIO rozpocznie zapytanie od tego elementu.
Na przykład, jeśli masz strukturę DOM typu:
<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>
I chcesz dodać produkt B do koszyka, byłoby trudno zrobić to tylko za pomocą selektora CSS.
Dzięki łączeniu selektorów jest to znacznie łatwiejsze. Po prostu zawęź poszukiwany element krok po kroku:
await $('.row .entry:nth-child(2)').$('button*=Add').click()
Appium Image Selector
Używając strategii lokalizatora -image
, możliwe jest wysłanie do Appium pliku obrazu reprezentującego element, do którego chcesz uzyskać dostęp.
Obsługiwane formaty plików jpg,png,gif,bmp,svg
Pełną referencję można znaleźć tutaj
const elem = await $('./file/path/of/image/test.jpg')
await elem.click()
Uwaga: Sposób, w jaki Appium pracuje z tym selektorem, polega na tym, że wewnętrznie wykona (zrzut ekranu aplikacji) i użyje dostarczonego selektora obrazu, aby sprawdzić, czy element można znaleźć na tym (zrzucie ekranu aplikacji).
Pamiętaj, że Appium może zmienić rozmiar wykonanego (zrzutu ekranu aplikacji), aby dopasować go do rozmiaru CSS twojego (ekranu aplikacji) (stanie się to na iPhone'ach, ale także na komputerach Mac z wyświetlaczem Retina, ponieważ DPR jest większy niż 1). Spowoduje to brak dopasowania, ponieważ dostarczony selektor obrazu mógł zostać pobrany z oryginalnego zrzutu ekranu. Możesz to naprawić, aktualizując ustawienia serwera Appium, zobacz dokumentację Appium dla ustawień i ten komentarz dla szczegółowego wyjaśnienia.
Selektory React
WebdriverIO zapewnia sposób wybierania komponentów React na podstawie nazwy komponentu. Aby to zrobić, masz wybór dwóch poleceń: react$
i react$$
.
Te polecenia pozwalają wybierać komponenty z VirtualDOM React i zwracać pojedynczy element WebdriverIO lub tablicę elementów (w zależności od tego, która funkcja jest używana).
Uwaga: Polecenia react$
i react$$
są podobne funkcjonalnie, z tą różnicą, że react$$
zwróci wszystkie pasujące instancje jako tablicę elementów WebdriverIO, a react$
zwróci pierwszą znalezioną instancję.
Podstawowy przykład
// 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'))
W powyższym kodzie istnieje prosta instancja MyComponent
wewnątrz aplikacji, którą React renderuje wewnątrz elementu HTML z id="root"
.
Za pomocą polecenia browser.react$
możesz wybrać instancję MyComponent
:
const myCmp = await browser.react$('MyComponent')
Teraz, gdy masz element WebdriverIO przechowywany w zmiennej myCmp
, możesz wykonać polecenia elementu na nim.
Filtrowanie komponentów
Biblioteka, której WebdriverIO używa wewnętrznie, pozwala filtrować wybór według właściwości i/lub stanu komponentu. Aby to zrobić, musisz przekazać drugi argument dla właściwości i/lub trzeci argument dla stanu do polecenia przeglądarki.
// 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'))
Jeśli chcesz wybrać instancję MyComponent
, która ma właściwość name
jako WebdriverIO
, możesz wykonać polecenie w następujący sposób:
const myCmp = await browser.react$('MyComponent', {
props: { name: 'WebdriverIO' }
})
Jeśli chciałbyś filtrować wybór według stanu, polecenie browser
wyglądałoby mniej więcej tak:
const myCmp = await browser.react$('MyComponent', {
state: { myState: 'some value' }
})
Obsługa React.Fragment
Podczas używania polecenia react$
do wybierania fragmentów React, WebdriverIO zwróci pierwszy podrzędny element tego komponentu jako węzeł komponentu. Jeśli używasz react$$
, otrzymasz tablicę zawierającą wszystkie węzły HTML wewnątrz fragmentów, które pasują do selektora.
// 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'))
W powyższym przykładzie, oto jak działałyby polecenia:
await browser.react$('MyComponent') // zwraca element WebdriverIO dla pierwszego <div />
await browser.react$$('MyComponent') // zwraca elementy WebdriverIO dla tablicy [<div />, <div />]
Uwaga: Jeśli masz wiele instancji MyComponent
i używasz react$$
do wybierania tych komponentów fragmentów, zostanie zwrócona jednowymiarowa tablica wszystkich węzłów. Innymi słowy, jeśli masz 3 instancje <MyComponent />
, zostanie zwrócona tablica z sześcioma elementami WebdriverIO.
Niestandardowe strategie selektorów
Jeśli Twoja aplikacja wymaga określonego sposobu pobierania elementów, możesz zdefiniować własną strategię selektora, której możesz używać z custom$
i custom$$
. W tym celu zarejestruj swoją strategię raz na początku testu, np. w hooku before
:
loading...
Mając następujący fragment HTML:
loading...
Następnie użyj tego, wywołując:
loading...
Uwaga: działa to tylko w środowisku sieciowym, w którym można uruchomić polecenie execute
.