2020-11-24に更新

BackstopJS を試す (Error: Failed to launch the browser process! エラー発生→ puppeteer のバージョンを指定して解決)

読了目安:26分

経緯

Jest でユニットテストができることは分かりました。何かを処理するものに関しては行けそうな気がします。

……しかし、Webサイトの場合は見た目も気になります。見た目をテストするにはどううれば良いのか……と考えていたところ、 BackstopJS というものがあることを知ったので試してみることにします。

準備

見た目のテストのためには何かページを生成するものが必要です。

今回は簡単に試せる環境として、手前味噌ですが arm-band/mori を使うことにしました。

1. パッケージ追加

package.jsonbackstopjs を追加します。

```json:package.json
// 略
"devDependencies": {
"backstopjs": "^5.0.6", // 追加
"browser-sync": "^2.26.12",
// 略
},
// 略


### 2. yarn これで `yarn` してインストールを待ちます。 ```bash > yarn

BackstopJS の準備

後は手順通りに backstop init して

> backstop init

生成された backstop.jsonscenarios -> url を編集。今回は Browsersync のローカルサーバにしたいので https://localhost:3000/ で。

```json:package.json
// 略
"scenarios": [
{
"label": "BackstopJS Homepage",
"cookiePath": "backstop_data/engine_scripts/cookies.json",
// "url": "https://garris.github.io/BackstopJS/",
"url": "https://localhost:3000/", // 変更
"referenceUrl": "",
// 略
}
]
// 略


## 検証1 (失敗) ここまで準備をしたら、正解の見本を保存するために `backstop reference` でスクリーンショットを撮ります。 まずは `yarn start` で Gulpタスク を走らせて `https://localhost:3000/` を上げます。 次に、上述の `backstop reference` コマンドを実行します。 ```bash > backstop reference BackstopJS v5.0.6 Loading config: PATH\TO\TEST_PROJECT\randa_back_stopper\backstop.json COMMAND | Executing core for "reference" clean | backstop_data/bitmaps_reference was cleaned. createBitmaps | Selected 1 of 1 scenarios. COMMAND | Command "reference" ended with an error after [1.414s] Error: Failed to launch the browser process! TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md at onClose (PATH\TO\TEST_PROJECT\randa_back_stopper\node_modules\puppeteer\lib\Launcher.js:750:14) at ChildProcess.<anonymous> (PATH\TO\TEST_PROJECT\randa_back_stopper\node_modules\puppeteer\lib\Launcher.js:740:61) at ChildProcess.emit (events.js:326:22) at Process.ChildProcess._handle.onexit (internal/child_process.js:276:12) # 略

……あれ?失敗しました。

Command "reference" ended with an error after

Error: Failed to launch the browser process!

エラーメッセージ的にはこの部分が原因っぽいですね。

そして、問題を起こしているのは puppeteer のようです。

ちなみに、 BackstopJS がスクリーンショットを撮る際は Puppeteer からヘッドレスブラウザとして Chromium でレンダリングしている模様。

ということで、原因は Puppeteer にあり。エラー文で検索しましたが、イマイチ情報が少ない上にあまり新しい情報がないので、半ば手探りで追及してみることになりました……。

Puppeteer の検証1

まずはそもそも Puppeteer が自分の環境で動くか確認してみます。

> node -v
v14.8.0

> npm -v
6.14.7

Node.js とnpm のバージョン確認。

この状態で新規のプロジェクトを作成します。

package.json

```json:package.json
{
"name": "puppeteer_test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"puppeteer": "^5.5.0"
}
}


`npm init` で `package.json` を用意して、 `yarn add --dev puppeteer` で `devDependencies` に追加します。 ### index.js ```javascript:index.js const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://google.com/'); await page.screenshot({path: 'screenshot.png'}); await browser.close(); })();

Google のスクリーンショットを撮影するだけの簡単な JS を用意します。

これで

> yarn

# 略

> node index.js
>

……普通に撮影でき、 screenshot.png が保存されました。問題ありません。

Puppeteer の検証2

次に、 BackstopJS の内部で使用されている Puppeteer のバージョンを確認するために node_modules/backstopjs/package.json を調べます。

```json:package.json
{
"name": "backstopjs",
"version": "5.0.6",
"description": "BackstopJS: Catch CSS curveballs.",
"bin": {
"backstop": "./cli/index.js"
},
"scripts": {
"lint": "eslint 'compare/.js' 'compare/src//*.js' 'core//.js' 'cli//*.js' 'capture//.js' 'test//*.js'",
"format": "prettier-eslint --write \"compare/src/
/
.js\"",
"genConfig": "node ./cli/index.js genConfig",
"init": "node ./cli/index.js init",
"reference": "node ./cli/index.js reference",
"test": "node ./cli/index.js test",
"approve": "node ./cli/index.js approve",
"openReport": "node ./cli/index.js openReport",
"report": "node ./cli/index.js openReport",
"echo": "node ./cli/index.js echo",
"unit-test": "mocha --reporter spec --recursive 'test//*_spec.js'",
"precommit": "lint-staged",
"build-compare": "cp ./node_modules/diverged/src/diverged.js ./compare/output/ && cp ./node_modules/diff/dist/diff.js ./compare/output/ && webpack --config ./compare/webpack.config.js && npm run lint",
"dev-compare": "webpack-dev-server --content-base ./compare/output --config ./compare/webpack.config.js",
"integration-test": "rm -rf newdir && mkdir newdir && cd newdir && node ../cli/index.js genConfig && node ../cli/index.js reference && node ../cli/index.js test && node -e \"require('../')('test')\"",
"smoke-test": "cd test/configs/ && node ../../cli/index.js test --config=backstop_features",
"smoke-test-docker": "cd test/configs/ && node ../../cli/index.js test --config=backstop_features --docker",
"sanity-test": "cd test/configs/ && node ../../cli/index.js test",
"sanity-test-docker": "cd test/configs/ && node ../../cli/index.js test --docker",
"kill-zombies": "pkill -f \"(chrome)?(--headless)\"",
"copy-report-bundle": "mkdir -p test/configs/backstop_data/html_report && cp compare/output/index_bundle.js test/configs/backstop_data/html_report/",
"build-and-copy-report-bundle": "npm run build-compare && npm run copy-report-bundle",
"remote": "node ./cli/index.js remote",
"stop": "curl http://localhost:3000/stop"
},
"lint-staged": {
"compare/src/
/*.js": [
"node_modules/.bin/prettier --single-quote --write",
"git add"
]
},
"repository": {
"type": "git",
"url": "https://github.com/garris/backstopjs.git"
},
"author": "https://github.com/garris/BackstopJS/graphs/contributors",
"license": "MIT",
"main": "core/runner.js",
"devDependencies": {
"assert": "^1.4.1",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"backstop-twentytwenty": "^1.0.4",
"eslint": "^5.6.1",
"eslint-config-semistandard": "^12.0.1",
"eslint-config-standard": "^12.0.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-react": "^7.11.1",
"eslint-plugin-standard": "^4.0.0",
"file-loader": "^2.0.0",
"lint-staged": "^4.3.0",
"mocha": "^5.2.0",
"mockery": "^1.4.0",
"prettier": "^1.14.3",
"prettier-eslint-cli": "^4.7.1",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-modal": "^3.0.3",
"react-redux": "^5.0.6",
"react-sticky": "^6.0.1",
"react-toggle-button": "^2.1.0",
"react-visibility-sensor": "^3.11.1",
"redux": "^3.7.2",
"sinon": "^1.17.7",
"styled-components": "^2.1.2",
"url-loader": "^1.1.1",
"webpack": "^4.43.0",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.3.1"
},
"dependencies": {
"chalk": "^1.1.3",
"diverged": "^0.1.3",
"fs-extra": "^0.30.0",
"jump.js": "^1.0.2",
"junit-report-builder": "^1.3.3",
"lodash": "^4.17.11",
"merge-img": "^2.1.3",
"minimist": "^1.2.0",
"node-resemble-js": "^0.2.0",
"object-hash": "1.1.5",
"opn": "^5.3.0",
"os": "^0.1.1",
"p-map": "^1.1.1",
"path": "^0.12.7",
"portfinder": "^1.0.17",
"puppeteer": "^2.0.0",
"resolve": "^1.11.1",
"super-simple-web-server": "^1.1.2",
"temp": "^0.8.3"
}
}


`puppeteer` のバージョンは…… `2.0.0` ?!先ほど入れた最新版は `5.5.0` だったのでかなりバージョンに乖離があります。 ということで先程の Puppeteer のテストプロジェクトの `package.json` を直します。 ```json:package.json { "name": "puppeteer_test", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "devDependencies": { "puppeteer": "^2.0.0" } }

はい。 2.0.0 です。これで yarn し直して node index.js を実行。

> node index.js
(node:28172) UnhandledPromiseRejectionWarning: Error: Failed to launch the browser process!


TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md

    at onClose (PATH\TO\TEST_PROJECT\puppeteer_test\node_modules\puppeteer\lib\Launcher.js:750:14)      
    at ChildProcess.<anonymous> (PATH\TO\TEST_PROJECT\puppeteer_test\node_modules\puppeteer\lib\Launcher.js:740:61)
    at ChildProcess.emit (events.js:326:22)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:276:12)

Error: Failed to launch the browser process! のエラーが出て、 BackstopJS のプロジェクトで出た内容を再現できました。

エラーの発生個所も node_modules\puppeteer\lib\Launcher.js:750:14 以下同じです。

念のための確認

エラー文の中にあるトラブルシューティングなど、いくつかの記事で「 puppeteer.launch() のオプションで ignoreDefaultArgs: ['--disable-extensions'] を指定すると良い」と書かれていたので、一応やってみます。

```javascript:index.js
const puppeteer = require('puppeteer');

(async () => {
//const browser = await puppeteer.launch();
const browser = await puppeteer.launch({
ignoreDefaultArgs: ['--disable-extensions'],
});

const page = await browser.newPage();
await page.goto('https://google.com/');
await page.screenshot({path: 'screenshot.png'});

await browser.close();
})();


修正して、実行。 ```bash > node index.js (node:31444) UnhandledPromiseRejectionWarning: Error: Failed to launch the browser process! TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md at onClose (PATH\TO\TEST_PROJECT\puppeteer_test\node_modules\puppeteer\lib\Launcher.js:750:14) at ChildProcess.<anonymous> (PATH\TO\TEST_PROJECT\puppeteer_test\node_modules\puppeteer\lib\Launcher.js:740:61) at ChildProcess.emit (events.js:326:22) at Process.ChildProcess._handle.onexit (internal/child_process.js:276:12)

はい、変わりませんでした。

検証2 (成功)

以上より、 Puppeteer のバージョンをどうにかすれば BackstopJS が動きそうなことが分かりました。

そこで、 Yarn の依存関係の解決オプションの resolutions を試してみることにします。

```json:package.json
// 略
"devDependencies": {
"backstopjs": "^5.0.6", // 追加
"browser-sync": "^2.26.12",
// 略
},
"resolutions": {
"puppeteer": "^5.5.0" // 追加
},
// 略


これで `yarn` し直して、念のため `backstop_data`ディレクトリ と `backstop.json` を削除して `backstop init` をやり直し、再度 `url` を `https://localhost:3000/` に直した上で、 `backstop reference` を実行。 ```bash > backstop reference BackstopJS v5.0.6 Loading config: PATH\TO\TEST_PROJECT\randa_back_stopper\backstop.json COMMAND | Executing core for "reference" clean | backstop_data/bitmaps_reference was cleaned. createBitmaps | Selected 1 of 1 scenarios. CREATING NEW REFERENCE FILE CREATING NEW REFERENCE FILE Cookie state restored with: [ { "path": "/", "name": "yourCookieName", "value": "yourCookieValue", "expirationDate": 1798790400, "hostOnly": false, "httpOnly": false, "secure": false, "session": false, "sameSite": "no_restriction", "url": "https://.www.yourdomain.com" } ] Cookie state restored with: [ { "path": "/", "name": "yourCookieName", "value": "yourCookieValue", "expirationDate": 1798790400, "hostOnly": false, "httpOnly": false, "secure": false, "session": false, "sameSite": "no_restriction", "url": "https://.www.yourdomain.com" } ] Browser Console Log 0: JSHandle:BackstopTools have been installed. SCENARIO > BackstopJS Homepage x Close Browser Browser Console Log 0: JSHandle:BackstopTools have been installed. SCENARIO > BackstopJS Homepage x Close Browser Run `$ backstop test` to generate diff report. COMMAND | Command "reference" successfully executed in [3.752s]

動きました!

リグレッションテストの検証

ようやく backstop reference が動いたので、リグレッションテストを試してみます。

まずは編集前( backstop reference したとき)のページのキャプチャ画像。

編集前のページのキャプチャ画像

次に、 Scss や JS などいくつかパラメータやコードを編集して、 backstop test します。

編集後のページのキャプチャ画像

> backstop test
BackstopJS v5.0.6
Loading config:  PATH\TO\TEST_PROJECT\randa_back_stopper\backstop.json

COMMAND | Executing core for "test"
createBitmaps | Selected 1 of 1 scenarios.
Cookie state restored with: [
  {
    "path": "/",
    "name": "yourCookieName",
    "value": "yourCookieValue",
    "expirationDate": 1798790400,
    "hostOnly": false,
    "httpOnly": false,
    "secure": false,
    "session": false,
    "sameSite": "no_restriction",
    "url": "https://.www.yourdomain.com"
  }
]
Cookie state restored with: [
  {
    "path": "/",
    "name": "yourCookieName",
    "value": "yourCookieValue",
    "expirationDate": 1798790400,
    "hostOnly": false,
    "httpOnly": false,
    "secure": false,
    "session": false,
    "sameSite": "no_restriction",
    "url": "https://.www.yourdomain.com"
  }
]
Browser Console Log 0: JSHandle:Invisible Full-moon!
Browser Console Log 0: JSHandle:BackstopTools have been 
installed.
SCENARIO > BackstopJS Homepage
x Close Browser
Browser Console Log 0: JSHandle:Invisible Full-moon!
Browser Console Log 0: JSHandle:BackstopTools have been 
installed.
SCENARIO > BackstopJS Homepage
x Close Browser
      COMMAND | Executing core for "report"
   See: PATH\TO\TEST_PROJECT\randa_back_stopper\backstop_data\bitmaps_test\yyyymmdd-hhiiss\failed_diff_backstop_default_BackstopJS_Homepage_0_document_0_phone.png
      compare | ERROR { requireSameDimensions: true, size: isDifferent, content: 2.06%, threshold: 0.1% }: BackstopJS Homepage backstop_default_BackstopJS_Homepage_0_document_0_phone.png
   See: PATH\TO\TEST_PROJECT\randa_back_stopper\backstop_data\bitmaps_test\yyyymmdd-hhiiss\failed_diff_backstop_default_BackstopJS_Homepage_0_document_1_tablet.png
      compare | ERROR { requireSameDimensions: true, size: isDifferent, content: 0.70%, threshold: 0.1% }: BackstopJS Homepage backstop_default_BackstopJS_Homepage_0_document_1_tablet.png
       report | Test completed...
       report | 0 Passed
       report | 2 Failed
       report | Writing browser report
       report | Resources copied
       report | Copied json report to: PATH\TO\TEST_PROJECT\randa_back_stopper\backstop_data\bitmaps_test\yyyymmdd-hhiiss\report.json
       report | Copied jsonp report to: PATH\TO\TEST_PROJECT\randa_back_stopper\backstop_data\html_report\config.js
      COMMAND | Executing core for "openReport"
   openReport | Attempting to ping 
   openReport | Remote not found. Opening backstop_data\html_report\index.html
       report | *** Mismatch errors found ***
      COMMAND | Command "report" ended with an error after [1.539s]
      COMMAND | Error: Mismatch errors found.
                    at PATH\TO\TEST_PROJECT\randa_back_stopper\node_modules\backstopjs\core\command\report.js:189:17
                    at processTicksAndRejections (internal/process/task_queues.js:93:5)
      COMMAND | Command "test" ended with an error after [5.431s]
      COMMAND | Error: Mismatch errors found.
                    at PATH\TO\TEST_PROJECT\randa_back_stopper\node_modules\backstopjs\core\command\report.js:189:17
                    at processTicksAndRejections (internal/process/task_queues.js:93:5)

エラーが出ましたね。

BackstopJS のテスト失敗画面

また、自動的にブラウザも上がってきて上のように差分も表示されます。

BackstopJS のテスト失敗、差分の比較画面

「DIFF」の画像をクリックした先では backstop reference で撮影したスクリーンショットと backstop test で撮影したスクリーンショットの差分をワイパーのように比較することができます。

細かく比較できて良いですね。


今度は修正を元に戻して再度 backstop test

> backstop test
BackstopJS v5.0.6
Loading config:  PATH\TO\TEST_PROJECT\randa_back_stopper\backstop.json

COMMAND | Executing core for "test"
createBitmaps | Selected 1 of 1 scenarios.
Cookie state restored with: [
  {
    "path": "/",
    "name": "yourCookieName",
    "value": "yourCookieValue",
    "expirationDate": 1798790400,
    "hostOnly": false,
    "httpOnly": false,
    "secure": false,
    "session": false,
    "sameSite": "no_restriction",
    "url": "https://.www.yourdomain.com"
  }
]
Cookie state restored with: [
  {
    "path": "/",
    "name": "yourCookieName",
    "value": "yourCookieValue",
    "expirationDate": 1798790400,
    "hostOnly": false,
    "httpOnly": false,
    "secure": false,
    "session": false,
    "sameSite": "no_restriction",
    "url": "https://.www.yourdomain.com"
  }
]
Browser Console Log 0: JSHandle:BackstopTools have been 
installed.
SCENARIO > BackstopJS Homepage
Browser Console Log 0: JSHandle:BackstopTools have been 
installed.
SCENARIO > BackstopJS Homepage
x Close Browser
x Close Browser
      COMMAND | Executing core for "report"
      compare | OK: BackstopJS Homepage backstop_default_BackstopJS_Homepage_0_document_0_phone.png
      compare | OK: BackstopJS Homepage backstop_default_BackstopJS_Homepage_0_document_1_tablet.png
       report | Test completed...
       report | 2 Passed
       report | 0 Failed
       report | Writing browser report
       report | Resources copied
       report | Copied json report to: PATH\TO\TEST_PROJECT\randa_back_stopper\backstop_data\bitmaps_test\yyyymmdd-hhiiss\report.json
       report | Copied jsonp report to: PATH\TO\TEST_PROJECT\randa_back_stopper\backstop_data\html_report\config.js
      COMMAND | Executing core for "openReport"
   openReport | Attempting to ping
   openReport | Remote not found. Opening backstop_data\html_report\index.html
      COMMAND | Command "report" successfully executed in [0.698s]
      COMMAND | Command "test" successfully executed in 
[4.135s]

成功です。

BackstopJS のテスト成功画面

今度はテスト成功の画面で、同じスクリーンショットが撮影できていることが確認できます。


ということで、軽くですが BackstopJS が使えることを確認しました。

実際にはシナリオの編集とか色々あるようですが、ひとまず動作確認が取れたということで。

追加検証

動作確認が取れたところで、より実践的になりそうな部分を深堀り。

  • ブラウザサイズ: PCサイズも追加
  • BrowserSync のリロード通知を削除: スクリーンショットのタイミングである/なしで分かれてしまうので一律非表示にしてDIFF検知されないようにする
  • 複数ページの場合:
    • シナリオを追加
    • label をファイル名に使用するので半角英数字で記述、ページごとにユニークな名前にする
  • 縦長ページの場合: 普通に全体のスクリーンショットを撮影する
  • position: fixed; のナビゲーションバー: 上端

何回かテストを試して気になったのはこの辺りですかね。いずれもクリア、問題なさそうです。

BackstopJS のテスト失敗画面 (複数ページテスト実行、Browsersyncの通知非表示、縦長ページ、position:fixed:top;のナビゲーションバーあり)

わざと失敗させた場合の画面。

BackstopJS の比較画面

縦長ページでは普通に下までスクロールできます。

BackstopJS のテスト成功画面 (複数ページテスト実行、Browsersyncの通知非表示、縦長ページ、position:fixed:top;のナビゲーションバーあり)

成功画面。最初のテストのときと同様、特に問題はなさそうです。

参考

BackstopJS

Puppeteer の Error: Failed to launch the browser process! エラー対処

Yarn の reasolutions

Originally published at labor.ewigleere.net
ツイッターでシェア
みんなに共有、忘れないようにメモ

arm-band

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント