Edited at

nightwatch+resemblejsでデザインのデグレチェックをしてみた


前書き

社内でnightwatchをWinMergeを使用してデグレチェックしたよーって話があったので、

WinMergeの部分も自動化してみました。

ツールを作ったのなら、こっちにも展開してくれよーと思うのだがなー。。。


やること


  • nightwatchでサイトのスクリーンショットを撮影(master/作業ブランチそれぞれで)

  • 撮影したスクリーンショットの差異をresemblejs出力する


事前準備


Seleniumを使用するのでJDKのインストール

公式からインストールしてください

https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

# バージョンがでればOK

$ java -version


必要なモジュールのインストール

$ npm init -y

$ npm install nightwatch
$ npm install webdriver-manager
$ npm install resemblejs

# babelも使用したので以下も入れておく
$ babel-cli
$ npm install babel-register
$ npm install babel-preset-es2015


WebDriverのダウンロード

webdriver-managerを利用してWebDriverダウンロードする

node_modules/.bin/webdriver-manager update

node_modules/.bin/webdriver-manager update --chrome

ダウンロードされていることを確認

パスは後で使用するのでメモっておく

$ find node_modules/webdriver-manager/selenium/ -type f

node_modules/webdriver-manager/selenium/chromedriver_2.43.exe
node_modules/webdriver-manager/selenium/geckodriver-v0.23.0.exe
node_modules/webdriver-manager/selenium/selenium-server-standalone-3.141.0.jar
...


Nightwatch


nightwatchのconfigファイルを書く

事前にnightwatchに必要なディレクトリを作成しておきます。

configファイルで設定するので場所はどこでもOK!

$ mkdir tests

$ mkdir reports

公式ページの「Configuration」部分をコピーしてnightwatch.jsonに記述します。

http://nightwatchjs.org/gettingstarted

一部調整が必要なので書き換えてください。

・src_folders,output_folderに作成したディレクトリを設定

・server_pathにseleniumのパスを設定

・webdriver.chrome.driverにはwebdriverのパスを設定

$ vi nightwatch.json


nightwatch.json

{

"src_folders" : ["tests"],
"output_folder" : "reports",
"custom_commands_path" : "",
"custom_assertions_path" : "",
"page_objects_path" : "",
"globals_path" : "node_modules/babel-register",

"selenium" : {
"start_process" : true,
"server_path" : "node_modules/webdriver-manager/selenium/selenium-server-standalone-3.141.0.jar",
"log_path" : "",
"port" : 4444,
"cli_args" : {
"webdriver.chrome.driver" : "node_modules/webdriver-manager/selenium/chromedriver_2.43.exe",
"webdriver.gecko.driver" : "node_modules/webdriver-manager/selenium/geckodriver-v0.23.0.exe",
}
},

"test_settings" : {
"default" : {
"launch_url" : "http://localhost",
"selenium_port" : 4444,
"selenium_host" : "localhost",
"silent": true,
"screenshots" : {
"enabled" : false,
"path" : ""
},
"desiredCapabilities": {
"browserName": "chrome",
"marionette": true
}
},

"chrome" : {
"desiredCapabilities": {
"browserName": "chrome",
}
}
}
}



スクリーンショットを撮影するテスト作成

# スクリーンショットを出力するディレクトリは作っておいてください。

$ mkdir screenshots

# スクリーンショットを撮影したいURL一覧を作成しておく
# 今回はこのURL一覧のスクリーンショットを撮影するようにします
$ vi urls.txt
https://qiita.com/
...

$ vi tests/test.js


tests/test.js

require('date-utils'); // 使用する場合はnpm install date-utilが必要

const fs = require('fs');
const url = require('url');

module.exports = {
'@tags': ['screenshots'],
// スクリーンショットテストケース
'screenshots': function (browser) {
// 出力先ディレクトリ
const time = (new Date()).toFormat("YYYYMMDDHH24MISS");
const outputDir = `screenshots/test_${time}`;

// URL一覧取得
const data = fs.readFileSync('urls.txt', "utf8");
const urls = data.split("\n").filter((item) => {
return url.parse(item).protocol; // URLチェック(プロトコルがあるのものだけ)
});

// スクリーンショットの出力先ディレクトリ作成
fs.mkdirSync(outputDir);
// URL一覧のスクリーンショットを撮影
urls.forEach(function(targetUrl, index) {
browser
.url(targetUrl)
.saveScreenshot(`${outputDir}/fileName${index}.png`);
});
browser.end();
}
};



Resemblejs


スクリーンショットを比較スクリプト

resemblejsを使用してスクショを比較するスクリプトを書きます

かなり雑な書き方になってしまったのでお許しを。。。

$ vi compareScreenshot.js


compareScreenshot.js

require('date-utils');

const fs = require('fs');
const compareImages = require("resemblejs/compareImages");

/* 実行時間 */
const nowtime = (new Date()).toFormat("YYYYMMDDHH24MISS");
/* スクリーンショットディレクトリ */
const screenshotsDir = 'screenshots';
/* レポートの出力先 */
const outputDir = `output/report_${nowtime}`;
/* 差分画像出力先 */
const outputDiffDir = `${outputDir}/diff`;
/* レポートファイル名 */
const reportFile = `${outputDir}/report.tsv`;
/* レポート項目 */
const reportItem = [
"比較ファイル1",
"比較ファイル2",
"比較結果",
"不一致率(%)",
"差分画像ファイル"
];
/* 差分ファイル名用ユニークindex */
let index = 0;

/* 画像の比較 */
async function compareDiff(filePath1, filePath2) {
const data = await compareImages(
fs.readFileSync(filePath1),
fs.readFileSync(filePath2),
{
// 設定値は好きなように調整する
output: {
errorColor: { red: 255, green: 255, blue: 0 },
errorType: "movement",
transparency: 0.2,
largeImageThreshold: 1200,
useCrossOrigin: false,
outputDiff: true
},
scaleToSameSize: true,
ignore: "antialiasing"
}
);
return data;
}

/* レポートの書き出し */
async function createReport(data, filePath1, filePath2) {
let report = [];
report[0] = filePath1;
report[1] = filePath2;
report[2] = 0;
report[3] = 0;
report[4] = '';

if (data.misMatchPercentage > 0) {
report[2] = 1;
report[3] = data.misMatchPercentage;
report[4] = `${outputDiffDir}/${index++}.png`;
fs.writeFileSync(report[4], data.getBuffer());
}
fs.appendFileSync(reportFile, report.join("\t") + "\n");
}

/* チェック処理 */
async function check(filePath1, filePath2) {
const data = await compareDiff(filePath1, filePath2);
createReport(data, filePath1, filePath2);
}

/* ディレクトリなどの準備 */
function prepare() {
fs.mkdirSync(outputDir);
fs.mkdirSync(outputDiffDir);
fs.writeFileSync(reportFile, "");
fs.appendFileSync(reportFile, reportItem.join("\t") + "\n");
}

/* メイン処理 */
function main() {
if (process.argv.length < 4) {
console.log("引数を2つ指定してください。");
process.exit();
}

const dir1 = `${screenshotsDir}/${process.argv[2]}`;
const dir2 = `${screenshotsDir}/${process.argv[3]}`;
if (!fs.existsSync(dir1)) {
console.log(`指定ディレクトリが存在しません。${dir1}`);
process.exit();
}
if (!fs.existsSync(dir2)) {
console.log(`指定ディレクトリが存在しません。${dir2}`);
process.exit();
}

const fileList = fs.readdirSync(dir1);
if (fileList.length == 0) {
console.log(`ディレクトリにデータが存在しません ${dir1}`);
}

prepare();
fileList.forEach((fileName) => {
check(`${dir1}/${fileName}`, `${dir2}/${fileName}`);
});
console.log(`チェックが完了しました! => ${outputDir}`);
};
main();



デグレ確認を行う!


nightwatchを実行し、スクリーンショットを撮影する

デグレを確認したいのでmasterと作業ブランチでそれぞれスクリーンショットを撮影しましょう

$ node_modules/.bin/nightwatch --tag screenshots

# => test_YYYYMMDDhhmmssみたいなディレクトリにスクショが出力されます


差分の出力

差分を比較したいディレクトリを指定して実行してください。

$ node_modules/.bin/babel-node compareScreenshot.js test_20181102114551 test_20181102114507

チェックが完了しました! => output/report_20181102130524


出力結果

以下みたいにtsvで差分情報が出力されます。

不一致率が0でなかったら差分があるので対象の差分画像ファイルを確認してみましょう

比較ファイル1 比較ファイル2 比較結果    不一致率(%) 差分画像ファイル

screenshots/test_20181102130429/fileName0.png screenshots/test_20181102130408/fileName0.png 1 3.29 output/report_20181102130524/diff/0.png


差分画像ファイル

差分画像は以下みたいな感じになっています。

差分のある部分が黄色く表示されていますね

qiitaのトップページとかだと広告部分が変わるので差分としてでてるのがわかります

0.png