自訂命令
如果您想用自己的一組命令擴充 browser
實例,可以使用瀏覽器方法 addCommand
。您可以像在規格中一樣以非同步方式編寫命令。
參數
命令名稱
一個定義命令的名稱,它將附加到瀏覽器或元素範圍。
類型:String
自訂函數
當呼叫命令時執行的函數。this
的範圍是 WebdriverIO.Browser
或 WebdriverIO.Element
,取決於命令是附加到瀏覽器還是元素範圍。
類型:Function
目標範圍
決定是否將命令附加到瀏覽器或元素範圍的旗標。如果設定為 true
,則該命令將是元素命令。
類型:Boolean
預設:false
範例
此範例示範如何新增一個新命令,該命令將目前 URL 和標題作為一個結果傳回。範圍 (this
) 是一個 WebdriverIO.Browser
物件。
browser.addCommand('getUrlAndTitle', async function (customVar) {
// `this` refers to the `browser` scope
return {
url: await this.getUrl(),
title: await this.getTitle(),
customVar: customVar
}
})
此外,您可以透過傳遞 true
作為最後一個引數,使用您自己的一組命令來擴充元素實例。在這種情況下,範圍 (this
) 是一個 WebdriverIO.Element
物件。
browser.addCommand("waitAndClick", async function () {
// `this` is return value of $(selector)
await this.waitForDisplayed()
await this.click()
}, true)
自訂命令讓您有機會將您經常使用的特定命令序列捆綁為單個呼叫。您可以在測試套件中的任何時間點定義自訂命令;只需確保在第一次使用之前定義該命令即可。(wdio.conf.js
中的 before
hook 是一個建立它們的好地方。)
定義後,您可以如下使用它們
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) // 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"
注意:如果您需要鏈接自訂命令,則該命令應以 $
結尾。
browser.addCommand("user$", (locator) => { return ele })
browser.addCommand("user$", (locator) => { return ele }, true)
await browser.user$('foo').user$('bar').click()
請小心不要使用太多自訂命令來超載 browser
範圍。
我們建議在頁面對象中定義自訂邏輯,以便將它們綁定到特定頁面。
擴充類型定義
使用 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 識別。
- 模組(使用 import/export)
- 環境類型定義(無 tsconfig include)
{
"compilerOptions": { ... },
"include": [
"./test/**/*.ts",
"./src/types/**/*.ts"
]
}
{
"compilerOptions": { ... }
}
- 根據您的執行模式為您的命令新增定義。
- 模組(使用 import/export)
- 環境類型定義
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) // returns response body
})
注意:您的自訂命令的結果是您傳回的 promise 的結果。
覆寫命令
您也可以使用 overwriteCommand
來覆寫原生命令。
不建議這樣做,因為它可能會導致框架的行為無法預測!
整體方法與 addCommand
類似,唯一的區別是命令函數中的第一個引數是您將要覆寫的原始函數。請參閱以下一些範例。
覆寫瀏覽器命令
/**
* 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)}`)
覆寫元素命令
在元素層級覆寫命令幾乎相同。只需傳遞 true
作為 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 })
新增更多 WebDriver 命令
如果您正在使用 WebDriver 協定並在支援 @wdio/protocols
中任何協定定義未定義的額外命令的平台上執行測試,您可以透過 addCommand
介面手動新增它們。webdriver
套件提供一個命令包裝函式,允許以與其他命令相同的方式註冊這些新端點,並提供相同的參數檢查和錯誤處理。若要註冊此新端點,請匯入命令包裝函式,並使用它註冊新命令,如下所示
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
}]
}))
使用無效參數呼叫此命令會產生與預先定義的協定命令相同的錯誤處理,例如
// 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')
,會正確向例如 https://127.0.0.1:4444/session/7bae3c4c55c3bf82f54894ddc83c5f31/foobar/foo
發出 WebDriver 請求,其酬載類似 { foo: 'bar' }
。
:sessionId
URL 參數將會自動替換為 WebDriver 會話的會話 ID。其他 URL 參數可以應用,但需要在 variables
內定義。
請參閱 @wdio/protocols
套件,了解如何定義協定命令的範例。