Selektoren
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>
Wir empfehlen und empfehlen nicht die folgenden Selektoren:
| Selektor | Empfohlen | Hinweise |
|---|---|---|
$('button') | 🚨 Niemals | Schlechteste Wahl - zu generisch, kein Kontext. |
$('.btn.btn-large') | 🚨 Niemals | Schlecht. An Styling gekoppelt. Stark änderungsanfällig. |
$('#main') | ⚠️ Sparsam | Besser. Aber immer noch an Styling oder JS Event-Listener gekoppelt. |
$(() => document.queryElement('button')) | ⚠️ Sparsam | Effektive Abfrage, komplex zu schreiben. |
$('button[name="submission"]') | ⚠️ Sparsam | An das name-Attribut gekoppelt, das HTML-Semantik hat. |
$('button[data-testid="submit"]') | ✅ Gut | Erfordert zusätzliches Attribut, nicht mit a11y verbunden. |
$('aria/Submit') | ✅ Gut | Gut. Spiegelt wider, wie der Benutzer mit der Seite interagiert. Es wird empfohlen, Übersetzungsdateien zu verwenden, damit Ihre Tests nicht brechen, wenn Übersetzungen aktualisiert werden. Hinweis: Dieser Selektor kann auf großen Seiten langsamer sein als andere. |
$('button=Submit') | ✅ Immer | Am besten. Spiegelt wider, wie der Benutzer mit der Seite interagiert und ist schnell. Es wird empfohlen, Übersetzungsdateien zu verwenden, damit Ihre Tests nicht brechen, wenn Übersetzungen aktualisiert werden. |
CSS Query Selector
If not indicated otherwise, WebdriverIO will query elements using the CSS selector pattern, e.g.:
loading...
Link Text
Um ein Ankerelement mit einem bestimmten Text zu erhalten, fragen Sie den Text ab, der mit einem Gleichheitszeichen (=) beginnt.
Zum Beispiel:
loading...
Sie können dieses Element abrufen, indem Sie aufrufen:
loading...
Partial Link Text
Um ein Ankerelement zu finden, dessen sichtbarer Text teilweise mit Ihrem Suchwert übereinstimmt,
fragen Sie es ab, indem Sie *= vor die Abfragezeichenfolge setzen (z.B. *=driver).
Sie können das Element aus dem obigen Beispiel auch abrufen, indem Sie aufrufen:
loading...
Hinweis: Sie können nicht mehrere Selektorstrategien in einem Selektor mischen. Verwenden Sie mehrere verkettete Elementabfragen, um dasselbe Ziel zu erreichen, z.B.:
const elem = await $('header h1*=Welcome') // funktioniert nicht!!!
// verwenden Sie stattdessen
const elem = await $('header').$('*=driver')
Element mit bestimmtem Text
Die gleiche Technik kann auch auf Elemente angewendet werden. Zusätzlich ist es auch möglich, einen Abgleich ohne Berücksichtigung der Groß- und Kleinschreibung mit .= oder .*= innerhalb der Abfrage durchzuführen.
Zum Beispiel, hier ist eine Abfrage für eine Überschrift der Ebene 1 mit dem Text "Welcome to my Page":
loading...
Sie können dieses Element abrufen, indem Sie aufrufen:
loading...
Oder durch die Abfrage von Teiltext:
loading...
Das Gleiche funktioniert für id und class Namen:
loading...
Sie können dieses Element abrufen, indem Sie aufrufen:
loading...
Hinweis: Sie können nicht mehrere Selektorstrategien in einem Selektor mischen. Verwenden Sie mehrere verkettete Elementabfragen, um dasselbe Ziel zu erreichen, z.B.:
const elem = await $('header h1*=Welcome') // funktioniert nicht!!!
// verwenden Sie stattdessen
const elem = await $('header').$('h1*=Welcome')
Tag Name
Um ein Element mit einem bestimmten Tag-Namen abzufragen, verwenden Sie <tag> oder <tag />.
loading...
Sie können dieses Element abrufen, indem Sie aufrufen:
loading...
Name Attribute
Zum Abfragen von Elementen mit einem bestimmten Name-Attribut können Sie entweder einen normalen CSS3-Selektor oder die vom JSONWireProtocol bereitgestellte Name-Strategie verwenden, indem Sie etwas wie [name="some-name"] als Selektor-Parameter übergeben:
loading...
loading...
Hinweis: Diese Selektorstrategie ist veraltet und funktioniert nur in alten Browsern, die vom JSONWireProtocol ausgeführt werden oder bei Verwendung von Appium.
xPath
Es ist auch möglich, Elemente über einen bestimmten xPath abzufragen.
Ein xPath-Selektor hat ein Format wie //body/div[6]/div[1]/span[1].
loading...
Sie können den zweiten Absatz abrufen, indem Sie aufrufen:
loading...
Sie können xPath auch verwenden, um im DOM-Baum nach oben und unten zu navigieren:
loading...
Accessibility Name Selector
Elemente anhand ihres zugänglichen Namens abfragen. Der zugängliche Name ist das, was von einem Screenreader angekündigt wird, wenn dieses Element den Fokus erhält. Der Wert des zugänglichen Namens kann sowohl visueller Inhalt als auch versteckte Textalternativen sein.
Mehr über diesen Selektor erfahren Sie in unserem Release-Blog-Beitrag
Abrufen nach aria-label
loading...
loading...
Abrufen nach aria-labelledby
loading...
loading...
Abrufen nach Inhalt
loading...
loading...
Abrufen nach Titel
loading...
loading...
Abrufen nach alt-Eigenschaft
loading...
loading...
ARIA - Role Attribute
Zum Abfragen von Elementen basierend auf ARIA-Rollen können Sie direkt die Rolle des Elements wie [role=button] als Selektor-Parameter angeben:
loading...
loading...
ID Attribute
Die Locator-Strategie "id" wird im WebDriver-Protokoll nicht unterstützt. Man sollte stattdessen entweder CSS- oder xPath-Selektorstrategien verwenden, um Elemente anhand ihrer ID zu finden.
Allerdings könnten einige Treiber (z.B. Appium You.i Engine Driver) diesen Selektor immer noch unterstützen.
Aktuell unterstützte Selektor-Syntaxen für ID sind:
//css locator
const button = await $('#someid')
//xpath locator
const button = await $('//*[@id="someid"]')
//id strategy
// Hinweis: funktioniert nur in Appium oder ähnlichen Frameworks, die die Locator-Strategie "ID" unterstützen
const button = await $('id=resource-id/iosname')
JS Function
Sie können auch JavaScript-Funktionen verwenden, um Elemente mit nativen Web-APIs abzurufen. Natürlich können Sie dies nur innerhalb eines Web-Kontexts tun (z.B. browser oder Web-Kontext in mobilen Anwendungen).
Gegeben die folgende HTML-Struktur:
loading...
Sie können das Geschwisterelement von #elem wie folgt abfragen:
loading...
Deep Selectors
Ab Version v9 von WebdriverIO gibt es keinen Bedarf mehr für diesen speziellen Selektor, da WebdriverIO automatisch durch den Shadow DOM für Sie hindurchgeht. Es wird empfohlen, von diesem Selektor wegzumigrieren, indem Sie das >>> davor entfernen.
Viele Frontend-Anwendungen verlassen sich stark auf Elemente mit Shadow DOM. Es ist technisch unmöglich, Elemente innerhalb des Shadow DOM ohne Umwege abzufragen. Die shadow$ und shadow$$ waren solche Umwege, die ihre Einschränkungen hatten. Mit dem Deep-Selektor können Sie jetzt alle Elemente innerhalb eines Shadow DOM mit dem üblichen Abfragebefehl abfragen.
Angenommen, wir haben eine Anwendung mit der folgenden Struktur:

Mit diesem Selektor können Sie das <button /> Element abfragen, das in einem anderen Shadow DOM verschachtelt ist, z.B.:
loading...
Mobile Selectors
Für hybrides mobiles Testen ist es wichtig, dass sich der Automatisierungsserver im richtigen Kontext befindet, bevor Befehle ausgeführt werden. Für die Automatisierung von Gesten sollte der Treiber idealerweise auf den nativen Kontext eingestellt sein. Um jedoch Elemente aus dem DOM auszuwählen, muss der Treiber auf den Webview-Kontext der Plattform eingestellt werden. Erst dann können die oben genannten Methoden verwendet werden.
Für natives mobiles Testen gibt es keinen Wechsel zwischen Kontexten, da Sie mobile Strategien verwenden und direkt auf die zugrunde liegende Geräteautomatisierungstechnologie zugreifen müssen. Dies ist besonders nützlich, wenn ein Test eine feinere Kontrolle über das Finden von Elementen benötigt.
Android UiAutomator
Android's UI Automator-Framework bietet eine Reihe von Möglichkeiten, Elemente zu finden. Sie können die UI Automator API, insbesondere die UiSelector-Klasse verwenden, um Elemente zu lokalisieren. In Appium senden Sie den Java-Code als String an den Server, der ihn in der Umgebung der Anwendung ausführt und das Element oder die Elemente zurückgibt.
const selector = 'new UiSelector().text("Cancel").className("android.widget.Button")'
const button = await $(`android=${selector}`)
await button.click()
Android DataMatcher and ViewMatcher (nur Espresso)
Android's DataMatcher-Strategie bietet eine Möglichkeit, Elemente mit Data Matcher zu finden
const menuItem = await $({
"name": "hasEntry",
"args": ["title", "ViewTitle"]
})
await menuItem.click()
Und ähnlich View Matcher
const menuItem = await $({
"name": "hasEntry",
"args": ["title", "ViewTitle"],
"class": "androidx.test.espresso.matcher.ViewMatchers"
})
await menuItem.click()
Android View Tag (nur Espresso)
Die View-Tag-Strategie bietet eine bequeme Möglichkeit, Elemente anhand ihres Tags zu finden.
const elem = await $('-android viewtag:tag_identifier')
await elem.click()
iOS UIAutomation
Bei der Automatisierung einer iOS-Anwendung kann Apples UI Automation Framework verwendet werden, um Elemente zu finden.
Diese JavaScript API hat Methoden, um auf die Ansicht und alles darauf zuzugreifen.
const selector = 'UIATarget.localTarget().frontMostApp().mainWindow().buttons()[0]'
const button = await $(`ios=${selector}`)
await button.click()
Sie können auch die Prädikatsuche innerhalb der iOS UI Automation in Appium verwenden, um die Elementauswahl noch weiter zu verfeinern. Details finden Sie hier.
iOS XCUITest Prädikat-Strings und Klassenverkettungen
Mit iOS 10 und höher (bei Verwendung des XCUITest-Treibers) können Sie Prädikat-Strings verwenden:
const selector = `type == 'XCUIElementTypeSwitch' && name CONTAINS 'Allow'`
const switch = await $(`-ios predicate string:${selector}`)
await switch.click()
Und Klassenverkettungen:
const selector = '**/XCUIElementTypeCell[`name BEGINSWITH "D"`]/**/XCUIElementTypeButton'
const button = await $(`-ios class chain:${selector}`)
await button.click()
Accessibility ID
Die Locator-Strategie accessibility id ist so konzipiert, dass sie eine eindeutige Kennung für ein UI-Element liest. Dies hat den Vorteil, dass sie sich während der Lokalisierung oder eines anderen Prozesses, der Text ändern könnte, nicht ändert. Außerdem kann sie bei der Erstellung plattformübergreifender Tests helfen, wenn Elemente, die funktional gleich sind, dieselbe Accessibility ID haben.
- Für iOS ist dies der
accessibility identifier, der von Apple hier beschrieben wird. - Für Android wird die
accessibility idauf diecontent-descriptionfür das Element abgebildet, wie hier beschrieben.
Für beide Plattformen ist das Abrufen eines Elements (oder mehrerer Elemente) anhand ihrer accessibility id in der Regel die beste Methode. Es ist auch die bevorzugte Methode gegenüber der veralteten name-Strategie.
const elem = await $('~my_accessibility_identifier')
await elem.click()
Class Name
Die Strategie class name ist ein string, der ein UI-Element in der aktuellen Ansicht repräsentiert.
- Für iOS ist es der vollständige Name einer UIAutomation-Klasse und beginnt mit
UIA-, wie z.B.UIATextFieldfür ein Textfeld. Eine vollständige Referenz finden Sie hier. - Für Android ist es der vollständig qualifizierte Name einer UI Automator Klasse, wie z.B.
android.widget.EditTextfür ein Textfeld. Eine vollständige Referenz finden Sie hier. - Für Youi.tv ist es der vollständige Name einer Youi.tv-Klasse und beginnt mit
CYI-, wie z.B.CYIPushButtonViewfür ein Druckknopfelement. Eine vollständige Referenz finden Sie auf der GitHub-Seite des You.i Engine Drivers
// iOS Beispiel
await $('UIATextField').click()
// Android Beispiel
await $('android.widget.DatePicker').click()
// Youi.tv Beispiel
await $('CYIPushButtonView').click()
Chain Selectors
Wenn Sie bei Ihrer Abfrage spezifischer sein möchten, können Sie Selektoren verketten, bis Sie das richtige Element gefunden haben. Wenn Sie element vor Ihrem eigentlichen Befehl aufrufen, beginnt WebdriverIO die Abfrage von diesem Element aus.
Wenn Sie beispielsweise eine DOM-Struktur wie die folgende haben:
<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>
Und Sie möchten Produkt B in den Warenkorb legen, wäre es schwierig, dies nur mit dem CSS-Selektor zu tun.
Mit der Selektor-Verkettung ist es viel einfacher. Grenzen Sie das gewünschte Element einfach Schritt für Schritt ein:
await $('.row .entry:nth-child(2)').$('button*=Add').click()
Appium Image Selector
Mit der Locator-Strategie -image ist es möglich, an Appium eine Bilddatei zu senden, die ein Element darstellt, auf das Sie zugreifen möchten.
Unterstützte Dateiformate: jpg,png,gif,bmp,svg
Eine vollständige Referenz finden Sie hier
const elem = await $('./file/path/of/image/test.jpg')
await elem.click()
Hinweis: Die Art und Weise, wie Appium mit diesem Selektor arbeitet, ist, dass es intern einen (App-)Screenshot macht und den bereitgestellten Bildselektor verwendet, um zu überprüfen, ob das Element in diesem (App-)Screenshot gefunden werden kann.
Beachten Sie, dass Appium möglicherweise den aufgenommenen (App-)Screenshot verkleinert, um ihn an die CSS-Größe Ihres (App-)Bildschirms anzupassen (dies geschieht auf iPhones, aber auch auf Mac-Geräten mit einem Retina-Display, da die DPR größer als 1 ist). Dies führt dazu, dass keine Übereinstimmung gefunden wird, da der bereitgestellte Bildselektor möglicherweise aus dem ursprünglichen Screenshot stammt. Sie können dies beheben, indem Sie die Appium-Server-Einstellungen aktualisieren. Siehe die Appium-Dokumentation für die Einstellungen und diesen Kommentar für eine detaillierte Erklärung.
React Selectors
WebdriverIO bietet eine Möglichkeit, React-Komponenten basierend auf dem Komponentennamen auszuwählen. Dazu haben Sie die Wahl zwischen zwei Befehlen: react$ und react$$.
Diese Befehle ermöglichen es Ihnen, Komponenten aus dem React VirtualDOM auszuwählen und entweder ein einzelnes WebdriverIO-Element oder ein Array von Elementen zurückzugeben (abhängig davon, welche Funktion verwendet wird).
Hinweis: Die Befehle react$ und react$$ sind in ihrer Funktionalität ähnlich, mit dem Unterschied, dass react$$ alle passenden Instanzen als Array von WebdriverIO-Elementen zurückgibt, und react$ die erste gefundene Instanz zurückgibt.
Grundlegendes Beispiel
// 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'))
Mit dem Befehl browser.react$ können Sie eine Instanz von MyComponent auswählen:
const myCmp = await browser.react$('MyComponent')
Nachdem Sie das WebdriverIO-Element in der Variablen myCmp gespeichert haben, können Sie Element-Befehle darauf ausführen.
Filtern von Komponenten
Die Bibliothek, die WebdriverIO intern verwendet, ermöglicht es, Ihre Auswahl nach Props und/oder Zustand der Komponente zu filtern. Dazu müssen Sie ein zweites Argument für Props und/oder ein drittes Argument für den Zustand an den Browser-Befehl übergeben.
// 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'))
Wenn Sie die Instanz von MyComponent auswählen möchten, die ein Prop name mit dem Wert WebdriverIO hat, können Sie den Befehl wie folgt ausführen:
const myCmp = await browser.react$('MyComponent', {
props: { name: 'WebdriverIO' }
})
Wenn Sie Ihre Auswahl nach Zustand filtern möchten, würde der browser-Befehl etwa so aussehen:
const myCmp = await browser.react$('MyComponent', {
state: { myState: 'some value' }
})
Umgang mit React.Fragment
Wenn Sie den Befehl react$ verwenden, um React Fragments auszuwählen, gibt WebdriverIO das erste Kind dieser Komponente als Knoten der Komponente zurück. Wenn Sie react$$ verwenden, erhalten Sie ein Array, das alle HTML-Knoten innerhalb der Fragments enthält, die dem Selektor entsprechen.
// 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'))
Im obigen Beispiel würden die Befehle wie folgt funktionieren:
await browser.react$('MyComponent') // gibt das WebdriverIO-Element für das erste <div /> zurück
await browser.react$$('MyComponent') // gibt die WebdriverIO-Elemente für das Array [<div />, <div />] zurück
Hinweis: Wenn Sie mehrere Instanzen von MyComponent haben und react$$ verwenden, um diese Fragment-Komponenten auszuwählen, wird Ihnen ein eindimensionales Array aller Knoten zurückgegeben. Mit anderen Worten, wenn Sie 3 <MyComponent />-Instanzen haben, erhalten Sie ein Array mit sechs WebdriverIO-Elementen.
Custom Selector Strategies
Wenn Ihre App eine spezifische Methode zum Abrufen von Elementen erfordert, können Sie selbst eine benutzerdefinierte Selektorstrategie definieren, die Sie mit custom$ und custom$$ verwenden können. Registrieren Sie dazu Ihre Strategie einmal zu Beginn des Tests, z.B. in einem before-Hook:
loading...
Gegeben das folgende HTML-Snippet:
loading...
Dann verwenden Sie es, indem Sie aufrufen:
loading...
Hinweis: Dies funktioniert nur in einer Web-Umgebung, in der der Befehl execute ausgeführt werden kann.