こんにちは、CX事業本部のmorimorikochanです。
表題の通りNext.jsで構築したフロントエンドでJestを実行させようとしていたのですが、vanilla-extractがうまく読み込むことができませんでした。
あれこれ試行錯誤して暫定的な解決方法がわかったので、もし同じ問題ではまっている人のために共有したいと思います。
問題
vanilla-extractのフォーマットに沿った*.css.ts
をimportしているコンポーネントのテストをJestで書くと、Jest実行時に*.css.ts
の中身が読み込まれずundefined
になってしまいエラーが出てしまいます。
テスト対象コンポーネント
import { ButtonStyles as styles } from './button.css'
// ...
export const ButtonA: FC<ButtonProps> = ({ type, children }) => (
<button type={type} className={clsx([styles.color])}>
{children}
</button>
)
● Buttonのテスト › `isSubmit`がONの場合、typeが`submit`になっていること
TypeError: Cannot read properties of undefined (reading 'color')
12 | */
13 | export const ButtonA: FC<ButtonProps> = ({ type, children }) => (
> 14 | <button type={type} className={clsx([styles.color])}>
|
このときjest.config.js
は以下のような状態です。
const nextJest = require('next/jest')
const createJestConfig = nextJest({
dir: './',
})
// ...
const customJestConfig = {
moduleDirectories: ['node_modules', '<rootDir>/'],
testEnvironment: 'jsdom',
transform: { '^.+\\.css\\.ts$': '@vanilla-extract/jest-transform' },
}
module.exports = createJestConfig(customJestConfig)
この問題の原因は、Jestが*.css.ts
を読み込む場合、あらかじめtransform
に設定された@vanilla-extract/jest-transform
によって[hash].css
に変換されるのですが、それをJestがmoduleNameMapper
を通して読み込む際にモックファイル(styleMock.js
)が優先されてしまい本来読み込ませたい[hash].css
が読み込めなくなってしまうためでした。
// Next.js(next/jest)が提供する`moduleNameMapper`はこんな感じになってる。これにvanilla-extractのcssファイルがマッチしてしまう
{
'^.+\\.(css|sass|scss)$': '/Users/xxxxx/yyyyy/node_modules/next/dist/build/jest/__mocks__/styleMock.js'
}
これらのモックファイルの仕組みはNext.js
が提供している仕組みであり、グローバルなcssファイル(やsassやscss)をJestで読み込めるように用意された仕組みです。
実はvanilla-extractの公式サイトではこの問題と回避策がすでに記載されています
2パターン紹介されていますが、いずれもvanilla-extractによって作成されたcssファイルとこれらのcssファイルを区別する方法です。
しかし、実際はこの方法で解決できないことが多いのではないかと思います。
例えば外部パッケージから読み込むcssファイルはファイル名やディレクトリが変更できないので、その場合は結果的に区別することができません。
だとすると逆にvanilla-extractが吐き出すcssファイルを区別できるような命名規則に変更(例えば[hash].vanilla.css
)できれば解決するのですが、残念ながら今現在vanilla-extractからこの設定は提供されていません。
もしかするとJestに vanilla-extractが吐き出したCSSファイルをさらにrenameするtransform を追加すればこれらが区別できそうですが、Jest力がないため諦めました。。
暫定的な対策
暫定的な対策としては、以下のようにグローバルなcssファイルをimportしているファイルはJestでは一切読み込まないようにするしかないかと思っています。
実際、Next.jsではグローバルなCSSファイルはpages/_app.js
でしか読み込むことができないため、このpages/_app.js
をJestで読み込まなければ正常にテストできるはずです
const nextJest = require('next/jest')
// ...
module.exports = async () => {
const config = await createJestConfig(customJestConfig)()
// WARN: キーはNext.jsのバージョンなどにより変化する可能性があります
delete config.moduleNameMapper['^.+\\.(css|sass|scss)$']
// WARN: なぜか`customJestConfig.transform`に含めても動作しないのでここで定義
config.transform = {
'^.+\\.css\\.ts$': '@vanilla-extract/jest-transform',
...config.transform,
}
return config
}
また、別問題ですが私の環境では transform
に@vanilla-extract/jest-transform
を追加する記述がcreateJestConfig
に含まれていても正常に動作しませんでした。
なのでcreateJestConfig
が解決した後のconfigに強制的に追加しています。
FAIL tests/unit/atoms/Button.test.tsx
● Test suite failed to run
Styles were unable to be assigned to a file. This is generally caused by one of the following:
- You may have created styles outside of a '.css.ts' context
- You may have incorrect configuration. See https://vanilla-extract.style/documentation/getting-started