自定义命令
如果您想用自己的命令集扩展browser实例,可以使用浏览器的addCommand方法。您可以像在测试规范中一样,以异步方式编写命令。
参数
命令名称
定义命令并将其附加到浏览器或元素作用域的名称。
类型:String
自定义函数
命令调用时执行的函数。this作用域是WebdriverIO.Browser或WebdriverIO.Element,取决于命令是附加到浏览器还是元素作用域。
类型:Function
选项
修改自定义命令行为的配置选项对象
目标作用域
决定是否将命令附加到浏览器或元素作用域的标志。如果设置为true,则该命令将是元素命令。
选项名称:attachToElement
类型:Boolean
默认值:false
禁用隐式等待
决定是否在调用自定义命令前隐式等待元素存在的标志。
选项名称:disableElementImplicitWait
类型:Boolean
默认值:false
示例
此示例展示了如何添加一个新命令,该命令以一个结果返回当前URL和标题。作用域(this)是一个WebdriverIO.Browser对象。
browser.addCommand('getUrlAndTitle', async function (customVar) {
// `this` 指向 `browser` 作用域
return {
url: await this.getUrl(),
title: await this.getTitle(),
customVar: customVar
}
})
此外,您可以通过将true作为最后一个参数来扩展元素实例。在这种情况下,作用域(this)是一个WebdriverIO.Element对象。
browser.addCommand("waitAndClick", async function () {
// `this` 是 $(selector) 的返回值
await this.waitForDisplayed()
await this.click()
}, { attachToElement: true })
默认情况下,元素自定义命令会在调用自定义命令前等待元素存在。虽然大多数情况下这是需要的,但如果不需要,可以使用disableImplicitWait禁用:
browser.addCommand("waitAndClick", async function () {
// `this` 是 $(selector) 的返回值
await this.waitForExists()
await this.click()
}, { attachToElement: true, disableElementImplicitWait: true })
自定义命令使您可以将经常使用的特定命令序列捆绑为单个调用。您可以在测试套件中的任何点定义自定义命令;只需确保在首次使用命令之前定义它。(wdio.conf.js中的before钩子是创建它们的好地方。)
定义后,您可以按以下方式使用它们:
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')
})
注意: 如果您将自定义命令注册到browser作用域,则该命令对元素不可访问。同样,如果您将命令注册到元素作用域 ,它在browser作用域中也不可访问:
browser.addCommand("myCustomBrowserCommand", () => { return 1 })
const elem = await $('body')
console.log(typeof browser.myCustomBrowserCommand) // 输出 "function"
console.log(typeof elem.myCustomBrowserCommand()) // 输出 "undefined"
browser.addCommand("myCustomElementCommand", () => { return 1 }, { attachToElement: true })
const elem2 = await $('body')
console.log(typeof browser.myCustomElementCommand) // 输出 "undefined"
console.log(await elem2.myCustomElementCommand('foobar')) // 输出 "1"
const elem3 = await $('body')
elem3.addCommand("myCustomElementCommand2", () => { return 2 })
console.log(typeof browser.myCustomElementCommand2) // 输出 "undefined"
console.log(await elem3.myCustomElementCommand2('foobar')) // 输出 "2"
注意: 如果您需要链接自定义命令,该命令应以$结尾,
browser.addCommand("user$", (locator) => { return ele })
browser.addCommand("user$", (locator) => { return ele }, { attachToElement: true })
await browser.user$('foo').user$('bar').click()
注意不要用过多的自定义命令重载browser作用域。
我们建议在页面对象中定义自定义逻辑,这样它们就绑定到特定页面。
多远程
addCommand对多远程的工作方式类似,除了新命令将向下传播到子实例。使用this对象时要注意,因为多远程browser及其子实例有不同的this。
这个示例展示如何为多远程添加新命令。
import { multiRemoteBrowser } from '@wdio/globals'
multiRemoteBrowser.addCommand('getUrlAndTitle', async function (this: WebdriverIO.MultiRemoteBrowser, customVar: any) {
// `this` 指向:
// - 浏览器的MultiRemoteBrowser作用域
// - 实例的Browser作用域
return {
url: await this.getUrl(),
title: await this.getTitle(),
customVar: customVar
}
})
multiRemoteBrowser.getUrlAndTitle()
/*
{
url: [ 'https://webdriver.io/', 'https://webdriver.io/' ],
title: [
'WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO',
'WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO'
],
customVar: undefined
}
*/
multiRemoteBrowser.getInstance('browserA').getUrlAndTitle()
/*
{
url: 'https://webdriver.io/',
title: 'WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO',
customVar: undefined
}
*/
扩展类型定义
使用TypeScript,很容易扩展WebdriverIO接口。为自定义命令添加类型如下:
-
创建一个类型定义文件(例如,
./src/types/wdio.d.ts) -
a. 如果使用模块风格的类型定义文件(在类型定义文件中使用import/export 和
declare global WebdriverIO),确保将文件路径包含在tsconfig.json的include属性中。b. 如果使用环境风格的类型定义文件(类型定义文件中没有import/export,自定义命令使用
declare namespace WebdriverIO),确保tsconfig.json不包含任何include部分,因为这会导致所有未在include部分列出的类型定义文件不被TypeScript识别。
- Modules (using import/export)
- Ambient Type Definitions (no tsconfig include)
{
"compilerOptions": { ... },
"include": [
"./test/**/*.ts",
"./src/types/**/*.ts"
]
}
{
"compilerOptions": { ... }
}
- 根据您的执行模式为命令添加定义。
- 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>
}
}
集成第三方库
如果您使用支持promise的外部库(例如,进行数据库调用),一种很好的集成方法是用自定义命令包装某些API方法。
当返回promise时,WebdriverIO确保它不会继续执行下一个命令,直到promise解决。如果promise被拒绝,命令将抛出错误。
browser.addCommand('makeRequest', async (url) => {
const response = await fetch(url)
return await response.json()
})
然后,只需在WDIO测试规范中使用它:
it('execute external library in a sync way', async () => {
await browser.url('...')
const body = await browser.makeRequest('http://...')
console.log(body) // 返回响应体
})
注意: 自定义命令的结果是您返回的promise的结果。
重写命令
您也可以用overwriteCommand重写原生命令。
不建议这样做,因为它可能导致框架的不可预测行为!
整体方法与addCommand类似,唯一的区别是命令函数的第一个参数是您要重写的原始函数。请参见下面的一些示例。