環境#
- NX
- PNPM
- lodash-es
- Jest
karma から Jest への移行で以下のエラーが発生しました#
主な原因は、"node_modules" フォルダ内の ESM(ECMAScript Modules)ライブラリが Jest にサポートされていないことです。
Jest の ESM サポートはまだほとんど使用できない実験段階にあるため、現時点では主に会社のプロジェクトで Jest に移行しています。そのため、この問題を解決するために、この記事では transformIgnorePatterns
と moduleNameMapper
の 2 つの設定を使用します。
テストスイートの実行に失敗しました
Jestが予期しないトークンに遭遇しました
Jestはファイルを解析できませんでした。これは、コードまたはその依存関係が非標準のJavaScript構文を使用している場合、またはJestがそのような構文をサポートするように設定されていない場合に発生します。
デフォルトでは、JestはBabelをサポートしており、Babelの設定に基づいてファイルを有効なJSに変換します。
デフォルトでは、トランスフォーマーによって「node_modules」フォルダは無視されます。
以下の操作ができます:
• ECMAScript Modulesを使用しようとしている場合は、https://jestjs.io/docs/ecmascript-modules を参照して有効にする方法を確認してください。
• TypeScriptを使用しようとしている場合は、https://jestjs.io/docs/getting-started#using-typescript を参照してください。
• 「node_modules」の一部のファイルを変換するには、設定でカスタムの「transformIgnorePatterns」を指定できます。
• カスタムの変換が必要な場合は、設定で「transform」オプションを指定します。
• 単に非JSモジュール(バイナリアセットなど)をモックする場合は、「moduleNameMapper」構成オプションでそれらをスタブ化できます。
これらの設定オプションの詳細と例については、ドキュメントを参照してください:
https://jestjs.io/docs/configuration
カスタム変換に関する情報については、次を参照してください:
https://jestjs.io/docs/code-transformation
以下の設定は、lodash-es を参考にしています。
transformIgnorePatterns#
公式ドキュメントの説明は次のとおりです:変換前にすべてのソースファイルパスと一致する正規表現パターン文字列の配列。ファイルパスがいずれかのパターンと一致する場合、変換は行われません。
つまり、transformIgnorePatterns
は、コード変換時に無視するファイルやフォルダを指定するために使用されます。
NX のデフォルトの Jest 設定では、node_modules/(?!.*\\.mjs$)
となっています。
この正規表現は、node_modules/
で始まるフォルダパスに一致し、ただし、拡張子が.mjs
で終わるフォルダパスは除外します。?!
は否定先読みを表し、このようなフォルダパスには一致しないことを意味します。
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
上記の設定は、拡張子が.mjs
であるファイルを ESM から CommonJS に変換して Jest をサポートすることを意味します。
lodash-es の変換を追加#
PNPM もサポートするようにしましょう
const esModules = ['.*\\.mjs$', 'lodash-es'].join('|');
export default {
...
transformIgnorePatterns: [`node_modules/(?!.pnpm|${esModules})`],
...
}
変換後、失敗した数は 15 から 11 に減少しましたが、この方法では変換に余分なコストがかかり、51 秒かかります。ただし、最初の変換が完了すると、キャッシュされて変換する必要がなくなるようです。
より少ないコストの方法:moduleNameMapper#
この方法では、ライブラリ自体が対応する CommonJS が必要ですので、変換は必要ありません。12 秒で実行できます。
export default {
...
moduleNameMapper: {
'^lodash-es$': 'lodash',
},
...
}
最終的な設定の参考例#
/* eslint-disable */
const esModules = ['.*\\.mjs$'].join('|');
export default {
displayName: 'pc',
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../coverage/apps/pc',
moduleNameMapper: {
'^lodash-es$': 'lodash',
},
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: [`node_modules/(?!.pnpm|${esModules})`],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};