📖

Jest27でStoryshotsが動作しない問題の対策

2022/01/31に公開

背景

Jest 27でStoryshotsが動作しない。

以下のようなエラーが出力される

TypeError: Cannot destructure property 'instrument' of 'undefined' as it is undefined.
        at Object.process (/project/node_modules/@storybook/addon-storyshots/injectFileName.js:12:36)
        at ScriptTransformer.transformSource (/project/node_modules/jest-runner/node_modules/@jest/transform/build/ScriptTransformer.js:620:31)
        at ScriptTransformer._transformAndBuildScript (/project/node_modules/jest-runner/node_modules/@jest/transform/build/ScriptTransformer.js:766:40)
        at ScriptTransformer.transform (/project/node_modules/jest-runner/node_modules/@jest/transform/build/ScriptTransformer.js:823:19)
        at Runtime.transformFile (/project/node_modules/jest-runtime/build/index.js:1677:47)
        at Runtime._execModule (/project/node_modules/jest-runtime/build/index.js:1594:34)
        at Runtime._loadModule (/project/node_modules/jest-runtime/build/index.js:1185:12)
        at Runtime.requireModule (/project/node_modules/jest-runtime/build/index.js:1009:12)
        at Runtime.requireModuleOrMock (/project/node_modules/jest-runtime/build/index.js:1210:21)
        at requireContext (/project/node_modules/@storybook/babel-plugin-require-context-hook/register.js:29:12)

原因はスナップショットの出力先を変更することができるinjectFileNameがJest27で仕様変更があったTransformerへの対応ができてない。

思いつく対策案

  • 案1 Storybookの対応を持つ
  • 案2 Storyshotsの利用をやめる
  • 案3 Transformerを使わずに動かす
  • 案4 野良のTransformerを使う
  • 案5 自分で実装する

案1 Storybookの対応を持つ

Issueは上がっていますが、対応はちょっと難しい模様。Jest 26でも良いなら待つのも手だろう。

案2 Storyshotsの利用をやめる。

待ってもいいが、やめる手もある。
やめた代わりに@storybook/testing-reactなどなどを使って自分でsnapshotを取るのも手だろう。こちらのほうがコントロールしやすいので量が少ないなら大きな手間はない。
Chromaticなど利用している場合は何らかの対応が必要かもしれないが調べていない。

一応Trendを確認すると、Storyshotに対して、testing-reactはまだまだ使われていなさそうである。
StoryshotsとTesting Reactのtreand
https://www.npmtrends.com/@storybook/addon-interactions-vs-@storybook/addon-storyshots-vs-@storybook/addon-storyshots-puppeteer-vs-@storybook/addons-interactions-vs-@storybook/testing-react

案3 Transformerを使わずに動かす

動かない部分を調査するとjest.config.jsに指定するTransformerの仕様が27で変わったから。この機能はjestがテストを実行する前にコードを変換する関数を登録する。ということはその変換を事前に手でやって解決する方法がある。

if(exports.default != null) {
  exports.default.parameters = exports.default.parameters || {};
  exports.default.parameters.fileName = '${fileName.replace(/\\/g, '\\\\')}';
}

https://github.com/storybookjs/storybook/blob/next/addons/storyshots/storyshots-core/injectFileName.js#L16-L20

処理内容を確認するとparametersfileNameにファイル名が入るようにしていることがわかる。
Storyに予め書いておくとtransformを使わなくても動くと考えられる。(試してない)
CommonJS形式なら __filename を使って書けば手間がすくないかもしれない。(試してない)

スナップショットの出力先が変わるだけではあるので、いっそ何もしないのも手である。

案4 野良のTransformerを使う

公式ではまだ実装されていないが、代わりのTransformerを実装している人がいる。
不完全ながらも普通の使い方では使えるようになっているようだ。

https://github.com/eiel/storyshot-jest-27

案5 自分で実装する

案4の方法はすべてのユースケースを完璧に使える実装になっていない。自分の環境で動くように作るのもありだろう。公式はいろんなユースケースをサポートしなければならないだろうから実装は難しくなりがちだが、特定のケースであればさまざまな方法があるだろう。

ここで行われているのはだいたいこんな感じ

  1. storiesファイルだった場合よばれるが、storiesファイルなのでbabelやtsでトランスパイルしたい。
  2. configを読み込んでtrasformに設定されている値を検査して、別のTransformerに処理を依頼する
  3. 返ってきたコードにparameters.fileNameへファイル名を追加するコードを追加する

余談

  • なんでプルリクエストしてないの?
    • Issueに解決案はのこしてるよ
    • 解決案はその場しのぎで、元の動作と同じ挙動にできてない。するのはちょっと大変かも。
    • Jest 26と27を共存するようにするのがよいのか、別にするのが良いのか、議論を持ちかけたいが、それを英語でやる英語力とリソースがない
    • 需要が読めてなく見合う労力があるのか、否か。

Discussion