Benutzerdefinierte Befehle
Wenn Sie die Instanz von browser
mit Ihrem eigenen Befehlssatz erweitern möchten, ist die Browsermethode addCommand
für Sie da. Sie können Ihren Befehl asynchron schreiben, genau wie in Ihrem Test.
Parameter
Befehlsname
Ein Name, der den Befehl definiert und an das Browser- oder Element Objekt angehängt wird.
Type: String
Benutzerdefinierte Funktion
Eine Funktion, die ausgeführt wird, wenn der Befehl aufgerufen wird. Die this
Variable ist entweder WebdriverIO.Browser
oder WebdriverIO.Element
, je nachdem, ob der Befehl vom Browser- oder Element-Objekt ausgeführt wurde.
Type: Function
Target Scope
Markierung, die festlegt, ob der Befehl an den Browser- oder Element-Objekt angehängt werden soll. Wenn auf true
gesetzt, ist der Befehl ein Elementbefehl.
Type: Boolean
Default: false
Beispiele
Dieses Beispiel zeigt, wie Sie einen neuen Befehl hinzufügen, der die aktuelle URL und den Titel als ein Ergebnis zurückgibt. Der Geltungsbereich (this
) ist ein WebdriverIO.Browser
Objekt.
browser.addCommand('getUrlAndTitle', async function (customVar) {
// `this` refers to the `browser` scope
return {
url: await this.getUrl(),
title: await this.getTitle(),
customVar: customVar
}
})
Darüber hinaus können Sie die Elementinstanz mit Ihrem eigenen Befehlssatz erweitern, indem Sie true
als letztes Argument übergeben. Der Geltungsbereich (this
) ist in diesem Fall ein WebdriverIO.Element
Objekt.
browser.addCommand("waitAndClick", async function () {
// `this` is return value of $(selector)
await this.waitForDisplayed()
await this.click()
}, true)
Benutzerdefinierte Befehle geben Ihnen die Möglichkeit, eine bestimmte Folge von häufig verwendeten Befehlen in einem einzigen Aufruf zu bündeln. Sie können an jedem Punkt Ihrer Testsuite benutzerdefinierte Befehle definieren; Stellen Sie nur sicher, dass der Befehl vor seiner Verwendung definiert ist. (Die before
Hook in Ihrer wdio.conf.js
ist ein guter Ort, um benuzterdefinierte Befehle zu erstellen.)
Einmal definiert, können Sie sie wie folgt verwenden:
it('should use my custom command', async () => {
await browser.url('http://www.github.com')
const result = await browser.getUrlAndTitle('foobar')
assert.strictEqual(result.url, 'https://github.com/')
assert.strictEqual(result.title, 'GitHub · Where software is built')
assert.strictEqual(result.customVar, 'foobar')
})
Hinweis: Wenn Sie einen benutzerdefinierten Befehl für die Browser Instanz
registrieren, ist der Befehl für Elemente nicht zugänglich. Wenn Sie einen Befehl im Elementbereich registrieren, ist er für die browser
Variable nicht zugänglich:
browser.addCommand("myCustomBrowserCommand", () => { return 1 })
const elem = await $('body')
console.log(typeof browser.myCustomBrowserCommand) // outputs "function"
console.log(typeof elem.myCustomBrowserCommand()) // outputs "undefined"
browser.addCommand("myCustomElementCommand", () => { return 1 }, true)
const elem2 = await $('body')
console.log(typeof browser.myCustomElementCommand) // outputs "undefined"
console.log(await elem2.myCustomElementCommand('foobar')) // outputs "1"
const elem3 = await $('body')
elem3.addCommand("myCustomElementCommand2", () => { return 2 })
console.log(typeof browser.myCustomElementCommand2) // outputs "undefined"
console.log(await elem3.myCustomElementCommand2('foobar')) // outputs "2"
Hinweis: Wenn Sie einen benutzerdefinierten Befehl verketten müssen, sollte der Befehl mit $
enden.
browser.addCommand("user$", (locator) => { return ele })
browser.addCommand("user$", (locator) => { return ele }, true)
await browser.user$('foo').user$('bar').click()
Achten Sie darauf, die browser
Variable nicht mit zu vielen benutzerdefinierten Befehlen zu überladen.
Wir empfehlen, benutzerdefinierte Logik in Seitenobjekten zu definieren, damit sie an eine bestimmte Seite gebunden sind.
Typdefinitionen Erweitern
Mit TypeScript ist es einfach, WebdriverIO-Schnittstellen zu erweitern. Fügen Sie Ihren benutzerdefinierten Befehlen Typen wie folgt hinzu:
-
Erstellen Sie eine Typdefinitionsdatei (z. B.
./src/types/wdio.d.ts
) -
a. Wenn Sie eine Typdefinitionsdatei im Modulstil verwenden (mit Import/Export und
deklarieren Sie im globalen WebdriverIO Namespace
in der Typdefinitionsdatei), und stellen Sie sicher, dass Sie den Dateipfad in die Eigenschafttsconfig.json
include
aufnehmen.b. Wenn Sie Typdefinitionsdateien im Ambient-Stil verwenden (kein Import/Export in Typdefinitionsdateien und
deklarieren Sie den Namensraum WebdriverIO
für benutzerdefinierte Befehle), Sie sicher, dass dietsconfig.json
nicht einen Abschnittinclude
enthält, da dies dazu führen, dass alle Typdefinitionsdateien, die nicht im Abschnittinclude
aufgeführt sind, von TypeScript nicht erkannt werden.
- Modules (using import/export)
- Ambient Type Definitions (no tsconfig include)
{
"compilerOptions": { ... },
"include": [
"./test/**/*.ts",
"./src/types/**/*.ts"
]
}
{
"compilerOptions": { ... }
}
- Fügen Sie Definitionen für Ihre Befehle entsprechend Ihrem Ausführungsmodus hinzu.
- Modules (using import/export)
- Ambient Type Definitions
declare global {
namespace WebdriverIO {
interface Browser {
browserCustomCommand: (arg: any) => Promise<void>
}
interface MultiRemoteBrowser {
browserCustomCommand: (arg: any) => Promise<void>
}
interface Element {
elementCustomCommand: (arg: any) => Promise<number>
}
}
}
declare namespace WebdriverIO {
interface Browser {
browserCustomCommand: (arg: any) => Promise<void>
}
interface MultiRemoteBrowser {
browserCustomCommand: (arg: any) => Promise<void>
}
interface Element {
elementCustomCommand: (arg: any) => Promise<number>
}
}
Integrieren Sie Bibliotheken von Drittanbietern
Wenn Sie externe Bibliotheken verwenden (z. B. um Datenbankaufrufe durchzuführen), die Promises unterstützen, ist es eine gute Idee, bestimmte API-Methoden mit einem benutzerdefinierten Befehl zu umhüllen.
Bei der Rückgabe des Promises stellt WebdriverIO sicher, dass es nicht mit dem nächsten Befehl fortfährt, bis das Promise aufgelöst ist. Wenn das Promise abgelehnt wird, gibt der Befehl einen Fehler aus.
import got from 'got'
browser.addCommand('makeRequest', async (url) => {
return got(url).json()
})
Dann verwenden Sie es einfach in Ihren WDIO-Testspezifikationen:
it('execute external library in a sync way', async () => {
await browser.url('...')
const body = await browser.makeRequest('http://...')
console.log(body) // returns response body
})
Hinweis: Das Ergebnis Ihres benutzerdefinierten Befehls ist das Ergebnis des von Ihnen zurückgegebenen Promises.
Befehle Überschreiben
Sie können native Befehle auch mit overwriteCommand
überschreiben.
Dies wird nicht empfohlen, da dies zu unvorhersehbarem Verhalten des Frameworks führen kann!
Der Gesamtansatz ähnelt addCommand
, der einzige Unterschied besteht darin, dass das erste Argument in der Befehlsfunktion die ursprüngliche Funktion ist, die Sie überschreiben werden. Sehen Sie sich unten einige Beispiele an.
Überschreiben von Browserbefehlen
/**
* print milliseconds before pause and return its value.
*/
// 'pause' - name of command to be overwritten
// origPauseFunction - original pause function
browser.overwriteCommand('pause', async (origPauseFunction, ms) => {
console.log(`sleeping for ${ms}`)
await origPauseFunction(ms)
return ms
})
// then use it as before
console.log(`was sleeping for ${await browser.pause(1000)}`)
Überschreiben von Elementbefehlen
Das Überschreiben von Befehlen auf Elementebene ist fast dasselbe. Übergeben Sie einfach true
als drittes Argument an overwriteCommand
:
/**
* Attempt to scroll to element if it is not clickable.
* Pass { force: true } to click with JS even if element is not visible or clickable.
*/
// 'click' - name of command to be overwritten
// origClickFunction - original click function
browser.overwriteCommand('click', async function (origClickFunction, { force = false } = {}) {
if (!force) {
try {
// attempt to click
await origClickFunction()
return null
} catch (err) {
if (err.message.includes('not clickable at point')) {
console.warn('WARN: Element', this.selector, 'is not clickable.',
'Scrolling to it before clicking again.')
// scroll to element and click again
await this.scrollIntoView()
return origClickFunction()
}
throw err
}
}
// clicking with js
console.warn('WARN: Using force click for', this.selector)
await browser.execute((el) => {
el.click()
}, this)
}, true) // don't forget to pass `true` as 3rd argument
// then use it as before
const elem = await $('body')
await elem.click()
// or pass params
await elem.click({ force: true })
Fügen Sie weitere WebDriver-Befehle hinzu
Wenn Sie das WebDriver-Protokoll verwenden und Tests auf einer Plattform ausführen, die zusätzliche Befehle unterstützt, die nicht durch eine der Protokolldefinitionen in @wdio/protocols
definiert sind, können Sie diese manuell über die Schnittstelle addCommand
hinzufügen. Das Paket webdriver
bietet einen Befehlswrapper, der es ermöglicht, diese neuen Endpunkte auf die gleiche Weise wie andere Befehle zu registrieren und dieselben Parameterprüfungen und Fehlerbehandlungen bereitzustellen. Um diesen neuen Endpunkt zu registrieren, importieren Sie den Befehlswrapper und registrieren Sie wie folgt einen neuen Befehl damit:
import { command } from 'webdriver'
browser.addCommand('myNewCommand', command('POST', '/session/:sessionId/foobar/:someId', {
command: 'myNewCommand',
description: 'a new WebDriver command',
ref: 'https://vendor.com/commands/#myNewCommand',
variables: [{
name: 'someId',
description: 'some id to something'
}],
parameters: [{
name: 'foo',
type: 'string',
description: 'a valid parameter',
required: true
}]
}))
Der Aufruf dieses Befehls mit ungültigen Parametern führt zur gleichen Fehlerbehandlung wie vordefinierte Protokollbefehle, z.B.:
// call command without required url parameter and payload
await browser.myNewCommand()
/**
* results in the following error:
* Error: Wrong parameters applied for myNewCommand
* Usage: myNewCommand(someId, foo)
*
* Property Description:
* "someId" (string): some id to something
* "foo" (string): a valid parameter
*
* For more info see https://my-api.com
* at Browser.protocolCommand (...)
* ...
*/
browser.myNewCommand('foo', 'bar')
, stellt eine korrekte WebDriver-Anfrage an z. B. http://localhost:4444/session/7bae3c4c55c3bf82f54894ddc83c5f31/foobar/foo
mit dem Argument { foo: 'bar' }
.
Der URL-Parameter :sessionId
wird automatisch durch die Sitzungs-ID der WebDriver-Sitzung ersetzt. Andere URL-Parameter können angewendet werden, müssen aber innerhalb von variables
definiert werden.
Sehen Sie sich Beispiele an, wie Protokollbefehle im @wdio/protocols
definiert werden können.