Devtoolsサービス
テストでChrome DevToolsコマンドを実行できるようにするWebdriverIOサービス
Chrome v63以降、ブラウザはマルチクライアントのサポートを開始し、任意のクライアントがChrome DevTools Protocolにアクセスできるようになりました。これにより、WebDriverプロトコルを超えてChromeを自動化する興味深い機会が生まれます。このサービスを使用すると、wdio browserオブジェクトを拡張してそのアクセスを活用し、テスト内でChrome DevToolsコマンドを呼び出して、リクエストをインターセプトしたり、ネットワーク機能を調整したり、CSS/JSカバレッジを取得したりできます。
Firefox 86以降、"moz:debuggerAddress": true
という機能を追加することで、Chrome DevTools Protocolのサブセットが実装されました。
注:このサービスは現在、Chrome v63以降、Chromium、およびFirefox 86以降のみをサポートしています。クラウドベンダーがChrome DevTools Protocolへのアクセスを公開していないため、このサービスは通常、ローカルでテストを実行するか、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
は、WebDriverプロトコルを超えてChromeを自動化するのに役立つさまざまな機能を提供します。Chrome DevToolsプロトコルへのアクセスと、Puppeteerインスタンスへのアクセスを提供し、Puppeteer自動化インターフェースを使用してChromeを自動化できます。
パフォーマンステスト
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 Performance Scoreを返します:firstContentfulPaint
、speedIndex
、largestContentfulPaint
、cumulativeLayoutShift
、totalBlockingTime
、interactive
、maxPotentialFID
またはcumulativeLayoutShift
。
console.log(await browser.getPerformanceScore())
/**
* 0.897826278457836
*/
enablePerformanceAudits
url
コマンドを呼び出すか、リンクをクリックするか、ページロードを引き起こすすべてのページロードに対して、自動パフォーマンス監査を有効にします。設定オブジェクトを渡して、一部のスロットルオプションを決定できます。デフォルトのスロットルプロファイルは、CPUスロットルが4倍のGood 3G
ネットワークです。
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.dokyumento.jp')
// 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 Protocolにアクセスする方法が2つあります。
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のようなもので、ノードの識別子です。これは、DOM.focus
などの他のChrome DevToolsメソッドのパラメータとして使用できます。
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.dokyumento.jp')
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の詳細については、ホームページをご覧ください。