こんにちは!のせっち@nosecchi01です。
サイト運営をしていたり、他の方からコードを引き継いだ時、「このコードはいるのかな?」と思うときがあると思います。しかし、
もしかしたら、ページが崩れるかもしれない…
と考えて放置した(している)という経験は誰にでもあるはずです。
特にCSSはグローバルに効いてしまうので、思わぬところで表示崩れが起きてしまう可能性があります。
変更前と変更後をスクリーンショット比較で確認できる『ビジュアルリグレッションテスト』というものがあります。
対象ページを全て比較して、差分があれば、色を付けて教えてくれるので非常に便利です。
backstopjsとは
ビジュアルリグレッションテストを行うツールの一つです。
上記の通り、REFERENCE – TEST – DIFFと並んでいて、差分がある場合には、色を付けて表示してくれます。
(背景色を黒からグレーに変わっているのでDIFFと判定されています。)
視覚的に差分がわかるので、意図している変更なのか、そうではないのか、一目瞭然です!
それではbackstopjsのセットアップ方法を解説します。
backstopjsのインストール
「ビジュアルリグレッションテストを行いたい」ということは既にプロジェクトがあるはずですので、そちらに移動してください。
cd PROJECT_NAME
プロジェクトに移動したら、早速パッケージをインストールしていきましょう!
npm i -D backstopjs
npm関連のファイル群がない人は先にnpm init -y
しておいてください。
必要なパッケージはこれだけですが、設定しないと何も動きません。
backstopjsの設定
backstopjsの設定をしていきます。
backstop.config.js
を作成してください。
下記を追加します。
// --production を指定すると本番環境を見る
let baseUrl = "http://localhost:8080/"; // ローカルサーバーのURL
if (process.argv.includes("--production")) {
baseUrl = "https://www.yoursite.com/"; // 本番サイトのURLを入れる
}
const allPaths = [""];
const targets = [];
process.argv.slice(4).forEach((arg) => {
switch (arg) {
case "--production":
baseUrl = "https://www.yoursite.com/"; // 本番サイトのURLを入れる
break;
default:
if (!allPaths.includes(arg)) {
throw new Error(`Unknown option or path: ${arg}`);
}
targets.push(arg);
}
});
module.exports = {
id: "backstop_default",
viewports: [
{
label: "sp",
width: 375,
height: 667,
},
{
label: "pc",
width: 1280,
height: 720,
},
],
scenarios: (targets.length ? targets : allPaths).map((path) => ({
label: path || "Top",
url: `${baseUrl}/${path}`,
})),
paths: {
bitmaps_reference: "backstop_data/bitmaps_reference",
bitmaps_test: "backstop_data/bitmaps_test",
engine_scripts: "backstop_data/engine_scripts",
html_report: "backstop_data/html_report",
},
report: ["browser"],
engine: "puppeteer",
asyncCaptureLimit: 5, // 画面取り込みの並列処理を制限
asyncCompareLimit: 50, // 画面比較の並列処理を制限
debug: false, // デバック結果をターミナルに出力
debugWindow: false, // 実行中の処理をChromeに表示
fileNameTemplate: "{scenarioLabel}_{viewportLabel}", // ファイル名を指定
onReadyScript: "puppet/onReady.js",
};
ローカルサーバーが必要です。Gulp、Nuxtなどなどを用意してください。
下記の部分をご自身のローカルサーバーのURLに変更します。
let baseUrl = "http://localhost:8080/"; // ローカルサーバーのURL
if (process.argv.includes("--production")) {
baseUrl = "https://www.yoursite.com";
}
// 省略
次に、backstop_dataを作成、その中にengine_scripts > puppet > onReady.jsを作成します。
こんな感じのディレクトリになります。
(混乱しないように関係ないファイルは隠しました)
onReady.js
を編集します。
module.exports = async (page, scenario) => {
console.log("SCENARIO > " + scenario.label);
await page.evaluate(() => {
// loading attributeを無効に
document.querySelectorAll('[loading="lazy"]').forEach((el) => {
el.loading = "eager";
});
// スクロールアニメーションを無効に
document.querySelectorAll(".fade-in").forEach((el) => {
el.style.opacity = 1;
});
if (window.jQuery) {
window.jQuery.fx.off = true; // jQueryが提供する全ての動作からアニメーション処理を外す。
}
});
await page.waitForTimeout(1000);
};
これで設定ができました。
最後にnpm scriptsで実行できるようにします。package.json
を編集します。
{
"scripts": {
"visual-reference": "backstop reference --config=backstop.config.js",
"visual-test": "backstop test --config=backstop.config.js"
},
}
これで下記のように実行できます。
## REFERENCEを作成する
npm run visual-reference
## TESTを実行する
npm run visual-test
これでテストが実行されます。
実行結果はbackstop_data > html_reportの中に生成されます。
npm run visual-reference
でREFERENCEを作成後、わざと変更を加えて、npm run visual-test
を実行すれば、差分を表示してくれます。
サーバーアップされている本番サイトとの差分比較をする場合
既にサイトアップされている本番サイトとの差分比較をしたい場合があるはずです。
その場合は、まずbackstop.cofig.js
で本番サイトのURLを指定します。
let baseUrl = "http://localhost:8080/";
if (process.argv.includes("--production")) {
baseUrl = "https://www.yoursite.com/";
}
const allPaths = [""];
const targets = [];
process.argv.slice(4).forEach((arg) => {
switch (arg) {
case "--production":
baseUrl = "https://www.yoursite.com/";
break;
default:
if (!allPaths.includes(arg)) {
throw new Error(`Unknown option or path: ${arg}`);
}
targets.push(arg);
}
});
あとは、--production
を付けて実行すればOKです。
## 本番サイトでREFERENCEを作成
npm run visual-reference --production
設定内容の解説(抜粋)
backstop.cofig.js
および、onReady.js
に書いた内容で、特に重要そうなところを抜粋して解説します。
backstopjsのオプションはGithubに記載があるのでそちらを参照しながら見ていただくのがいいです。
backstop.config.jsの解説
debug: false, // デバック結果をターミナルに出力
debugWindow: false, // 実行中の処理をChromeに表示
この2つはfalseでいいと思います。特にdebugWindow
はchromeを開いてしまうので。
クリックやホバーをしてから、スクリーンショットを撮ってもらうこともできるので、動作確認したい方はture
にしてください。
onReadyScript: "puppet/onReady.js",
スクリーンショットを撮る前に実行したいスクリプトを書きます。
onReady.js
の詳しい解説は次の章からです!↓↓
テストでの不都合を解消する
テストを実行していると、なにかと不都合が出てくるはずです。
例えば、
- スクロールアニメーションの部分が真っ白
- lazyloadした画像が読み込まれたり、読み込まれなかったり
- 動的なコンテンツがある
などなどです。
backstopjsも完璧ではないので、ある程度の折り合いは必要と思うのですが、なるべくDIFFが出ないようにしたいです。
backstopjsのオプションに用意されていないものは自分でscriptを書きましょう!
// loading attributeを無効に
document.querySelectorAll('[loading="lazy"]').forEach((el) => {
el.loading = "eager";
});
これでimg
タグなどのloading="lazy"
を無効にします。
// スクロールアニメーションを無効に
document.querySelectorAll(".fade-in").forEach((el) => {
el.style.opacity = 1;
});
これはスクロールでふわっと出現するアニメーションがある想定です。
上記の例では.fade-in
にopacity: 0
が書いてあるので、opacity: 1
に上書きしています。
if (window.jQuery) {
window.jQuery.fx.off = true; // jQueryが提供する全ての動作からアニメーション処理を外す。
}
});
window.jQuer.fx.off
を設定すると、jQueryが提供する全ての動作からアニメーション処理を外すことができるそうです。
jQuery.fx.off
jQuery日本語レファレンス:http://semooh.jp/jquery/api/effects/jQuery.fx.off/
動的なコンテンツが毎回DIFFになって面倒だ、という場合はhideSelectors
を使うといいです。
例えばslickスライダーが毎回DIFFになってストレスだ、という場合
scenarios: (targets.length ? targets : paths).map((path) => ({
label: path,
url: `${baseUrl}/${path}`,
hideSelectors: [".slick-list"],
})),
hideSelectors
を使うと、該当部分はvisibility: hidden
になってくれます。
これで領域は確保しつつ隠してくれるので、DIFFは出なくなります。
ただし該当箇所は真っ白になってしまうので、slick部分に変更があっても検知してくれません。使うかどうかはお任せします。
おまけ:ローカルサーバーにbrowser-syncを使うなら
ローカルサーバーにbrowser-syncを使っているなら、"BrowserSync Connected"
のメッセージは消しておきたいですね。これが原因で全ページエラーになってしまうので…。
const browserSync = require("browser-sync");
const buildServer = (done) => {
browserSync.init({
port: 8080,
files: ["**/*"],
server: { baseDir: "./" },
notify: false, // 追加
});
done();
};
notify: false
を追加すれば、"BrowserSync Connected"
は出なくなります。
まとめ
- ビジュアルリグレッションテスト・backstopjsを使えば変更前・後の差分が一目瞭然になる。
- 設定を追加することである程度カスタマイズ可能
- 完璧な動作は難しいことがあるので、設定追加で対応できないこともある。ある程度は折り合いをつける。