Devtools 服務
一個 WebdriverIO 服務,可讓您在測試中執行 Chrome DevTools 命令
在 Chrome v63 及更高版本中,瀏覽器開始支援多個客戶端,允許任意客戶端存取 Chrome DevTools 協定。 這提供了有趣的機會來自動化 Chrome,使其超出WebDriver 協定。透過此服務,您可以增強 wdio 瀏覽器物件,以利用該存取權,並在測試中呼叫 Chrome DevTools 命令,例如截取請求、限制網路功能或取得 CSS/JS 涵蓋範圍。
自 Firefox 86 起,已透過傳遞功能 "moz:debuggerAddress": true
實作Chrome DevTools 協定的子集。
注意:此服務目前僅支援 Chrome v63 及更高版本、Chromium 和 Firefox 86 及更高版本! 由於雲端供應商不公開存取 Chrome DevTools 協定,因此此服務通常僅在本地執行測試或透過Selenium Grid v4 或更高版本執行時才有效。
安裝
最簡單的方法是透過在您的 package.json
中將 @wdio/devtools-service
保留為開發相依性,方法是
npm install @wdio/devtools-service --save-dev
有關如何安裝 WebdriverIO
的說明,請參閱此處。
設定
為了使用此服務,您只需要將此服務新增至您的 wdio.conf.js
中的服務清單,如下所示
// wdio.conf.js
export const config = {
// ...
services: ['devtools'],
// ...
};
使用方式
@wdio/devtools-service
為您提供了多種功能,可協助您自動化 Chrome,使其超出 WebDriver 協定。它讓您可以存取 Chrome DevTools 協定,以及您可以用來透過 Puppeteer 自動化介面自動化 Chrome 的 Puppeteer 執行個體。
效能測試
DevTools 服務可讓您從每次頁面載入或點擊引起的頁面轉換中擷取效能資料。若要啟用它,請呼叫 browser.enablePerformanceAudits(<options>)
。在擷取所有必要的效能資料後,停用它以還原限制設定,例如
import assert from 'node:assert'
describe('JSON.org page', () => {
before(async () => {
await browser.enablePerformanceAudits()
})
it('should load within performance budget', async () => {
/**
* this page load will take a bit longer as the DevTools service will
* capture all metrics in the background
*/
await browser.url('http://json.org')
let metrics = await browser.getMetrics()
assert.ok(metrics.speedIndex < 1500) // check that speedIndex is below 1.5ms
let score = await browser.getPerformanceScore() // get Lighthouse Performance score
assert.ok(score >= .99) // Lighthouse Performance score is at 99% or higher
$('=Esperanto').click()
metrics = await browser.getMetrics()
assert.ok(metrics.speedIndex < 1500)
score = await browser.getPerformanceScore()
assert.ok(score >= .99)
})
after(async () => {
await browser.disablePerformanceAudits()
})
})
您可以使用 emulateDevice
命令來模擬行動裝置,限制 CPU 和網路,以及將 mobile
設定為外型規格
await browser.emulateDevice('iPhone X')
await browser.enablePerformanceAudits({
networkThrottling: 'Good 3G',
cpuThrottling: 4,
formFactor: 'mobile'
})
以下命令及其結果可用
getMetrics
取得最常用的效能指標。
console.log(await browser.getMetrics())
/**
* { timeToFirstByte: 566,
* serverResponseTime: 566,
* domContentLoaded: 3397,
* firstVisualChange: 2610,
* firstPaint: 2822,
* firstContentfulPaint: 2822,
* firstMeaningfulPaint: 2822,
* largestContentfulPaint: 2822,
* lastVisualChange: 15572,
* interactive: 6135,
* load: 8429,
* speedIndex: 3259,
* totalBlockingTime: 31,
* maxPotentialFID: 161,
* cumulativeLayoutShift: 2822 }
*/
getDiagnostics
取得一些有關頁面載入的實用診斷資訊。
console.log(await browser.getDiagnostics())
/**
* { numRequests: 8,
* numScripts: 0,
* numStylesheets: 0,
* numFonts: 0,
* numTasks: 237,
* numTasksOver10ms: 5,
* numTasksOver25ms: 2,
* numTasksOver50ms: 2,
* numTasksOver100ms: 0,
* numTasksOver500ms: 0,
* rtt: 147.20600000000002,
* throughput: 47729.68474448835,
* maxRtt: 176.085,
* maxServerLatency: 1016.813,
* totalByteWeight: 62929,
* totalTaskTime: 254.07899999999978,
* mainDocumentTransferSize: 8023 }
*/
getMainThreadWorkBreakdown
傳回所有主執行緒工作及其總持續時間的明細清單。
console.log(await browser.getMainThreadWorkBreakdown())
/**
* [ { group: 'styleLayout', duration: 130.59099999999998 },
* { group: 'other', duration: 44.819 },
* { group: 'paintCompositeRender', duration: 13.732000000000005 },
* { group: 'parseHTML', duration: 3.9080000000000004 },
* { group: 'scriptEvaluation', duration: 2.437999999999999 },
* { group: 'scriptParseCompile', duration: 0.20800000000000002 } ]
*/
getPerformanceScore
傳回 Lighthouse 效能分數,它是下列指標的加權平均值:firstContentfulPaint
、speedIndex
、largestContentfulPaint
、cumulativeLayoutShift
、totalBlockingTime
、interactive
、maxPotentialFID
或 cumulativeLayoutShift
。
console.log(await browser.getPerformanceScore())
/**
* 0.897826278457836
*/
enablePerformanceAudits
針對呼叫 url
命令或按一下連結或任何導致頁面載入的動作所導致的所有頁面載入啟用自動效能稽核。您可以傳遞設定物件來決定一些限制選項。預設的限制設定檔是 Good 3G
網路,CPU 限制為 4 倍。
await browser.enablePerformanceAudits({
networkThrottling: 'Good 3G',
cpuThrottling: 4,
cacheEnabled: true,
formFactor: 'mobile'
})
以下網路限制設定檔可用:offline
、GPRS
、Regular 2G
、Good 2G
、Regular 3G
、Good 3G
、Regular 4G
、DSL
、Wifi
和 online
(無限制)。
裝置模擬
此服務可讓您模擬特定的裝置類型。如果設定,則會修改瀏覽器視窗以符合裝置功能,並且使用者代理程式也會根據裝置使用者代理程式進行設定。若要設定預先定義的裝置設定檔,您可以執行
await browser.emulateDevice('iPhone X')
// or `browser.emulateDevice('iPhone X', { inLandscape: true })` if you want to be in landscape mode
// or `browser.emulateDevice('iPhone X', { osVersion: "15.0" })` if you want to use emulated device with custom OS version
可用的預先定義裝置設定檔有:Blackberry PlayBook
、BlackBerry Z30
、Galaxy Note 3
、Galaxy Note II
、Galaxy S III
、Galaxy S5
、iPad
、iPad Mini
、iPad Pro
、iPhone 4
、iPhone 5
、iPhone 6
、iPhone 6 Plus
、iPhone 7
、iPhone 7 Plus
、iPhone 8
、iPhone 8 Plus
、iPhone SE
、iPhone X
、JioPhone 2
、Kindle Fire HDX
、LG Optimus L70
、Microsoft Lumia 550
、Microsoft Lumia 950
、Nexus 10
、Nexus 4
、Nexus 5
、Nexus 5X
、Nexus 6
、Nexus 6P
、Nexus 7
、Nokia Lumia 520
、Nokia N9
、Pixel 2
、Pixel 2 XL
您也可以透過提供物件作為參數來定義自己的裝置設定檔,如以下範例所示
await browser.emulateDevice({
viewport: {
width: 550, // <number> page width in pixels.
height: 300, // <number> page height in pixels.
deviceScaleFactor: 1, // <number> Specify device scale factor (can be thought of as dpr). Defaults to 1
isMobile: true, // <boolean> Whether the meta viewport tag is taken into account. Defaults to false
hasTouch: true, // <boolean> Specifies if viewport supports touch events. Defaults to false
isLandscape: true // <boolean> Specifies if viewport is in landscape mode. Defaults to false
},
userAgent: 'my custom user agent'
})
注意
這僅在您不使用 capabilities['goog:chromeOptions']
內的 mobileEmulation
時才有效。如果存在 mobileEmulation
,則呼叫 browser.emulateDevice()
不會執行任何動作。
PWA 測試
透過 checkPWA
命令,您可以驗證您的 Web 應用程式是否符合最新關於漸進式 Web 應用程式的 Web 標準。它會檢查
- 您的應用程式是否可安裝
- 提供服務工作程式
- 是否有啟動畫面
- 提供 Apple Touch 和遮罩圖示
- 是否可在行動裝置上提供
如果您對其中一個檢查不感興趣,您可以傳入您想要執行的檢查清單。如果所有檢查都通過,passed
屬性將會傳回 true
。如果它們失敗,您可以使用 details
屬性來使用失敗詳細資料豐富您的失敗訊息。
// open page first
await browser.url('https://webdriverio.dev.org.tw')
// validate PWA
const result = await browser.checkPWA()
expect(result.passed).toBe(true)
擷取程式碼涵蓋範圍
此服務可讓您擷取受測試應用程式的程式碼涵蓋範圍。為此,您需要在服務設定中啟用此功能
// wdio.conf.js
services: [
['devtools', {
coverageReporter: {
enable: true,
type: 'html', // lcov, json, text
logDir: __dirname + '/coverage',
exclude: [/resources/]
}
}]
]
然後,您就可以存取一個命令,該命令會計算涵蓋的程式碼行和分支的比率,以便在您的測試中判斷
const coverage = await browser.getCoverageReport()
expect(coverage.lines.total).toBeAbove(0.9)
expect(coverage.statements.total).toBeAbove(0.9)
expect(coverage.functions.total).toBeAbove(0.9)
expect(coverage.branches.total).toBeAbove(0.9)
Chrome DevTools 存取
目前,此服務允許兩種不同的方式來存取 Chrome DevTools 協定
cdp
命令
cdp
命令是新增至瀏覽器範圍的自訂命令,可讓您直接呼叫協定的命令。
browser.cdp(<domain>, <command>, <arguments>)
例如,如果您想要取得頁面的 JavaScript 涵蓋範圍,您可以執行下列操作
it('should take JS coverage', async () => {
/**
* enable necessary domains
*/
await browser.cdp('Profiler', 'enable')
await browser.cdp('Debugger', 'enable')
/**
* start test coverage profiler
*/
await browser.cdp('Profiler', 'startPreciseCoverage', {
callCount: true,
detailed: true
})
await browser.url('http://google.com')
/**
* capture test coverage
*/
const { result } = await browser.cdp('Profiler', 'takePreciseCoverage')
const coverage = result.filter((res) => res.url !== '')
console.log(coverage)
})
getNodeId(selector)
和 getNodeIds(selector)
命令
輔助方法,用於取得頁面中元素的 nodeId。NodeId 類似於 WebDriver 的節點 ID,是節點的識別符。它可以作為其他 Chrome DevTools 方法的參數,例如 DOM.focus
。
const nodeId = await browser.getNodeId('body')
console.log(nodeId) // outputs: 4
const nodeId = await browser.getNodeIds('img')
console.log(nodeId) // outputs: [ 40, 41, 42, 43, 44, 45 ]
startTracing(categories, samplingFrequency)
命令
開始追蹤瀏覽器。您可以選擇性地傳入自訂追蹤類別(預設為此清單)和取樣頻率(預設為 10000
)。
await browser.startTracing()
endTracing
命令
停止追蹤瀏覽器。
await browser.endTracing()
getTraceLogs
命令
傳回在追蹤期間擷取的追蹤日誌。您可以使用此命令將追蹤日誌儲存在檔案系統上,以透過 Chrome DevTools 介面分析追蹤。
import fs from 'node:fs/promises'
await browser.startTracing()
await browser.url('http://json.org')
await browser.endTracing()
await fs.writeFile('/path/to/tracelog.json', JSON.stringify(browser.getTraceLogs()))
getPageWeight
命令
傳回上次頁面載入的頁面權重資訊。
await browser.startTracing()
await browser.url('https://webdriverio.dev.org.tw')
await browser.endTracing()
console.log(await browser.getPageWeight())
// outputs:
// { pageWeight: 2438485,
// transferred: 1139136,
// requestCount: 72,
// details: {
// Document: { size: 221705, encoded: 85386, count: 11 },
// Stylesheet: { size: 52712, encoded: 50130, count: 2 },
// Image: { size: 495023, encoded: 482433, count: 36 },
// Script: { size: 1073597, encoded: 322854, count: 15 },
// Font: { size: 84412, encoded: 84412, count: 5 },
// Other: { size: 1790, encoded: 1790, count: 2 },
// XHR: { size: 509246, encoded: 112131, count: 1 } }
// }
設定瀏覽器的下載路徑
cdp
命令可用於呼叫 Devtools Protocol 的 Page.setDownloadBehavior
命令,以設定下載檔案時的行為。請確保 downloadPath
是絕對路徑,並且在下載檔案之前呼叫 browser.cdp()
。
await browser.cdp('Page', 'setDownloadBehavior', {
behavior: 'allow',
downloadPath: '/home/root/webdriverio-project/',
});
存取 Puppeteer 實例
此服務在底層使用 Puppeteer 進行自動化。您可以透過呼叫 getPuppeteer
命令來存取使用的實例。注意:Puppeteer 命令是異步的,必須在 call
命令中呼叫,或透過 async/await
處理。
describe('use Puppeteer', () => {
it('by wrapping commands with call', () => {
await browser.url('http://json.org')
const puppeteer = await browser.getPuppeteer()
const page = await browser.call(() => puppeteer.pages())[0]
console.log(await browser.call(() => page.title()))
})
})
事件監聽器
為了擷取瀏覽器中的網路事件,您可以向 Chrome DevTools 註冊事件監聽器。完整的可用 CDP 網路事件清單。
it('should listen on network events', () => {
await browser.cdp('Network', 'enable')
await browser.on('Network.requestWillBeSent', (event) => {
console.log(`Request: ${event.request.method} ${event.request.url}`);
});
await browser.on('Network.responseReceived', (event) => {
console.log(`Response: ${event.response.status} ${event.response.url}`);
});
await browser.on('Network.loadingFailed', (event) => {
console.log(`Request failed: ${event.errorText}`);
});
await browser.url('https://www.google.com')
})
有關 WebdriverIO 的更多資訊,請參閱首頁。