Storybook 6.x から 7.0へアップグレードするときに詰まったこと
概要
StorybookをStorybook 6.x から 7.0へアップグレードしたときに、公式のMigrationガイドを元に進めたものの、テストがFlakyになってしまったり、落ちてしまったり、修正に苦労したのでまとめます。 アップグレード方法はざっとだけ触れます
全体のおおまかな流れ
- Storybook7へアップグレード
- 廃止されたaddonの削除
- main.ts の修正
- 廃止された型の修正
@storybook/testing-react
アンインストール- 詰まったところ
公式のMigration Guide
Migration guide for Storybook 7.0
Storybook7へアップグレード
pnpm dlx storybook@latest upgrade
https://storybook.js.org/docs/react/migration-guide#automatic-upgrade
deprecatedになったaddonの削除 / 新規addon追加
互換性がなくなったaddonがあるとAttentionがでるので削除する
一覧 Community outreach to upgrade addons to 7.0 · Issue #20529 · storybookjs/storybook · GitHub
main.tsの修正
main.jsはこちらを参考に Configure Storybook
Next.jsのroutingなど、addon不要になったのが個人的に嬉しかった🎉
Integrate Next.js and Storybook automatically
廃止された型の修正
storybook/MIGRATION.md at next · storybookjs/storybook · GitHub
ComponentStory, ComponentStoryObj, ComponentStoryFn and ComponentMeta types are deprecated
The type of StoryObj and StoryFn have been changed in 7.0 so that both the "component" as "the props of the component" will be accepted as the generic parameter.
ジェネリックパラメータとして、コンポーネント自体とコンポーネントのプロパティが受け入れられるようになった
import type { Story } from '@storybook/react'; import { Button, ButtonProps } from './Button'; // This works in 7.0, making the ComponentX types redundant. const meta: Meta<typeof Button> = { component: Button }; export const CSF3Story: StoryObj<typeof Button> = { args: { label: 'Label' } }; export const CSF2Story: StoryFn<typeof Button> = (args) => <Button {...args} />; CSF2Story.args = { label: 'Label' }; // Passing props directly still works as well. const meta: Meta<ButtonProps> = { component: Button }; export const CSF3Story: StoryObj<ButtonProps> = { args: { label: 'Label' } }; export const CSF2Story: StoryFn<ButtonProps> = (args) => <Button {...args} />; CSF2Story.args = { label: 'Label' };
@storybook/testing-react
アンインストール
Hey there! I wanted to thank you for using @storybook/testing-react!
@storybook/testing-react has been promoted to a first-class Storybook functionality in Storybook 7. This means that you no longer need this package, and this package will not be worked on anymore (especially regarding Storybook 6, unless there are security issues). Instead, you can import the same utilities, but from the @storybook/react package. Additionally, the internals of composeStories and composeStory have been revamped, so the way a story is composed is way more accurate, and it's possible this issue doesn't happen there anymore.
Please do the following:
Upgrade to Storybook 7 if you haven't already Uninstall @storybook/testing-react Update your imports from @storybook/testing-react to @storybook/react
@storybook/testing-reactは同じユーティリティを@storybook/reactパッケージからインポートできるようになったので、不要になる。アンインストールする。
// Component.test.jsx - import { composeStories } from '@storybook/testing-react'; + import { composeStories } from '@storybook/react'; // setup-files.js - import { setProjectAnnotations } from '@storybook/testing-react'; + import { setProjectAnnotations } from '@storybook/react';
詰まったところ
上記の一通りの更新作業を終えたところ、StoryをJestで再利用しているのですが、Storybookのplay関数を使ってテストしている箇所や、userEventのsetup関数を使っている箇所が尽く落ちるようになってしまった。
コードは公式より引用
export const InputFieldFilled: Story<InputFieldProps> = { play: ({ canvasElement }) => { const canvas = within(canvasElement); userEvent.type(canvas.getByRole('textbox'), 'Hello world!'); }, };
const { InputFieldFilled } = composeStories(stories); test('renders with play function', async () => { const { container } = render(<InputFieldFilled />); // pass container as canvasElement and play an interaction that fills the input await InputFieldFilled.play({ canvasElement: container }); const input = screen.getByRole('textbox') as HTMLInputElement; expect(input.value).toEqual('Hello world!'); });
対処方法
非同期処理に修正
- play: ({ canvasElement }) => { + play: async ({ canvasElement }) => { const canvas = within(canvasElement) - userEvent.type(canvas.getByRole('textbox'), 'Hello world!') + await userEvent.type(canvas.getByRole('textbox'), 'Hello world!') }, }
関連ライブラリを全部最新化 storybookだけアップデートしたものの、testing-library周りを最新化していなかった しばらくupdateしていなかったもの↓ これでplay関数周りは治った
"@testing-library/dom": "9.3.3", "@testing-library/jest-dom": "6.1.3", "@testing-library/user-event": "14.5.1",
testing-library/jest-domのv6 アップデートについて詰まったところ
testing-library/jest-dom をv6系にupdateすると、型を手動で取り込む必要があるので、グローバルな型定義ファイル(global.d.ts
)にマッチャーの型定義を手動でインポートして定義する必要があった。
公式issue
※公式issueではこの問題はv6でresolvedとあるが、できなかった
userEvent.typeで詰まったところ
userEvent.type not workingのissue issue
おそらくこのissueと同じ?でuserEvent.type
を使用している箇所がflakyになってしまった。
↓公式のこの書き方だと、user.typeもoKだったけど、setup関数化して使っているところはflakyになる
Introduction | Testing Library
import userEvent from '@testing-library/user-event' // setup function function setup(jsx) { return { user: userEvent.setup(), // Import `render` from the framework library of your choice. // See https://testing-library.com/docs/dom-testing-library/install#wrappers ...render(jsx), } } test('render with a setup function', async () => { const {user} = setup(<MyComponent />) // ... })
const user = userEvent.setup() // Import `render` and `screen` from the framework library of your choice. // See https://testing-library.com/docs/dom-testing-library/install#wrappers render(<MyComponent />)