Angular v8とjest-preset-angularの相性が悪いので解決策をまとめた
(190829 追記)
久々に試してみたらjest-preset-angular v7.1.1
以降にて解決を感じました。このバージョン以降ならばjest@24.x
と組み合わせても大丈夫そうです。
こんにちは、 @okunokentaro です。タイトルでなんの事を指しているのか分かる人向けに書いているので、 jest-preset-angular
の詳細解説は割愛します。
Angular v8 rc 段階にて判明した問題と、その workaround をまとめています。将来的にjest-preset-angular
やjest
自体のアップデートが入れば、この記事が不要になる可能性は大いにあります。
2019/5/15 時点で何が起きているか
Angular v7 までで jest を併用したければjest-preset-angular
を使うのが常でした。jest-preset-angular@7.0.x
はjest@24
系に乗ったことで、transform
, stringifyContentPathRegex
などの各種オプションの解釈が変わっている認識です。しかし、このままだと Angular テンプレートhtml
、スタイルscss
等の読み込みにて、jest 実行時にエラーを起こします。
Error: connect ECONNREFUSED 127.0.0.1:80
at Object.dispatchError (/path/to/node_modules/jest-environment-jsdom-thirteen/node_modules/jsdom/lib/jsdom/living/xhr-utils.js:60:19)
at Request.client.on.err (/path/to/node_modules/jest-environment-jsdom-thirteen/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:674:20)
at Request.emit (events.js:194:15)
at Request.onRequestError (/path/to/node_modules/request/request.js:881:8)
at ClientRequest.emit (events.js:189:13)
at Socket.socketErrorListener (_http_client.js:392:9)
at Socket.emit (events.js:189:13)
at emitErrorNT (internal/streams/destroy.js:82:8)
at emitErrorAndCloseNT (internal/streams/destroy.js:50:3)
at process._tickCallback (internal/process/next_tick.js:63:19) undefined
この問題の解決法は根気よくたどればあるのでしょうが、現状はjest-preset-angular@6.0.2
を使うという workaround に落ち着いている方が多いと思います。
jest-preset-angular@6.0.2の寿命が来た
Angular v8 になるとこの workaround に問題が起きます。
Angular CLI v8 では新規プロジェクト作成時にcore-js
がインストールされないようになりました。実際、v7 初期まではインストールされていたcore-js
は、Angular CLI v7.x 以降省かれるようになっています。モダンブラウザの多くが polyfill を必要しなくなったため容量の削減が目的と推測されます。
jest-preset-angular
は、特にv6.0.2
に関してはこの polyfill を必須としてpeerDependencies
に組み入れています。そのため、Angular CLI v8 にて作成したプロジェクトにてjest-preset-angular@6.0.2
を併用すると、module 不足としてテストの実行ができません。
さらなる workaround としてcore-js
をインストールすることも考えられますが、将来的なことを考えると、jest-preset-angular@7.0.x
で起こっているテンプレートの読み込みに失敗する問題を解決させたほうがよさそうです。
jest-preset-angular@7.0.x
にて起こっている問題の解決法は~~現在分かっておりません。~~下記参照。
問題の本質
切り分けの結果、jest-preset-angular@7.0.x
自体が悪なのではなく、jest@23.x
がjest@24.x
になったことで問題になっているというところまでは掴めました。これはjest@24.x
が依存しているjsdom
がscss
を読めないことに起因しています。その証拠に上記エラーはObject.dispatchError (jsdom/lib/jsdom/living/xhr-utils.js:60:19)
が発していますし、scss をやめて css に変更したらjest-preset-angular@7.0.x
上でオールグリーンとなりました。
解決法
これも workaround の一種ですが、比較的実行しやすい手段を見つけたのでお知らせします。
npm i -D jest@24.8.0 jest-preset-angular@7.1.0 identity-obj-proxy
インストール物は以上です。バージョンは執筆日 2019/5/15 時点のものなので、閲覧日と離れてる場合はアテにしてはいけません。
インストールしたらjest-preset-angular
のために用意しているであろうjest.config.js
に書き加えます。moduleNameMapper
, identity-obj-proxy
の箇所が増えています。
module.exports = {
preset: 'jest-preset-angular',
setupFilesAfterEnv: ['<rootDir>/src/setup-jest.ts'],
testPathIgnorePatterns: ['<rootDir>/src/test.ts'],
transform: {
'^.+\\.(ts|js|html)$': 'ts-jest',
},
moduleNameMapper: {
'\\.(css|scss)$': 'identity-obj-proxy',
},
globals: {
'ts-jest': {
tsConfig: 'tsconfig.test.json',
stringifyContentPathRegex: '\\.html$',
},
},
};
identity-obj-proxy
はファイルの依存解決時に指定の拡張子をなにがしかのオブジェクトとして扱ってくれるライブラリのようです。深追いはしてないです。(本家とQiita 記事をざっと眺めた理解)
これによって、scss のファイル解決が失敗しconnect ECONNREFUSED
となる問題を解決できます。
なお、この workaround は Nrwl/Nx のJest プラグイン実装より着想を得ました。(thx! 情報提供 @lacolaco )
そこまで筋の悪い解決法ではない気がするので、しばらく様子見つつ Angular v8, Jest v24, jest-preset-angular 構成で進めたい方はこの方法を試してみるとよいと思います。jest-preset-angular
側にはcore-js
が不要であるため peer から除去してほしい話と、identity-obj-proxy
がないとscss
で失敗する旨を報告しますした。
Discussion