以前書いたやつの続き。

StoryBookでビジュアルリグレッションテストしたいときの構成。

前のはURL単位にテストファイルを作っていたのですが、一括で全URL対象にした方が楽。

storyshotsを使うかscreencapを使うか。

両方やってみてscreencapを使うようにしたのでその辺の話。


StoryShots

GitHubのReadmeを見れば導入はそのままでOK。

で、実際に使う時に設定したこと。

除外したいstoryの指定

実際のコンポーネントを考えると、ずっとアニメーションしてるものの場合、同じスクリーンショットとるとか不可能なので、テストを外したい。Readmeにも書いてあるけどその場合は↓。

import initStoryshots from '@storybook/addon-storyshots';

initStoryshots({
  storyKindRegex:/^((?!.*?DontTest).)*$/
});
スナップショット取る前にちょっと待機させたい場合

開いた瞬間アニメーションしてるやつが落ち着くまで待機させたい場合。

import initStoryshots from '@storybook/addon-storyshots'
import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer'


const beforeScreenshot = (page, { context: { kind, story }, url }) => {
  return new Promise(
    (resolve) =>
      setTimeout(() => {
        resolve()
      }, 1000) // waitのミリ秒
  )
}

initStoryshots({
  suite: 'Image',
  storyKindRegex: /^(?!.*?nvis).*$/,
  test: imageSnapshot({
    storybookUrl: 'http://localhost:9009',
    beforeScreenshot,
  }),
})

で、見逃してるだけのような気もするのですが、story毎に待機時間を決められないように思うので、待機時間つけちゃうと全部遅くなっちゃう。。。

あと、除外する名前の正規表現ってURLのところになってると思うのですが、どうにも上手くいかない。名称の見方がなんか違うっぽい。どう書けばいいんだろうか。正規表現的に色々試してみたのですが、どうもやりたい事が出来ない。


StoryCap

GitHubのリポジトリ。

で、StoryShotsの代わりにこれを使うようにした。スターの数だと圧倒的にStoryShotsなんだけど、

こっちの方が望む結果にすぐたどり着いたので。導入もすごく楽。

動き的にHTMLの変化を監視して、画面のアニメーションが落ち着くのを待ってからスクリーンショットを撮ってるぽい。賢い。

もちろんずっとアニメーションしてるコンポーネントは↑のWaitのタイムアウトでエラーになるけど。

で、そういうコンポーネントを除外したい場合は↓の感じで実行コマンドに渡す。

storycap http://localhost:9009 -e "**/DontTest/**"

ルールとしてstorybookに表示されるサブディレクトリの階層で、DontTestとか階層を作って、テスト除外したい奴はそこに配置する感じ。ちょっとカッコ悪いけど、urlをminimatchしてるのかな?

ディレクトリ階層で切り分けないとうまくいかなかった。

ただし、StoryCapはスクリーンショットを撮るだけでテストまでは入ってないので、

そこは自分で書いておかないとダメ。ロードマップ見ると将来的には対応してくれるようす。

テストは↓の感じで書けばよいのではないかと。

const fs = require('fs')

function getFiles(dir, files_) {
  files_ = files_ || []
  const files = fs.readdirSync(dir)
  for (const i in files) {
    const name = dir + '/' + files[i]
    if (fs.statSync(name).isDirectory()) {
      getFiles(name, files_)
    } else {
      files_.push(name)
    }
  }
  return files_
}

const files = getFiles('storycapで画像が出来るディレクトリ')

describe.each(files)('ビジュアルテスト', (filepath) => {
  test(`file: ${filepath}`, () => {
    const data = fs.readFileSync(filepath)
    expect(data).toMatchImageSnapshot({
      customSnapshotsDir: 'テスト用のスナップショット周りが出来るディレクトリ',
      failureThreshold: 0.10,
      failureThresholdType: 'percent',
    })
  })
})