Closed26

storybookのVRTとカバレッジ

inomotoinomoto

ふと見つけた下記記事のが良さそうだったので試す。
https://zenn.dev/aiji42/articles/6656072a954a9b

storybookのVRTは色々やり方ありそうだし、今ならそこそこ簡単にもなってきていると思うが、これだと完全に入れるだけでいけそうなのが良さげに見える。
またテストレポートを自前で作らずにgithubにまかせてしまうという発想は好みにあう(正直その発想はなかったけど)。構築が楽という観点に加え、使い勝手が一定レベルで担保されるという観点から。

また、ついでに前から気になっていたstorybook test-runnerでの描画テストおよびカバレッジ計測も試したい。

inomotoinomoto

リポジトリを生やす。
https://github.com/cumet04/sbox_storybook-test

storybook用のサンドボックスなかったっけと思ったが、個別記事の専用のしかなかった。

inomotoinomoto
$ npx storybook@latest init
✔ Do you want to manually choose a Storybook project type to install? … yes
✔ Please choose a project type from the following list: › react
✔
We were not able to detect the right builder for your project. Please select one: › Vite

最初の以外はデフォルト選択。

inomotoinomoto

lost-pixelを入れてみる。
https://github.com/lost-pixel/lost-pixel

inomotoinomoto

READMEにサンプル設定はあるのだが、パッケージ名がわからないw

念のため適当なサンプルプロジェクトの設定を見に行くと、lost-pixelパッケージを入れれば動きそう(他に必須っぽい雰囲気のものはない)なこと、実行コマンドがlost-pixellost-pixel updateであることがわかる。
https://github.com/lost-pixel/lost-pixel-examples/

というわけでnpm install -D lost-pixelしつつREADMEにある設定をそのまま貼り付ける。

lostpixel.config.ts
import { CustomProjectConfig } from 'lost-pixel';

export const config: CustomProjectConfig = {
  storybookShots: {
    storybookUrl: './storybook-static',
  },
   // OSS mode 
  generateOnly: true,
  failOnDifference: true
  
  // Lost Pixel Platform (to use in Platform mode, comment out the OSS mode and uncomment this part )
  // lostPixelProjectId: "xxxx",
  // process.env.LOST_PIXEL_API_KEY,
};
inomotoinomoto

storybookのURLとしてディレクトリがそのまま指定されているあたり、storybookとは別のnpmパッケージ?プロジェクト?にしても動きそうな感じがするな。depsが混迷を極めるプロジェクトに入れるときにはよさそう。

また、ディレクトリが指定されているということは事前にbuild-storybookしろということだと思うので、やっておく。完了したら、lost-pixelを実行してみる。

$ npx lost-pixel
...
❌ browserType.launch: Executable doesn't exist at /home/inomoto/.cache/ms-playwright/chromium-1076/chrome-linux/chrome
╔═════════════════════════════════════════════════════════════════════════╗
║ Looks like Playwright Test or Playwright was just installed or updated. ║
║ Please run the following command to download new browsers:              ║
║                                                                         ║
║     npx playwright install                                              ║
║                                                                         ║
║ <3 Playwright Team                                                      ║
╚═════════════════════════════════════════════════════════════════════════╝

はい。

$ npx playwright install
Downloading Chromium 120.0.6099.28 (playwright build v1091) from https://playwright.azureedge.net/builds/chromium/1091/chromium-linux.zip
153.1 Mb [====================] 100% 0.0s
Chromium 120.0.6099.28 (playwright build v1091) downloaded to /home/inomoto/.cache/ms-playwright/chromium-1091
Downloading FFMPEG playwright build v1009 from https://playwright.azureedge.net/builds/ffmpeg/1009/ffmpeg-linux.zip
2.6 Mb [====================] 100% 0.0s
FFMPEG playwright build v1009 downloaded to /home/inomoto/.cache/ms-playwright/ffmpeg-1009
Downloading Firefox 119.0 (playwright build v1429) from https://playwright.azureedge.net/builds/firefox/1429/firefox-ubuntu-22.04.zip
80.9 Mb [====================] 100% 0.0s
Firefox 119.0 (playwright build v1429) downloaded to /home/inomoto/.cache/ms-playwright/firefox-1429
Downloading Webkit 17.4 (playwright build v1944) from https://playwright.azureedge.net/builds/webkit/1944/webkit-ubuntu-22.04.zip
83.7 Mb [====================] 100% 0.0s
Webkit 17.4 (playwright build v1944) downloaded to /home/inomoto/.cache/ms-playwright/webkit-1944

$ npx lost-pixel
...
❌ browserType.launch: Executable doesn't exist at /home/inomoto/.cache/ms-playwright/chromium-1076/chrome-linux/chrome
╔═════════════════════════════════════════════════════════════════════════╗
║ Looks like Playwright Test or Playwright was just installed or updated. ║
║ Please run the following command to download new browsers:              ║
║                                                                         ║
║     npx playwright install                                              ║
║                                                                         ║
║ <3 Playwright Team                                                      ║
╚═════════════════════════════════════════════════════════════════════════╝

ぐぬぬ。

inomotoinomoto

よくみるとchromiumのバージョンがズレている。そいやnpxしたときにplaywrightのインストールを要求されていたな。
プロジェクトのpackage.jsonには明示的にplaywrightが入っているわけではないので、グローバルインストールしたんだろうな。

明示的にnpm install playwrightしてもよいが、一旦ここはバージョン指定で合わせる。

$ npm ls --all | grep playwright
│ ├── playwright-core@1.37.0

$ npx playwright@1.37.0 install
...

$ npx lost-pixel
...
❌ browserType.launch:
╔══════════════════════════════════════════════════════╗
║ Host system is missing dependencies to run browsers. ║
║ Please install them with the following command:      ║
║                                                      ║
║     sudo npx playwright install-deps                 ║
║                                                      ║
║ Alternatively, use apt:                              ║
║     sudo apt-get install libnss3\                    ║
║         libnspr4\                                    ║
║         libatk1.0-0\                                 ║
║         libatk-bridge2.0-0\                          ║
║         libxkbcommon0\                               ║
║         libatspi2.0-0\                               ║
║         libxdamage1\                                 ║
║         libgbm1\                                     ║
║         libasound2                                   ║
║                                                      ║
║ <3 Playwright Team                                   ║
╚══════════════════════════════════════════════════════╝

よし進捗した。install-depsのやつは前にも見た。

依存を入れた後、改めてnpx lost-pixelすると、なにやら完了したっぽい。
ログを見ると、./.lost-pixel/にファイルを作っているっぽい。

$ tree .lostpixel/
.lostpixel/
├── baseline
├── current
│   ├── example-button--large.png
│   ├── example-button--primary.png
│   ├── example-button--secondary.png
│   ├── example-button--small.png
│   ├── example-header--logged-in.png
│   ├── example-header--logged-out.png
│   ├── example-page--logged-in.png
│   └── example-page--logged-out.png
└── difference

3 directories, 8 files

なんかできてる。よさそう。

inomotoinomoto

ちなみにこの時点ではコマンド(テスト)は失敗状態になる。比較状態がないのでそれはそう。
npx lost-pixel updateするとbaselineディレクトリにもファイルが出来上がる。その状態でnpx lost-pixelするとコマンドが成功で終わる。

ここから適当にstoryを変更し、build-storybookしてからnpx lost-pixelしてみる。
差分はこれ

diff --git a/stories/Button.stories.js b/stories/Button.stories.js
index 3a3f67e..4c08718 100644
--- a/stories/Button.stories.js
+++ b/stories/Button.stories.js
@@ -20,7 +20,7 @@ export default {
 export const Primary = {
   args: {
     primary: true,
-    label: 'Button',
+    label: 'Button!!!',
   },
 };

想定通りにnpx lost-pixelは失敗ステータスになる。また差分ファイル(一番下)が生成される。

$ tree .lostpixel/
.lostpixel/
├── baseline
│   ├── example-button--large.png
│   ├── example-button--primary.png
│   ├── example-button--secondary.png
│   ├── example-button--small.png
│   ├── example-header--logged-in.png
│   ├── example-header--logged-out.png
│   ├── example-page--logged-in.png
│   └── example-page--logged-out.png
├── current
│   ├── example-button--large.png
│   ├── example-button--primary.png
│   ├── example-button--secondary.png
│   ├── example-button--small.png
│   ├── example-header--logged-in.png
│   ├── example-header--logged-out.png
│   ├── example-page--logged-in.png
│   └── example-page--logged-out.png
└── difference
    └── example-button--primary.png

で、差分画像を見てみる。

だいぶなんもわからんなw

まぁ実際の差分はbaselineをPullRequestで見るだろうから大丈夫かな。

inomotoinomoto

続いてカバレッジについて。公式にドキュメントがあるのでざっとみる。
https://storybook.js.org/docs/writing-tests/test-coverage

@storybook/addon-coverageをいれて、npm run test-storybook -- --coverageしろとある。後者のためにtest-runner自体も入れる必要がありそうだ。
で、確かtest-runnerはデフォルトではstoryの正常描画をテストする(のでテストコードは書かなくて良い)はずなので、本当に入れるだけで良さそうに見える。

inomotoinomoto

test-runnerの方はこっち。
https://storybook.js.org/docs/writing-tests/test-runner

$ npm install @storybook/test-runner --save-dev
...

$ npm run storybook
...

(別タブで)$ npx test-storybook
 PASS   browser: chromium  stories/Header.stories.js
 PASS   browser: chromium  stories/Button.stories.js
 PASS   browser: chromium  stories/Page.stories.js

Test Suites: 3 passed, 3 total
Tests:       8 passed, 8 total
Snapshots:   0 total
Time:        2.107 s
Ran all test suites.

良さそう。本当に入れるだけである。

inomotoinomoto

addon-coverageをいれる。

$ npm install @storybook/addon-coverage --save-dev
diff --git a/.storybook/main.js b/.storybook/main.js
index df0a7aa..b662c32 100644
--- a/.storybook/main.js
+++ b/.storybook/main.js
@@ -9,6 +9,7 @@ const config = {
     "@storybook/addon-essentials",
     "@storybook/addon-onboarding",
     "@storybook/addon-interactions",
+    '@storybook/addon-coverage'
   ],
   framework: {
     name: "@storybook/react-vite",

デフォルトでなんかいろいろaddonが入っているが、storybook initで入ったやつなので、coverageで必須なのかは知らない。多分いらないと思うけど。

この状態でnpm run storybookを起動したのち、

$ npx test-storybook --coverage
 PASS   browser: chromium  stories/Header.stories.js
 PASS   browser: chromium  stories/Button.stories.js
 PASS   browser: chromium  stories/Page.stories.js

Test Suites: 3 passed, 3 total
Tests:       8 passed, 8 total
Snapshots:   0 total
Time:        1.682 s, estimated 2 s
Ran all test suites.
Coverage file (2 bytes) written to .nyc_output/coverage.json

できてそう。なんかファイルが生えたらしい。

inomotoinomoto

ん?ところでドキュメントによると上記コマンドの出力に表っぽいやつが出るらしいが、出てないな?オプションとかあるのかもなので、あとで探ってみよう

inomotoinomoto

更にいうと、生成されたのは.nyc_output/coverage.jsonではなくcoverage/storybook/coverage-storybook.jsonっぽいのだが?

一旦気にせず、ドキュメントのそれっぽいセクションより
https://storybook.js.org/docs/writing-tests/test-coverage#what-about-other-coverage-reporting-tools

それっぽいコマンドを実行してみる。

$ npx nyc report --reporter=lcov -t coverage/storybook --report-dir coverage/storybook

何も出力されないが、ファイルが増えている。

$ tree coverage/
coverage/
└── storybook
    ├── coverage-storybook.json
    ├── lcov-report
    │   ├── base.css
    │   ├── block-navigation.js
    │   ├── favicon.png
    │   ├── index.html
    │   ├── prettify.css
    │   ├── prettify.js
    │   ├── sort-arrow-sprite.png
    │   └── sorter.js
    └── lcov.info

2 directories, 10 files

みてみよう。

$ cd coverage/storybook/lcov-report/
$ python3 -m http.server

ん?何もないな?

inomotoinomoto

そもそもcoverage/storybook/coverage-storybook.json{}だったし、出るはずのCLIのカバレッジ出力が無いことから、なんか間違っているっぽい。ちょっと色々すっ飛ばしすぎたようだ。
カバレッジの動作などちゃんと追ってみよう。

inomotoinomoto

カバレッジがまともに認識しないのは、なんらかバグとしてよくレポートされているようだ。
https://github.com/storybookjs/addon-coverage/issues/15
https://github.com/storybookjs/addon-coverage/issues/25
https://github.com/storybookjs/addon-coverage/pull/27#issuecomment-1839973982
https://github.com/storybookjs/addon-coverage/issues/30

同リポジトリのexampleが(いちおう)動くことを考えると、かなりギリギリなバランスの上でしか動作しないのだろうな。

inomotoinomoto

カバレッジのデバッグ試行錯誤

inomotoinomoto

設定で明示的にstoriesのファイルパスを指定してみる。サンプル設定が"stories/**になっており、ルートパスが見れてないんじゃないかという仮説。

diff --git a/.storybook/main.js b/.storybook/main.js
index 0888fd0..61c19ff 100644
--- a/.storybook/main.js
+++ b/.storybook/main.js
@@ -9,7 +9,17 @@ const config = {
     "@storybook/addon-essentials",
     "@storybook/addon-onboarding",
     "@storybook/addon-interactions",
-    "@storybook/addon-coverage"
+    {
+      name: "@storybook/addon-coverage",
+      options: {
+        istanbul: {
+          include: [
+            "../stories/**/*.mdx",
+            "../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)"
+          ],
+        },
+      },
+    },
   ],
   framework: {
     name: "@storybook/react-vite",

すると、全ファイルに対し下記のようなエラーになる。

  ● Example/Page › LoggedIn › play-test

    [Test runner] An error occurred when evaluating code coverage:
      The code in this story is not instrumented, which means the coverage setup is likely not correct.
      More info: https://github.com/storybookjs/test-runner#setting-up-code-coverage
inomotoinomoto

テスト: react-webpackでやる

storybook initする段階でviteではなくwebpackにしてやってみる。

すると、@storybook/addon-coverageを入れて.storybook/main.jsに入れた時点で`npm run storybookが動かなくなる。いやいやいやw

以下のような出力が全ファイルについて出ている。

ERROR in ./stories/Page.jsx
Module build failed (from ./node_modules/@storybook/addon-coverage/dist/cjs/loader/webpack5-istanbul-loader.js):
TypeError: /home/inomoto/workdir/webpack/stories/Page.jsx: Cannot read properties of undefined (reading 'map')
    at new TraceMap (/home/inomoto/workdir/webpack/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.umd.js:364:44)
    at new SourceMap (/home/inomoto/workdir/webpack/node_modules/@babel/generator/lib/source-map.js:25:24)
    at generate (/home/inomoto/workdir/webpack/node_modules/@babel/generator/lib/index.js:84:33)
    at generateCode (/home/inomoto/workdir/webpack/node_modules/@babel/core/lib/transformation/file/generate.js:47:39)
    at run (/home/inomoto/workdir/webpack/node_modules/@babel/core/lib/transformation/index.js:39:33)
    at run.next (<anonymous>)
    at transform (/home/inomoto/workdir/webpack/node_modules/@babel/core/lib/transform.js:22:33)
    at transform.next (<anonymous>)
    at evaluateSync (/home/inomoto/workdir/webpack/node_modules/gensync/index.js:251:28)
    at sync (/home/inomoto/workdir/webpack/node_modules/gensync/index.js:89:14)
    at stopHiding - secret - don't use this - v1 (/home/inomoto/workdir/webpack/node_modules/@babel/core/lib/errors/rewrite-stack-trace.js:47:12)
    at transformSync (/home/inomoto/workdir/webpack/node_modules/@babel/core/lib/transform.js:42:76)
    at Instrumenter.instrumentSync (/home/inomoto/workdir/webpack/node_modules/@storybook/addon-coverage/node_modules/istanbul-lib-instrument/src/instrumenter.js:102:25)
    at Object._default (/home/inomoto/workdir/webpack/node_modules/@storybook/addon-coverage/dist/cjs/loader/webpack5-istanbul-loader.js:64:29)
 @ ./stories/Page.stories.js 120:0-30 123:13-17
 @ ./stories/ lazy ^\.\/.*$ include: (?:\/stories(?:\/(?%21\.)(?:(?:(?%21(?:^%7C\/)\.).)*?)\/%7C\/%7C$)(?%21\.)(?=.)[^/]*?\.stories\.(js%7Cjsx%7Cmjs%7Cts%7Ctsx))$ chunkName: [request] namespace object ./Page.stories.js ./Page.stories
 @ ./storybook-stories.js 23:11-27:5
 @ ./storybook-config-entry.js 6:0-50 25:21-29 28:2-31:4 28:58-31:3 30:31-39

preview compiled with 3 errors
=> Failed to build the preview
99% end closing watch compilationWARN Force closed preview build
SB_BUILDER-WEBPACK5_0003 (WebpackCompilationError): There were problems when compiling your code with Webpack.
Run Storybook with --debug-webpack for more information.
    at starter (./node_modules/@storybook/builder-webpack5/dist/index.js:1:9660)
    at starter.next (<anonymous>)
    at Module.start (./node_modules/@storybook/builder-webpack5/dist/index.js:1:11692)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

一旦スルーするが、実プロジェクトでいれるときはwebpackだろうしなぁ...先が思いやられるな...storybookこんなんばっかりだな...

inomotoinomoto

addon-coverageのリポジトリにexampleがあるので試してみる。viteとwebpackがある。
https://github.com/storybookjs/addon-coverage/tree/main/examples

exampleにはバージョン類の指定が無い(いるだろそれ...)ので、ひとまずnodeは20.10.0, yarn.lockがあるのでyarnでいく。yarn.lockにlockfile v1とあるのでまぁ1系でいいでしょ、ということでyarnは1系で。

またpackage.jsonにlink:../..という見慣れないのがあるので、リポジトリトップで事前にyarnしておく。

vite

リポジトリトップのREADMEの一番下に一応使い方がある。yarnしてyarn storybookしてyarn test-storybook --coverageせよとのこと。

とりあえずyarn storybookしてみる。

...エラー吐いてますね...一応起動してるけど...

一応起動はしてるので、yarn test-storybook --coverageしてみる。

見覚えのあるエラーを吐いてますね。exampleがas-isで動かないのは草を禁じえない。

ところで、謎パッチを当てていないがCLI上のカバレッジ出力が動いているな。

inomotoinomoto

viteのやつ、あとからよく見るとyarn.lockが更新されていた。え、yarnってそんなカジュアルにlockファイル壊すんだっけ?

というわけで一旦クリーンにしてからyarn --frozen-lockfileしてやり直したが、結果は同じだった。

inomotoinomoto

ふと気づいて、package.jsonのaddonのバージョンをローカルではなくリモートにしてみたところ、warnもエラーもなく動くようになった。

diff --git a/examples/vite/package.json b/examples/vite/package.json
index 7d44f83..752a482 100644
--- a/examples/vite/package.json
+++ b/examples/vite/package.json
@@ -20,7 +20,7 @@
   "devDependencies": {
     "@storybook/addon-essentials": "7.4.2",
     "@storybook/addon-interactions": "7.4.2",
-    "@storybook/addon-coverage": "link:../..",
+    "@storybook/addon-coverage": "1.0.0",
     "@storybook/react": "7.4.2",
     "@storybook/react-vite": "7.4.2",
     "@storybook/test-runner": "^0.13.0",

なおwebpack版も同様であった。addonがローカルだとvite同様に動かない、1.0.0に固定すると同じように動く。

inomotoinomoto

なお相変わらず.nyc_output/coverage.jsonは出力されておらず、coverage/storybook/coverage-storybook.jsonにそれっぽい出力が得られている。

ここでnpx nyc report --reporter=lcov -t coverage/storybook --report-dir coverage/storybookした上でファイルを見ると、良い感じにレポートされていそうだった。

inomotoinomoto

lost-pixelはとても良く動いたのでOK

test-runnerのカバレッジは動く環境を探り当てるのが難しすぎる上、ここのクリーン環境で再現したところで実環境への導入には役に立たない(全く別のyakが発生する)と思われるので、試すのはやめておく。

数年後、test-runnerが安定して動くようになったら起こしてください。

このスクラップは2024/01/02にクローズされました