storybookのVRTとカバレッジ
ふと見つけた下記記事のが良さそうだったので試す。
storybookのVRTは色々やり方ありそうだし、今ならそこそこ簡単にもなってきていると思うが、これだと完全に入れるだけでいけそうなのが良さげに見える。
またテストレポートを自前で作らずにgithubにまかせてしまうという発想は好みにあう(正直その発想はなかったけど)。構築が楽という観点に加え、使い勝手が一定レベルで担保されるという観点から。
また、ついでに前から気になっていたstorybook test-runnerでの描画テストおよびカバレッジ計測も試したい。
リポジトリを生やす。
storybook用のサンドボックスなかったっけと思ったが、個別記事の専用のしかなかった。
$ 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
最初の以外はデフォルト選択。
latestを指定したが、7.6.6になったようだ
lost-pixelを入れてみる。
READMEにサンプル設定はあるのだが、パッケージ名がわからないw
念のため適当なサンプルプロジェクトの設定を見に行くと、lost-pixel
パッケージを入れれば動きそう(他に必須っぽい雰囲気のものはない)なこと、実行コマンドがlost-pixel
やlost-pixel update
であることがわかる。
というわけでnpm install -D lost-pixel
しつつREADMEにある設定をそのまま貼り付ける。
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,
};
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 ║
╚═════════════════════════════════════════════════════════════════════════╝
ぐぬぬ。
よくみると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
なんかできてる。よさそう。
ちなみにこの時点ではコマンド(テスト)は失敗状態になる。比較状態がないのでそれはそう。
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で見るだろうから大丈夫かな。
続いてカバレッジについて。公式にドキュメントがあるのでざっとみる。
@storybook/addon-coverage
をいれて、npm run test-storybook -- --coverage
しろとある。後者のためにtest-runner自体も入れる必要がありそうだ。
で、確かtest-runnerはデフォルトではstoryの正常描画をテストする(のでテストコードは書かなくて良い)はずなので、本当に入れるだけで良さそうに見える。
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.
良さそう。本当に入れるだけである。
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
できてそう。なんかファイルが生えたらしい。
ん?ところでドキュメントによると上記コマンドの出力に表っぽいやつが出るらしいが、出てないな?オプションとかあるのかもなので、あとで探ってみよう
更にいうと、生成されたのは.nyc_output/coverage.json
ではなくcoverage/storybook/coverage-storybook.json
っぽいのだが?
一旦気にせず、ドキュメントのそれっぽいセクションより
それっぽいコマンドを実行してみる。
$ 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
ん?何もないな?
そもそもcoverage/storybook/coverage-storybook.json
は{}
だったし、出るはずのCLIのカバレッジ出力が無いことから、なんか間違っているっぽい。ちょっと色々すっ飛ばしすぎたようだ。
カバレッジの動作などちゃんと追ってみよう。
カバレッジがまともに認識しないのは、なんらかバグとしてよくレポートされているようだ。
同リポジトリのexampleが(いちおう)動くことを考えると、かなりギリギリなバランスの上でしか動作しないのだろうな。
カバレッジのデバッグ試行錯誤
コマンド出力でカバレッジが出ないのはバグっぽい。
ただ依然として「テスト」されない
設定で明示的に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
テスト: 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こんなんばっかりだな...
addon-coverageのリポジトリにexampleがあるので試してみる。viteとwebpackがある。
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上のカバレッジ出力が動いているな。
viteのやつ、あとからよく見るとyarn.lockが更新されていた。え、yarnってそんなカジュアルにlockファイル壊すんだっけ?
というわけで一旦クリーンにしてからyarn --frozen-lockfile
してやり直したが、結果は同じだった。
ふと気づいて、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に固定すると同じように動く。
なお相変わらず.nyc_output/coverage.json
は出力されておらず、coverage/storybook/coverage-storybook.json
にそれっぽい出力が得られている。
ここでnpx nyc report --reporter=lcov -t coverage/storybook --report-dir coverage/storybook
した上でファイルを見ると、良い感じにレポートされていそうだった。
ほしいのはイケてる機能ではなくstoryによるカバレッジでしかないので、storyshotsでjestのカバレッジ取るかーと思って確認すると、storyshotsがdeprecatedになっていた。それはそうかという気持ち。
lost-pixelはとても良く動いたのでOK
test-runnerのカバレッジは動く環境を探り当てるのが難しすぎる上、ここのクリーン環境で再現したところで実環境への導入には役に立たない(全く別のyakが発生する)と思われるので、試すのはやめておく。
数年後、test-runnerが安定して動くようになったら起こしてください。