跳至主要內容

WebdriverIO v9 發佈

·閱讀時間 15 分鐘

整個 Webdriverio 開發團隊很高興且自豪地發布今天的 WebdriverIO v9!

這標誌著所有使用 WebdriverIO 作為測試自動化工具的專案,開始進入一個令人興奮的新時代。隨著瀏覽器團隊在例如 Chrome 和 Firefox 上所做的偉大工作,我們現在進入一個新的時代,由於新的 WebDriver Bidi 協議,它提供了比以往更強大的自動化功能。透過 WebdriverIO v9,我們一直努力站在這個新時代採用者的最前沿,並讓您可以首先利用該協議的強大功能。

讓我們探索此版本中的新功能、更新和最佳化。

新功能

v9 中的大多數新功能都是由瀏覽器中現在可用的 WebDriver Bidi 功能所啟用。升級到 v9 後,除非您透過新的 wdio:enforceWebDriverClassic 功能明確停用它,否則所有工作階段都會自動使用 Bidi。

注意

如果您的遠端環境不支援 WebDriver Bidi,您將無法使用這些功能。您可以透過查看 browser.isBidi 屬性來檢查您的工作階段是否支援 Bidi。

新的 url 命令參數

url 命令已從簡單的導航工具演變為功能強大的命令。

傳入自訂標頭

您現在可以傳入自訂標頭,這些標頭將在瀏覽器發出請求時應用。這對於設定工作階段 cookie 以自動登入非常有用。

await browser.url('https://webdriverio.dev.org.tw', {
headers: {
Authorization: 'Bearer XXXXX'
}
});

請注意,雖然您可以透過 mock 命令修改瀏覽器處理所有請求的方式,但對 url 命令所做的變更僅持續到該特定頁面載入,並在導航完成後重設。

克服基本驗證

透過基本身份驗證自動執行使用者身份驗證從未如此簡單

await browser.url('https://the-internet.herokuapp.com/basic_auth', {
auth: {
user: 'admin',
pass: 'admin'
}
});
await expect($('p=Congratulations! You must have the proper credentials.').toBeDisplayed();

執行初始化腳本

在網站上的任何內容載入之前,使用 beforeLoad 參數注入 JavaScript。這對於操作 Web API 非常有用,例如

// navigate to a URL and mock the battery API
await browser.url('https://pazguille.github.io/demo-battery-api/', {
onBeforeLoad (win) {
// mock "navigator.battery" property
// returning mock charge object
win.navigator.getBattery = () => Promise.resolve({
level: 0.5,
charging: false,
chargingTime: Infinity,
dischargingTime: 3600, // seconds
})
}
})
// now we can assert actual text - we are charged at 50%
await expect($('.battery-percentage')).toHaveText('50%')
// and has enough juice for 1 hour
await expect($('.battery-remaining')).toHaveText('01:00)

在此範例中,我們覆寫了 Navigator 介面的 getBattery 方法。

新的 addInitScript 命令

addInitScript 命令使您能夠將腳本注入到瀏覽器中,該腳本在每次開啟新的瀏覽環境時都會觸發。這包括導航到 URL 或在應用程式中載入 iframe 等動作。您傳遞給此命令的腳本會收到一個回呼作為其最後一個參數,允許您將值從瀏覽器傳回您的 Node.js 環境。

例如,若要收到應用程式中節點新增或移除元素的通知,您可以使用以下範例

const script = await browser.addInitScript((myParam, emit) => {
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
emit(mutation.target.nodeName)
}
})
observer.observe(document, { childList: true, subtree: true })
})

script.on('data', (data) => {
console.log(data) // prints: BODY, DIV, P, ...
})

此初始化腳本可以修改全域變數並覆寫內建的 Web API 原語,讓您可以設定測試環境以符合您的特定需求。

跨瀏覽器請求模擬

WebdriverIO 在 v6.3.0 中引入了請求模擬,但僅限於 Chromium 瀏覽器。使用 v9,我們現在使用 WebDriver Bidi,將支援擴展到所有瀏覽器。此增強功能允許在請求發送到網路之前修改它們

// mock all API requests
const mock = await browser.mock('**/api/**')
// apply auth token to each request
mock.request({
headers: { 'Authorization': 'Bearer XXXXXX' }
})

自動穿透陰影根節點

透過自動穿透陰影根節點,使用 Web 元件測試應用程式現在變得無縫。WebdriverIO 現在會追蹤所有 ShadowRoot 節點並在其中搜尋。

例如,假設我們想要自動化以下包含多個巢狀陰影根節點的日期選擇器元件,請在應用程式中的任何元素上使用 滑鼠右鍵 > 檢查 來查看,並觀察它們在不同陰影根節點中的巢狀深度

為了變更日期,您現在只需呼叫

await browser.url('https://ionic.dev.org.tw/docs/usage/v8/datetime/basic/demo.html?ionic:mode=md')
await browser.$('aria/Sunday, August 4]').click()
await browser.$('.aux-input').getValue() // outputs "2024-08-04T09:00:00"

雖然 WebdriverIO 之前支援「深度選擇器」,但此功能僅限於 CSS 選擇器。新的選擇器引擎現在支援所有選擇器類型,包括協助工具標籤。

透過增強的定位器引擎,可以輕鬆找到多個巢狀陰影根節點中的元素。WebdriverIO 是第一個支援在 openclosed 模式下對陰影根節點提供此功能的框架。

改進的引數序列化

透過 WebDriver Classic,將資料物件從測試環境移動到瀏覽器環境的能力相當有限,僅限於 DOM 元素和可序列化的物件和類型。透過 WebDriver Bidi,我們現在能夠更好地轉換不可序列化的資料物件,使其在瀏覽器中用作原始物件。除了已知的 JavaScript 原語(例如 MapSet)之外,該協議還允許序列化 InfinitynullundefinedBigInt 等值。

以下範例說明如何在 Node.js 中組成一個 JavaScript Map 物件,並將其傳遞到瀏覽器,瀏覽器會將其自動反序列化回 Map,反之亦然

const data = new Map([
['username', 'Tony'],
['password', 'secret']
])
const output = await browser.execute(
(data) => `${data.size} entrie(s), username: ${data.get('username')}, password: ${data.get('secret')}`,
data
)

console.log(output)
// outputs: "1 entrie(s), username: Tony, password: secret"

這將使傳遞資料和使用自訂腳本更加容易,這些腳本現在可以傳回豐富的資料物件,這將有助於更好地觀察應用程式的狀態。它將使 WebdriverIO 等框架能夠更深入地與瀏覽器環境整合,並在未來建立更有用的功能。

設定視窗

雖然 WebdriverIO 允許您在桌面和行動瀏覽器中測試您的應用程式,但通常更簡單的方法是調整瀏覽器的視窗大小,以模擬行動使用者,檢查應用程式是否在響應式模式下正確呈現。使用傳統的 WebDriver,這可能會很有挑戰性,因為瀏覽器視窗無法縮小到非常小的尺寸,並且通常會維持最小寬度 500px。例如

await browser.url('https://webdriverio.dev.org.tw')
await browser.setWindowSize(393, 659)
console.log(await browser.execute(() => window.innerWidth)) // returns `500`

WebdriverIO setWindowSize Result

WebdriverIO v9 引入了 setViewport 命令,使您能夠將應用程式的視窗調整為任何大小,包括修改 devicePixelRatio。與會更改整個瀏覽器視窗大小的 setWindowSize 不同,setViewport 專門調整應用程式呈現的畫布大小,而瀏覽器視窗的尺寸保持不變。

為了簡化行動裝置模擬,我們增強了 emulate 命令。這項新功能允許您透過簡單地指定行動裝置的名稱,同時調整特定行動裝置的視窗大小、裝置像素比和使用者代理。例如

await browser.url('https://webdriverio.dev.org.tw')
await browser.emulate('device', 'iPhone 15')
console.log(await browser.execute(() => window.innerWidth)) // returns `393`
console.log(await browser.execute(() => navigator.userAgent)) // returns `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36`

WebdriverIO setViewportSize Result

雖然我們建議在實際行動裝置上執行行動測試,因為行動瀏覽器引擎與桌面瀏覽器使用的引擎不同,但如果我們只是想快速驗證應用程式在行動視窗中的呈現方式,這會是一個方便的解決方案。

假時間支援

想要更改瀏覽器中的時間嗎?透過 WebdriverIO v9,現在可以在瀏覽器中為您的測試偽造時間。我們使用一個新屬性 clock 增強了 emulate 命令。這讓您可以將日期和時間設定為您需要的任何時間,並控制時間何時應該前進。以下是其運作方式

const clock = await browser.emulate('clock', { now: new Date(2021, 3, 14) })
console.log(await browser.execute(() => new Date().getTime())) // returns 1618383600000

await clock.tick(1000)
console.log(await browser.execute(() => new Date().getTime())) // returns 1618383601000

新的 clock 模擬會傳回一個 Clock 物件,其中包含 ticksetSystemTimerestore 等方法,可讓您精確控制測試中的時間。

自動對話方塊處理

如果您的應用程式使用原生瀏覽器對話方塊,例如 alertconfirm,當這些提示意外出現時,有時可能會很棘手。在先前的版本中,如果您沒有正確處理它們,所有命令都會失敗。透過 WebdriverIO v9,除非您明確註冊監聽器,否則我們將開始自動抑制對話方塊,例如

await browser.url('https://webdriverio.dev.org.tw')
browser.on('dialog', async (dialog) => {
console.log(dialog.message()) // outputs: "Hello Dialog"
await dialog.dismiss()
})

await browser.execute(() => alert('Hello Dialog'))

新的 dialog 事件會傳入一個 dialog 物件,您可以使用該物件呼叫 acceptdismiss,取得對話方塊的類型或訊息,以及其預設值。我們希望這能在未來減少因瀏覽器意外警示而導致的測試失敗。

自動等待元素變成可互動的

當元素未顯示、無法滾動到視窗中或已停用時,該元素會被認為是不可互動的,而以前 WebdriverIO 會在發生這種情況時拋出錯誤。在 v8 中,我們已經改進了顯示元素的 html,但在 v9 中,我們現在會在對元素執行直接動作(例如點擊或使用 setValue!)時,自動等待元素變成可互動的!

這表示您不再需要編寫像這樣的程式碼

const submitButton = await $('button[type="submit"]');
await submitButton.waitForEnabled(); // validation of the form takes time, during which the button is disabled
await submitButton.click();

現在您可以將其簡化為

const submitButton = await $('button[type="submit"]');
await submitButton.click(); // automatically waits for the button to become enabled

Web 元件的快照測試

如果您的應用程式使用了許多 Web 元件,由於 WebdriverIO 無法查看 Shadow Root 內部,因此快照功能變得無法使用。透過 v9,WebdriverIO 現在可以完全查看所有元素,並允許透過將其轉換為 宣告式 Shadow DOM 來快照開啟和關閉的 Web 元件,例如

await browser.url('https://ionic.dev.org.tw/docs/usage/v8/button/basic/demo.html?ionic:mode=md')

// get snapshot of web component without its styles
const snapshot = await $('ion-button').getHTML({ excludeElements: ['style'] })

// assert snapshot
await expect(snapshot).toMatchInlineSnapshot(`
<ion-button class="md button button-solid ion-activatable ion-focusable hydrated">Default
<template shadowrootmode="open">
<button type="button" class="button-native" part="native">
<span class="button-inner">
<slot name="icon-only"></slot>
<slot name="start"></slot>
<slot></slot>
<slot name="end"></slot>
</span>
<ion-ripple-effect role="presentation" class="md hydrated">
<template shadowrootmode="open"></template>
</ion-ripple-effect>
</button>
</template>
</ion-button>
`)

為了啟用此功能,我們增強了 getHTML 命令,並將其先前的布林參數替換為一個物件,該物件可以更好地控制命令行為。在 getHTML 命令文件中閱讀更多關於新的 GetHTMLOptions 選項的資訊。

值得注意的重大變更

我們致力於盡量減少重大變更,以避免在升級到最新版本的 WebdriverIO 時需要花費大量時間。但是,主要版本提供了移除我們不再建議使用的已棄用介面的機會。

存取元素屬性

如您所知,WebdriverIO 允許您從瀏覽器中擷取元素,以便在您的測試中與它們互動。當您與元素互動時,WebdriverIO 會保留元素的參考和額外中繼資料,以便將命令正確地導向到正確的元素,並且在元素過時時重新擷取元素。您可能存取過其中一些元素屬性,例如透過 $('elem').selector 評估選取器,或透過 $('elem').elementId 取得其參考,以及其他

這些屬性不再以這種方式存取。相反地,您需要呼叫 getElement 來存取 WebdriverIO.Element 的屬性,並呼叫 getElements 來存取 WebdriverIO.ElementArray 的屬性。以下是一個範例

// WebdriverIO v8 and older
const elem = await $('elem')
console.log(elem.selector) // ❌ returns a `Promise<string>` now

// WebdriverIO v9
const elem = await $('elem').getElement()
console.log(elem.selector) // ✅ returns "elem"

此變更修正了 IDE 中的一個 非常惱人的錯誤,該錯誤會誤導它們相信 $('elem') 會傳回 promise,導致它們自動將鏈式元素呼叫包裝在多個 await 陳述式中,例如 await (await $('elem')).getText()。此改進將使編寫測試變得更加容易。

移除 XXXContaining 比對器

expect-webdriverio 中,我們維護專為 WebdriverIO 元素和端對端測試案例量身打造的自訂比對器。先前,我們為每個比對器提供了 XXXContaining 版本,以允許部分值比對。但是,底層的 expect 套件已經透過 非對稱比對器 支援此功能。為了減少我們維護的比對器數量,我們決定移除我們的自訂 XXXContaining 比對器,而改用這些非對稱比對器。

遷移到非對稱比對器很簡單。以下是一個範例

- await expect($('elem')).toHaveTextContaining('Hello')
+ await expect($('elem')).toHaveText(expect.stringContaining('Hello'))

移除 JSON Wire Protocol 命令

JSON Wire Protocol 是 Selenium 開發的第一個自動化協定,可使用任何支援 HTTP 的語言進行遠端瀏覽器自動化。在 2012 年,Selenium 的建立者開始正式標準化該協定,該協定現在已在所有瀏覽器中支援。這項努力最終使 WebDriver 協定成為 W3C 建議,導致瀏覽器驅動程式和雲端供應商遷移到此官方標準。

我們認為現在是時候告別舊的 JSON Wire Protocol,擁抱 WebDriver 的未來了。如果您仍在較舊的瀏覽器上進行測試,並且需要這些協定命令來執行您的自動化指令碼,則可以安裝 @wdio/jsonwp-service 以繼續使用必要的命令。

移除 devtools@wdio/devtools-service 套件

透過 WebdriverIO v8.15,我們引入了瀏覽器驅動程式的自動設定作為核心功能。此變更使 devtools 套件的原始用途過時。最初,devtools 套件旨在透過 Puppeteer 實作 WebDriver 規範,該規範會為您處理瀏覽器設定。這允許使用 WebdriverIO 而無需下載任何瀏覽器驅動程式。由於不再需要此功能,我們已決定移除 devtools 套件以及 automationProtocol 選項。

此外,隨著 WebDriver Bidi 的採用,我們現在為大多數自動化需求提供了一個標準化的協定。儘管如此,在 WebdriverIO 中使用 Chrome Devtools 進行更複雜的瀏覽器內省仍然是一項受歡迎的功能。先前,我們建議將 @wdio/devtools-service 套件用於這些使用案例。但是,我們發現大多數使用者更喜歡直接使用 Puppeteer,而不是我們自訂的 DevTools 介面。WebdriverIO 使用者可以同時使用 WebdriverIO 原語和 Puppeteer 自動化瀏覽器。透過呼叫 getPuppeteer 命令,您可以取得正在自動化的瀏覽器的 Puppeteer 實例,讓您可以透過更強大且維護完善的介面存取所有 Devtools 功能。因此,使用者主要將 @wdio/devtools-service 套件用於測試效能和其它 Google Lighthouse 功能。接下來,這些功能將在名為 `@wdio/lighthouse-service 的新服務套件下提供。

請放心,我們已經找到了這些套件的良好使用案例,並且不會放棄它們。

專案更新

在我們努力新增新功能,使 WebdriverIO 在測試 Web 和行動應用程式方面更有效率的同時,我們也一直專注於多項內部計畫,以確保專案擁有光明的前景。

pnpm 遷移

隨著我們的核心套件隨著時間推移而增長,我們在 NPM 方面面臨越來越多的挑戰,尤其是在我們的單一儲存庫中解決相依性方面。此外,自訂連結使我們的測試基礎架構比必要的更複雜。

考量到 JavaScript 工具生態系統的發展,我們評估並採用了新技術,以改善專案內的開發人員體驗。透過遷移至 pnpm,我們消除了許多自訂腳本,簡化了整體專案設置,現在透過 Dependabot 的定期更新,能夠享受更快、更可靠的依賴項解析,並減少合併衝突。

Esbuild 遷移

自從在 v7 中引入 TypeScript 以來,我們一直使用 TypeScript 編譯器將檔案轉換為 JavaScript,以便發佈到 NPM。然而,隨著我們的程式碼庫不斷增長,我們在這個設置中遇到了挑戰。例如,webdriverio@wdio/browser-runner 套件中的某些檔案僅在瀏覽器環境中使用,但它們卻被編譯為像 Node.js 一樣,導致了相容性問題。

在 WebdriverIO v9 中,所有專案檔案都使用 Esbuild 編譯。這個功能多樣且超快的打包工具讓我們能夠針對預期的環境編譯程式碼,確保應用正確的 polyfill。此外,現在每個套件都被打包成一個單一的 JavaScript 檔案,略微提升了效能,特別是對於元件測試而言。

ts-node 遷移到 tsx

WebdriverIO 已從 ts-node 轉換為 tsx,以便在執行時將 TypeScript 測試編譯為 JavaScript。此變更提供了更好的 ESM 支援、更高的效能,以及更準確的 TypeScript 測試錯誤堆疊追蹤。

停止支援 Node.js v16

如同每個版本一樣,我們正在停止支援較舊、未維護的 Node.js 版本。WebdriverIO v9 將不再支援 Node.js v16 及更舊的版本。我們建議使用者升級到 Node.js v20。

下一步是什麼?

隨著 WebdriverIO 慶祝其開發的第 13 年,對新測試功能的需求隨著不斷發展的測試行業和 Web 標準而持續增長。隨著現代瀏覽器中 WebDriver Bidi 協議的採用,像 Selenium、Nightwatch 和 WebdriverIO 這樣的工具可以匹配 Playwright 和 Cypress 的功能,而無需依賴專有的瀏覽器修補程式或特定的執行環境。

WebdriverIO 將繼續整合更多的 WebDriver Bidi 功能,以構建強大的新功能。我們對未來的發展感到興奮,包括一個 WebdriverIO 開發人員工具應用程式,它將透過提供一個強大的使用者介面來檢查和偵錯受測應用程式,從而顯著增強測試體驗。有關此計畫的更多詳細資訊將很快分享!

感謝

我們衷心感謝我們的優質贊助商 BrowserStackSauce Labs,以及所有為專案做出貢獻的人。WebdriverIO 仍然是一個社群驅動且獨立的專案,依賴於像您這樣的人的貢獻。加入我們的 開放辦公時間,並考慮回饋專案。

為了未來更多的創新與合作!🚀

感謝您的閱讀!

歡迎!我能幫您什麼忙?

WebdriverIO AI Copilot