jsでスクリーンショットを比較してデザインデグレの自動検出をする



E2Eテストの自動化にトライしてみました。
今回はUIの細かい挙動のテストではなく、デザインのデグレ検出にフォーカスをしています。

Webデザインにおいて1pxへのこだわりは非常に重要です。大規模なサービスになると、1pxの違いで数億円の売上に影響することも珍しくありません。
www.nikkei.com


複数人のチームで開発を進めていて、意図せずデザインのデグレが発生していまうことがあります。この時、1px単位のごくわずかな差分の場合、肉眼では見落としてしまいがちです。しかしながら1pxの差が事業に影響を与える可能性があるので、デザインのデグレは防ぐ必要があります。

今回はUIの差分を自動検知する簡易ツールを作成してみます。
nightwatch.jsとresemble.jsを利用します。
Nightwatch.js | Node.js powered End-to-End testing framework
Resemble.js : Image analysis


実装に入る前に、どれほどの精度で差分が検出できるのかお見せします。

nightwatch.jsの公式サイトでサンプルを用意してみました。
(今回はサンプルのため意図的に変更しています。)
beforeとafterで5箇所の差分が発生していますが、わかりますか?

# before

f:id:tky_bpp:20160712170619p:plain

# after

f:id:tky_bpp:20160712170609p:plain

いかがでしょうか?
ここで、beforeとafterの画像をresemble.jsを使って比較をしてみます。
そうすると、2つの画像の差分をこのように検出することができるのです。

# answer

f:id:tky_bpp:20160712170554p:plain

5つの差分は

  1. 左上のロゴの位置が2pxずれた
  2. 右上のHomeリンクからアンダーラインが消えた
  3. 画面中央の「Write End-to-End〜」の文章がボールドになった
  4. Downloadボタンの幅が狭くなった
  5. 画面下部の「Nightwatch.js is an easy〜」の文章のフォントカラーが変わった

でした。


細かい。


こんな細かい差分なら放っておいてもいいじゃん、という人もいます。
しかしながら、インターネットの世界では1pxの差分が数億円を左右することに繋がるのです。
とはいえ、こんな細かいところまで差分が無いか人力で確認しようとすればとても労力が必要になります。
肉眼だと気付きにくい差分でも自動化してしまえば一瞬ですね。

実装

2つの要素から構成しています。
nighwatch.js → 画面のスクリーンショットを保存する
resemble → スクリーンショットを比較して差分を検出する

スクリーンショットの保存はnighwatch.jsを利用すれば簡単にできます。
対象のURLを指定して、saveScreenshotを実行するだけです。

'Sample test' : function (browser) {
    browser
      .url('http://nightwatchjs.org/')
      .saveScreenshot('example.png')
      .end();
}

これを日次やコミット毎など定期的に実行するようにすれば便利です。

そして、画像ファイルの比較はresembleを利用してこのように書きます。

const fs = require('fs');
const resemble = require('node-resemble-js');

const file1 = Buffer(fs.readFileSync('file_path_to_before.png'));
const file2 = Buffer(fs.readFileSync('file_path_to_after.png'));

resemble.outputSettings({ transparency: 0.1 });
resemble(file1).compareTo(file2).onComplete( data => {
    if(data.misMatchPercentage >= 0.01) {
        data.getDiffImage().pack().pipe(fs.createWriteStream('file_path_to_diff.png'));
    }
});

比較結果はonComplete()の中で扱うことが出来ます。
差分の割合がmisMatchPercentageとして格納されているので、一定の割合で差分があった場合のみ処理をすることも簡単に出来ます。
また、差分の画像データもgetDiffImage()を利用して取得できるので、外部ファイルとして保存することが出来ます。