Jest で ECMAScript Modules (ESM) をテスト
Jest で ECMAScript Modules (ESM) をテストしている時に、 SyntaxError: Cannot use import statement outside a module
というエラーが発生しました。
このエラーは、テスト対象が import
キーワードで他のモジュールを利用していることが原因です。
Jest experimental support を利用することで、このエラーを解決できます。
Jest ships with experimental support for ECMAScript Modules (ESM).
この記事では、 ESM パッケージの例を用いて、公式ドキュメントを参考にどのように解決するかを紹介します。
プロジェクト初期化
ESM パッケージ作成
以下のコマンドを実行して、 ESM パッケージを作成してください。
mkdir jest-esm && cd jest-esm
npm init # Run with all options default
以下のパッケージをインストールしてください。
npm i -D typescript jest @types/jest ts-node ts-jest
生成された package.json
は以下のとおりです。
{
"name": "jest-esm",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/jest": "^29.5.12",
"jest": "^29.7.0",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
}
"type": "module"
を package.json
に追加してください。
@@ -3,6 +3,7 @@
"version": "1.0.0",
"description": "",
"main": "index.js",
+ "type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
TypeScript 設定
以下のコマンドを実行して、 tsconfig.json
を作成してください。
npx tsc --init
tsconfig.json
を以下のように修正してください。
@@ -14 +14 @@
- "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ "target": "es6", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
@@ -28 +28 @@
- "module": "commonjs", /* Specify what module code is generated. */
+ "module": "es6", /* Specify what module code is generated. */
@@ -30 +30 @@
- // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
+ "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
Jest 設定
以下のコマンドを実行して、設定ファイル (jest.config.ts
) を生成してください。
npm init jest@latest
The following questions will help Jest to create a suitable configuration for your project
✔ Would you like to use Jest when running "test" script in "package.json"? … yes
✔ Would you like to use Typescript for the configuration file? … yes
✔ Choose the test environment that will be used for testing › jsdom (browser-like)
✔ Do you want Jest to add coverage reports? … no
✔ Which provider should be used to instrument code for coverage? › v8
✔ Automatically clear mock calls, instances, contexts and results before every test? … no
指定したオプションは以下のテーブルのとおりです。
Option | Value |
---|---|
use Jest when running “test” script in “package.json” | yes |
use Typescript for the configuration file | yes |
test environment | jsdom (browser-like) |
add coverage reports | no |
provider for coverage | v8 |
Automatically clear mock calls, instances, contexts and results | no |
ts-jest 設定
公式ドキュメントに記載のとおり、 ts-jest
を jest.config.ts
の preset
に設定してください。
Instead, add the line: preset: “ts-jest” to the jest.config.js file afterwards.
@@ -102,7 +102,7 @@
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
- // preset: undefined,
+ preset: 'ts-jest',
// Run tests from one or more projects
// projects: undefined,
Jest の ESM サポート
2024年2月現在、 Jest ESM サポートは Experimental です。 詳細は、公式ドキュメントをご参照ください。
Jest ships with experimental support for ECMAScript Modules (ESM).
The implementation may have bugs and lack features. For the latest status check out the issue and the label on the issue tracker.
Also note that the APIs Jest uses to implement ESM support are still considered experimental by Node (as of version 18.8.0).
package.json
を以下のように修正してください。
@@ -5,7 +5,7 @@
"main": "index.js",
"type": "module",
"scripts": {
- "test": "jest"
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
},
"author": "",
"license": "ISC",
次に、 extensionsToTreatAsEsm
を jest.config.ts
に追加してください。
@@ -194,6 +194,8 @@
// Whether to use watchman for file crawling
// watchman: true,
+
+ extensionsToTreatAsEsm: ['.ts'],
};
export default config;
ts-jest の ESM サポート
公式ドキュメントに基づいて、 jest.config.ts
を以下のように更新してください。
@@ -90,7 +90,9 @@
// ],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
- // moduleNameMapper: {},
+ moduleNameMapper: {
+ '^(\\.{1,2}/.*)\\.js$': '$1',
+ },
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
@@ -175,7 +177,14 @@
// testRunner: "jest-circus/runner",
// A map from regular expressions to paths to transformers
- // transform: undefined,
+ transform: {
+ '^.+\\.tsx?$': [
+ 'ts-jest',
+ {
+ useESM: true,
+ },
+ ],
+ },
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
ESM 作成
hast-util-from-html インストール
サードパーティの ESM として、 hast-util-from-html
をインストールしてください。
npm i hast-util-from-html
@@ -15,5 +15,8 @@
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
+ },
+ "dependencies": {
+ "hast-util-from-html": "^2.0.1"
}
}
node_modules/hast-util-from-html/index.js
を確認してください。
export
キーワードでエクスポートされています。
/**
* @typedef {import('hast-util-from-parse5')} DoNotTouchItRegistersData
*
* @typedef {import('./lib/index.js').ErrorCode} ErrorCode
* @typedef {import('./lib/index.js').ErrorSeverity} ErrorSeverity
* @typedef {import('./lib/index.js').OnError} OnError
* @typedef {import('./lib/index.js').Options} Options
*/
export {fromHtml} from './lib/index.js'
ESM
以下の内容で index.ts
を作成してください。
hast-util-from-html
から fromHtml
をインポートしています。
import { fromHtml } from 'hast-util-from-html';
export default function JestEsm(): void {
const root = fromHtml(
'<span><a href="https://github.com">GitHub</a></span>',
{ fragment: true },
);
console.info(root);
}
テストコードとして、以下の内容で index.spec.ts
を作成してください。
import JestEsm from './index';
test('case1', () => {
JestEsm();
});
ESM テスト
以下のコマンドを実行して、モジュールをテストしてください。
npm run test
> [email protected] test
> jest
● Validation Error:
Test environment jest-environment-jsdom cannot be found. Make sure the testEnvironment configuration option points to an existing node module.
Configuration Documentation:
https://jestjs.io/docs/configuration
As of Jest 28 "jest-environment-jsdom" is no longer shipped by default, make sure to install it separately.
出力された情報に基づいて、 jest-environment-jsdom
をインストールしてください。
npm i -D jest-environment-jsdom
@@ -12,6 +12,7 @@
"devDependencies": {
"@types/jest": "^29.5.12",
"jest": "^29.7.0",
+ "jest-environment-jsdom": "^29.7.0",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
再度テストを実行してください。テストが成功するはずです。
npm run test
> [email protected] test
> node --experimental-vm-modules node_modules/jest/bin/jest.js
console.info
{
type: 'root',
children: [
{
type: 'element',
tagName: 'span',
properties: {},
children: [Array],
position: [Object]
}
],
data: { quirksMode: false },
position: {
start: { line: 1, column: 1, offset: 0 },
end: { line: 1, column: 53, offset: 52 }
}
}
at JestEsm (index.ts:8:11)
(node:47304) ExperimentalWarning: VM Modules is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
PASS ./index.spec.ts
✓ case1 (20 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1 s
Ran all test suites.