Webサイトに変更を加えるとき、ユーザーの取りうる行動を繰り返し試して、一貫したユーザー体験を実現できているか確認します。一貫性を保ちながら手軽に試すために、スクリプトで自動化するライブラリーを使って前提条件の表明(アサーション)を確認したり、結果をもとにドキュメントを整備したりします。ヘッドレスブラウザー(headless、GUIを持たないこと)とは、サイト上でのユーザーが取る行動をスクリプトで実行し、実行結果も保存できる、テスト向けのコマンドラインツールです。
開発者の多くはヘッドレスブラウザーに長年、PhantomJS、CasperJSなどのツールを使用してきました。しかし、恋と同じように、私たちの心は別のものへと移ります。Chrome 59から(Windows版は60から)、独自のヘッドレスブラウザーが付きました。現在はまだSeleniumをサポートしていませんが、ChromiumとBlinkレンダリングエンジンを採用しているので、実際のChromeユーザーのシミュレーションできます。
本記事のコードはすべてGitHubリポジトリにあります。
コマンドラインからヘッドレスChromeを実行
コマンドラインからヘッドレスChromeの実行は簡単です。MacではChromeのエイリアスを設定し、—headlessパラメーターを付けて実行します。
alias chrome="/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome”
chrome --headless --disable-gpu --remote-debugging-port=9090 https://www.sitepoint.com/
Linuxはもっと簡単です。
google-chrome --headless --disable-gpu --remote-debugging-port=9090 https://www.sitepoint.com/
- --headless:ユーザーインターフェイスやディスプレイサーバー無しで実行する
- --disable-gpu:GPUハードウェア・アクセラレーションを無効にする。現在、一時的に必要
- --remote-debugging-port:指定したポートでHTTPプロトコル経由でのリモートデバッグができる
リクエストしたページに対する操作もできます。たとえばdocument.body.innerHTMLを「標準出力」で出力します。
google-chrome --headless --disable-gpu --dump-dom http://endless.horse/
ほかにできることを知りたいならパラメーターの完全なリストを確認ください。
Node.jsでヘッドレスChromeを実行する
コマンドラインではなく、Node.jsでヘッドレスChromeを実行するために以下のモジュールが必要です。
- chrome-remote-interface:コマンドと通知のシンプルな抽象化を提供するJavaScript API
- chrome-launcher:マルチプラットフォーム対応でNode内からChromeを実行できるようにする
これで環境の構築ができます。Nodeとnpmがインストールされていない場合はこちらのチュートリアルを参考にインストールします。
mkdir headless
cd headless
npm init -y
npm install chrome-remote-interface --save
npm install chrome-launcher --save
完了後ヘッドレスChromeのセッションを開始します。プロジェクトフォルダー内に、index.jsファイルを作成します。
const chromeLauncher = require('chrome-launcher');
const CDP = require('chrome-remote-interface');
(async function() {
async function launchChrome() {
return await chromeLauncher.launch({
chromeFlags: [
'--disable-gpu',
'--headless'
]
});
}
const chrome = await launchChrome();
const protocol = await CDP({
port: chrome.port
});
// ALL FOLLOWING CODE SNIPPETS HERE
})();
始めに依存オブジェクトをリクエストし、Chromeセッションをインスタンス化する即時関数を作ります。執筆時点では--disable-gpuフラグが必須です。これは不具合の一時回避策としてGoogleが推薦しているだけなので、不要かもしれません。ここではasync/awaitを使い、以降の処理に進む前に確実にヘッドレスブラウザーを立ち上げます。
注意:使う関数には、ページ描画やそのほか処理実行の時間を確保するため、次の処理に進む前に完了が必須のアクションがあります。処理の多くはノンブロッキング処理(そのまま処理を進める)なので、プロミスで実行を停止します。非同期の関数はMozilla Developer Networkもしくはこの記事を参照してください。
今回のテストに必要なドメイン(領域)を定義します。
const {
DOM,
Page,
Emulation,
Runtime
} = protocol;
await Promise.all([Page.enable(), Runtime.enable(), DOM.enable()]);
UI上に描画されるコンテンツにアクセスするために、Pageオブジェクトを使います。どこに移動するのか、どの要素を操作するのか、どこのスクリプトを実行するのかも、Pageオブジェクトに指定します。もっとも重要なオブジェクトです。
ページを操作してみる
セッションを開始してドメインが定義したら、Webサイトを確認します。上記で有効化したPageドメインで開始位置を指定します。
Page.navigate({
url: 'https://en.wikipedia.org/wiki/SitePoint'
});
これでページを読み込みます。loadEventFiredメソッドで実行する処理を定義すれば、ユーザーの取る行動の模擬テストができます。今回の例では最初の段落のコンテンツを取得します:
Page.loadEventFired(async() => {
const script1 = "document.querySelector('p').textContent"
// Evaluate script1
const result = await Runtime.evaluate({
expression: script1
});
console.log(result.result.value);
protocol.close();
chrome.kill();
});
コマンドnode index.jsでスクリプトを実行すると以下の出力が表示されます。
SitePoint is a Melbourne, Australia-based website, and publisher of books, courses and articles for web developers. In January 2014, SitePoint.com had an Alexa ranking of 889,[1] and a Quantcast rating of 14,934.[2]
スクリーンショットの撮影
さらに上記script1を変えれば、リンクをクリックしたり、フォームを埋めたり、クエリセレクタを使い連続した処理をさせたりできます。各処理はJSON形式の設定ファイルに保存し、Nodeスクリプト上に読み込まれて連続で実行します。実行結果がUI/UXに求めることを満たせたかはMochaなどのテストプラットフォームで確認します。
テストスクリプトを強化し、Webページを移動しながらスクリーンショットを撮る機能を備えた関数「captureScreenshot」が用意されています。
const chromeLauncher = require('chrome-launcher');
const CDP = require('chrome-remote-interface');
const file = require('fs');
(async function() {
...
Page.loadEventFired(async() => {
const script1 = "document.querySelector('p').textContent"
// Evaluate script1
const result = await Runtime.evaluate({
expression: script1
});
console.log(result.result.value);
const ss = await Page.captureScreenshot({format: 'png', fromSurface: true});
file.writeFile('screenshot.png', ss.data, 'base64', function(err) {
if (err) {
console.log(err);
}
});
protocol.close();
chrome.kill();
});
})();
fromSurfaceフラグも、執筆時点ではクロスプラットフォーム対応のために必要なフラグです。将来のバージョンでは不要になる可能性があります。
コマンドnode index.jsでスクリプトを実行すると、以下のように出力されます。
最後に
いま自動化のためにスクリプトを書いているのなら、Chromeのヘッドレスブラウザーをおすすめします。Seleniumをはじめとしたツールとの統合が完全ではないものの、Chromeのレンダリングエンジンでシミュレーションができる利点をあなどってはいけません。完全な自動化を活用しながらユーザーエクスペリエンスを改善する、優れた方法です。
- APIのドキュメント類:https://chromedevtools.github.io/devtools-protocol/
- ヘッドレスChromeの始め方:https://developers.google.com/web/updates/2017/04/headless-chrome
(原文:Quick Tip: Getting Started with Headless Chrome in Node.js)
[翻訳:西尾 健史/編集:Livit]