環境#
- NX
- PNPM
- lodash-es
- Jest
從 karma 轉移到 Jest 遇到了如下報錯#
主要原因是 "node_modules" 文件夾中 ESM(ECMAScript Modules)庫不被 Jest 支持。
鑑於 Jest ESM 支持還在幾乎不可用的試驗階段,而目前我主要是在公司項目上遷移到 Jest。所以本文主要採用 transformIgnorePatterns
和 moduleNameMapper
兩種配置來解決這個問題。
測試套件運行失敗
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})`],
...
}
轉換後 failed 數量從 15 減少到 11,但是這麼做會有一個轉換的過程會有額外的支出,需要 51s。不過第一次轉換完後貌似就會緩存然後就不用轉換了。
支出更少的方法 moduleNameMapper#
這種方法需要庫本身有對應的 CommonJS,就不需要轉換了。可以跑到 12s
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',
],
};