MUIでE2Eテストをするときにアニメーションをオフにする方法

MUIとは

MUIとはReact向けのUIコンポーネントライブラリです。

コンポーネントが圧倒的に多く、UIのデザインなども良いので、現在日本では1番利用されているUIコンポーネントライブラリとなっています。

https://mui.com

そんなMUIですが、アニメーションを多用しているので動作が遅いという弱点があります。

アニメーションを多用していることや動作が遅いということはPlaywrightなどのE2Eテストをするときには大きなデメリットがあります。

アニメーションが多用されるということはE2Eテストの開始から終了までの時間が長くなることを意味します。

E2Eテストではスクリーンショットの保存をすることが多いですが、アニメーションの完了前にスクリーンショットを保存してしまうと問題があります。

例えばフェードアウトの最中なのに完全に非表示になる前に写り込んでしまうなどの意図しない状態で保存してしまいます。

また、MUIのボタンは押すとRippleエフェクトというアニメーションが発生しますが、Rippleエフェクト中にスクリーンショットを保存すると通常の状態の見た目で保存することができません。

React Button コンポーネント - Material-UI - MUI

MUIのアニメーションをオフにする方法

先程述べた通り、MUIではアニメーションの多用によりE2Eテストの完了時間が長くなったり、Rippleエフェクトのアニメーションがスクリーンショットに写り込んでしまうという問題が発生します。

これらの問題はMUIのアニメーションをオフにすることで解決します。

やり方はまず、MUIのテーマを読み込むためのテーマファイル(theme.ts)を作成して、Rippleエフェクトやアニメーションを無効化するコードを以下のように記述します。

theme.ts
import { createTheme } from '@mui/material/styles'

export const theme = createTheme({
  components: {
    MuiButtonBase: {
      defaultProps: {
        disableRipple: true
      }
    }
  },
  transitions: {
    create: () => 'none'
  }
})

手動で変えるのではなく、テストのときだけアニメーションを自動でオフにしたい場合は環境変数を設定します。

package.json
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "VITE_DISABLE_ANIMATION=true vite preview",
    "test": "VITE_DISABLE_ANIMATION=true playwright test"
  }
}
theme.ts
import { createTheme } from '@mui/material/styles'

const disableAnimation = import.meta.env.VITE_DISABLE_ANIMATION === 'true'

export const theme = createTheme({
  components: {
    MuiButtonBase: {
      defaultProps: {
        disableRipple: disableAnimation
      }
    }
  },
  transitions: disableAnimation
    ? {
        create: () => 'none'
      }
    : undefined
})

次にmain.tsxに<ThemeProvider>と先ほど作成したthemeを読み込みます。

main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'

import { ThemeProvider } from '@mui/material/styles'
import { theme } from './theme.ts'


createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <ThemeProvider theme={theme}>
      <App />
    </ThemeProvider>
  </StrictMode>,
)

以上の設定が完了した状態で起動してブラウザで確認すると、セレクトボックスを選択したときのアニメーションやボタンを押したときのRippleエフェクトが発生していないことが確認できます。

アニメーションとRippleエフェクトをオフにしたMUIのサンプル

アニメーションとRippleエフェクトをオフにすれば、Playwrightなどで以下のようなE2Eテストを行う場合、処理時間が短縮され、スクリーンショット保存時にRippleエフェクトなどが写り込むこともなくなります。

example.spec.ts
import { test, expect } from '@playwright/test'

test('MUI Button サンプル', async ({ page }, testInfo) => {
  await page.goto('http://localhost:5173/')

  const button = page.getByRole('button', { name: /クリック回数/ })
  await expect(button).toBeVisible()

  // ボタンをダブルクリック
  await button.dblclick()
  await expect(button).toHaveText(/クリック回数: 2/)

  // スクリーンショット保存
  const screenshotPath = testInfo.outputPath('button.png')
  await page.screenshot({
    path: screenshotPath
  })

  await page.close()
})