BackstopJSでサクッとビジュアルリグレッションテストしてみた

# BackstopJS
https://github.com/garris/BackstopJS

## 背景
最近、サイト全体で読み込んでいる不要なjsやcssを削除してサイト全体に表示崩れがないか確認する機会が続いたのでビジュアルリグレッションテストをローカルで実行してみました

### ビジュアルリグレッションテストとは
ページ全体のスナップショットを撮影して変更前と変更後の画像の差分を検出するテスト
これによって変更後のページで画像崩れなどピクセル単位のズレを検出します

## ツール選定
reg-suit をよく耳にしますが、E2Eテストをがっつりしたいのではなくサクッとビジュアルリグレッションテストだけをしたかったので[BackstopJS](https://github.com/garris/BackstopJS)を使用することにしました

## 導入
```
$ npm install backstopjs
$ npx backstop init
```

initするとbackstop.jsonという設定ファイルの雛形とbackstop_dataディレクトリが作成されます

```
.
├── node_modules
├── package-lock.json
├── package.json
├── backstop.json
└── backstop_data
    └── engine_scripts
        ├── cookies.json
        ├── imageStub.jpg
        ├── playwright
        │   └── ..省略
        └── puppet
            ├── clickAndHoverHelper.js
            ├── ignoreCSP.js
            ├── interceptImages.js
            ├── loadCookies.js
            ├── onBefore.js
            ├── onReady.js
            └── overrideCSS.js
```

## 設定ファイル
#### viewports
SPやPCなど複数のviewportを設定できます
label名毎にスナップショットを生成します

```json
  "viewports": [
    {
      "label": "SP",
      "width": 375,
      "height": 812
    },
    {
      "label": "PC",
      "width": 1280,
      "height": 800
    }
  ]
```

#### scenarios
スナップショットを撮るページを指定します

```json
  "scenarios": [
    {
      "label": "top",
      "url": "https://dev.lvnmatch.jp/"
    },
    {
      "label": "sell",
      "url": "https://dev.lvnmatch.jp/sell/"
    }
  ],
```

## 早速実行
```shell
$ npx backstop reference
//特定のラベルのみ実行
$ npx backstop reference --filter=top
```

上記コマンドを実行するとbackstop_dataディレクトリ配下にbitmaps_referenceディレクトリが作成されてスナップショットが格納されます

```shell
bitmaps_reference
├── backstop_default_sell_0_document_0_SP.png
├── backstop_default_sell_0_document_1_PC.png
├── backstop_default_top_0_document_0_SP.png
└── backstop_default_top_0_document_1_PC.png
```

試しに1ページだけ見出しの文言変更を行います
変更後に以下コマンドにて検証を行います

```
$ npx backstop test
//特定のラベルのみ実行
$ npx backstop test --filter=top
```

検証が終わると結果をブラウザで確認できます
![スクリーンショット 2022-01-14 16.16.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2348159/798c8f85-0f18-30ce-0b61-55a8c6a2ace6.png)

また、diffも以下のように確認が可能です

![mov-backstop.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2348159/8558901e-5328-efc4-d21e-a3c46d99a15d.gif)


## その他scenariosの便利なオプション
#### delay
キャプチャーを撮る前にスリープをかけることができます

#### removeSelectors / hideSelectors
広告などアクセスする度に画像が変わる要素は、removeSelectorsで削除するかhideSelectorsで非表示にすることができます

### hoverSelectors / clickSelectors
ホバー時やクリック後にキャプチャーできます

### selectors
ページ全体ではなくページ内の一部のみキャプチャーできます

### scrollToSelector
無限スクロールや画像遅延読み込みをしている場合は、scrollToSelectorで特定の要素までスクロールできます
しかし、スクロールスピードが早いのでsmoothスクロールがしたい場合は、scrollToSelectorを使わずにonReadyScriptにsmoothスクロールの記述することも可能です

```javascript
module.exports = async (page, scenario, vp) => {
  console.log('SCENARIO > ' + scenario.label);
  await require('./clickAndHoverHelper')(page, scenario);
  // add more ready handlers here...

  const selector = "footer";
  await page.evaluate(selector => {
    document.querySelector(selector).scrollIntoView({behavior: "smooth", block: "start"});
  }, selector);
};
```

### misMatchThreshold
チャプチャー比較の感度を指定できます
画面内の何%ズレたらテストを失敗にするのか指定できます