React + TypeScriptのアプリケーションの環境を構築していて、StorybookとStoryshotsの設定でハマったところのメモ。
環境
React 16.12.0
typescript 3.7.4
webpack 4.41.5
storybook 5.2.8
最初create-react-appでやっていたが、storyshotの導入でwebpackやjestの設定を変える必要が出てきたので、使わないで構築し直した。
#storybookの導入
セットアップ
package.json
devDependenciesに以下を追加してインストール
"@storybook/addon-actions": "5.2.8",
"@storybook/addon-links": "5.2.8",
"@storybook/addons": "5.2.8",
"@storybook/preset-typescript": "1.2.0",
"@storybook/react": "5.2.8",
scriptsにスクリプトを追加
"storybook": "start-storybook -p 6006",
/.storybook/config.jsを追加
import { configure } from '@storybook/react';
const req = require.context('../src/', true, /\.stories\.(?:js|ts)x?$/);
configure(req, module);
/.storybook/webpack.config.js を追加
ストーリーをTypeScriptで書くには、 /.storybook/
内にwebpack.config.jsを追加する。
アプリケーションのwebpack設定を引き継ぎたい場合は
https://storybook.js.org/docs/configurations/custom-webpack-config/#using-your-existing-config
のように書くと短く書ける。
resolve: {
modules: ['src', 'node_modules'],
extensions: ['.ts', '.tsx', '.js'],
},
この辺の設定も引き継がないと、import文のpath解決がうまく行かなくてちょっとハマった。
stories.tsx内でimport文でエラーになってしまう。
import OtherComponent from 'components/OtherComponent'; // モジュールのrootに指定した`src`がmodulesに設定されてないと Cannot find module
import AppHeader from '..'; //同階層のindex.tsxをインポート。extensionsにtsxが設定されていないと、Can't resolve '..'
なので
/.storybook/webpack.config.js はこのようになった。
const path = require('path');
// your app's webpack.config.js
const custom = require('../webpack.config.js');
module.exports = async ({ config, mode }) => {
return {
...config,
resolve: { ...config.resolve, ...custom.resolve },
module: { ...config.module, rules: custom.module.rules }
};
};
Storyshotsの導入
require-context-hookの設定
セットアップは
https://storybook.js.org/docs/testing/structural-testing/#using-storyshots
の通りだが、
require.context
の部分でエラーが出てしまうので、require-context-hookの設定をする。
devDependenciesに追加するのはこの辺
"jest": "24.9.0",
"react-test-renderer": "16.12.0",
"@storybook/addon-storyshots": "5.2.8",
"@types/jest": "24.0.25",
"babel-plugin-require-context-hook": "1.0.0",
.babelrcにテスト用の設定を追加
"env": {
"test": {
"plugins": [
"require-context-hook"
]
}
}
/.storybook/config.js
のrequire.contextの行の前に以下を足す。
if (process.env.NODE_ENV === 'test') {
require('babel-plugin-require-context-hook/register')();
}
【追記】 storybook 5.3.1 にアップデートしたらrequire.contextを使わない記述になっていたので、ここの設定は不要っぽい
cssや画像のインポートでのエラーを解決する
ストーリーの中でcssや画像をインポートしていると、storyshotsの実行時にエラーが出る。
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
https://jestjs.io/docs/en/webpack#handling-static-assets
この通りにmoduleNameMapperを使用して解決できる。
スナップショットファイルをストーリーごとに分ける
デフォルトでは全てのストーリーのスナップショットが1ファイルに書きだされる。
スナップショットはストーリーごとに分かれた方が圧倒的に差分が見やすいので、分けたい。
そこでmultiSnapshotWithOptionsというオプションを指定する。
https://github.com/storybookjs/storybook/tree/master/addons/storyshots/storyshots-core#multisnapshotwithoptionsoptions
Storyshots.test.jsをこのように書き換える。
import initStoryshots, {
multiSnapshotWithOptions,
} from '@storybook/addon-storyshots';
initStoryshots({
test: multiSnapshotWithOptions(),
});
jestのconfigにはtransformの設定をする。
"transform": {
"^.+\\.stories\\.tsx$": "@storybook/addon-storyshots/injectFileName",
"^.+\\.jsx?$": "babel-jest",
".+\\.tsx?$": "ts-jest"
}
これで、各ストーリーのフォルダにスナップショットが作成される。