✍️

【Storybook】ブラウザ標準のモーダルをインタラクションテストの中で操作したい

2024/04/05に公開

はじめに

インタラクションテストの中でブラウザ標準のモーダルを操作したいけど、どうすればいいんだろう?となりましたので、解決方法をまとめます。

問題

Sample.tsx
import React from 'react'

export const Sample: React.FC = () => {
  const handleButtonClick = () => {
    // この関数で表示されるダイアログの操作をインタラクションテストの中で行いたい
    window.confirm('更新します。よろしいですか?')
  }

  return (
    <div>
      <button onClick={handleButtonClick}>ボタン</button>
    </div>
  )
}

Sample.stories.tsx
import type { Meta, StoryFn, StoryObj } from '@storybook/react'
import { within } from '@storybook/test'

import { Sample } from './Sample'

export default {
  title: 'template/Sample',
  component: Sample,
} as Meta<typeof Sample>
type Story = StoryObj<typeof Sample>

const Template: StoryFn<typeof Sample> = (props) => {
  return <Sample {...props} />
}

export const Primary: Story = {
  render: Template,
  play: ({ canvasElement }) => {
    // ここの中でブラウザ標準のモーダルを操作したいけど、どうすればいい?
    within(canvasElement)
  },
}

解決方法

結論

window.confirmをmockして、操作する。

解説

以下のように記述することで、window.confirmの処理をmockすることが出来て、
モーダルの操作を行うことが出来ます。

Sample.stories.tsx
import type { Meta, StoryFn, StoryObj } from '@storybook/react'
import { expect, fn, userEvent, within } from '@storybook/test'

import { Sample } from './Sample'

export default {
  title: 'template/Sample',
  component: Sample,
} as Meta<typeof Sample>
type Story = StoryObj<typeof Sample>

const Template: StoryFn<typeof Sample> = (props) => {
  return <Sample {...props} />
}

export const Primary: Story = {
  render: Template,
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement)

    // ボタンをクリックした時、モーダルが呼ばれてOKを押す
    window.confirm = fn(() => true)

    const button = canvas.getByRole('button')

    await userEvent.click(button)

    // モーダルが表示されることを確認
    expect(window.confirm).toHaveBeenCalledWith('更新します。よろしいですか?')
  },
}

おわりに

「mockする」という考え自体が思い浮かばなかったので、新たな引き出しを増やせて良かったです。
同じく迷われた方の参考になれば幸いです。

参考

Discussion