Reputation: 91
hello I am running typescript with esm and jest and I have a problem with middy:
I had this test:
import { APIGatewayEvent, Context } from 'aws-lambda';
import { lambda } from '../index.js';
describe('get-by-id handler', () => {
test('Should not find id', async () => {
const x = jest.fn();
x();
expect(x).toHaveBeenCalled();
});
});
which worked fine with the configurations I supplied below and npm run test
command.
howerver when I changed it to look like this:
import { APIGatewayEvent, Context } from 'aws-lambda';
import { lambda } from '../index.js';
describe('get-by-id handler', () => {
test('Should not find id', async () => {
const res = await lambda({
pathParameters: { id: 'id' },
} as unknown as APIGatewayEvent, {} as Context);
console.log(res);
const x = jest.fn();
x();
expect(x).toHaveBeenCalled();
});
});
I get this error
jest -c jest.config.cjs
FAIL app/src/handlers/get-by-id/tests/index.test.ts
● Test suite failed to run
app/src/handlers/get-by-id/index.ts:4:19 - error TS2307: Cannot find module '@middy/core' or its corresponding type declarations.
4 import middy from '@middy/core';
~~~~~~~~~~~~~
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 11.409 s, estimated 13 s
Ran all test suites.
here is the config:
tsconfig.json
:
{
"extends": "./node_modules/@tsconfig/node20/tsconfig.json",
"compilerOptions": {
"declaration": true,
"outDir": "./dist/",
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false,
"resolveJsonModule": true
},
}
jest.config.cjs
:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(ts?)$',
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
};
package.json
:
{
"type": "module",
"main": "index.js",
"scripts": {
"build": "npm run clean && npm run copy-files && npm run tsc",
"copy-files": "copyfiles -e \"!{*.md,package?(-lock).json}\" \"**/*.*\" dist",
"clean": "rimraf ./dist",
"tsc": "tsc -p tsconfig.build.json",
"deploy": "cdktf deploy",
"lint": "npx eslint . --ext .ts,.tsx",
"test": "jest -c jest.config.cjs",
"coverage": "jest -c jest-cov.config.cjs"
},
"license": "ISC",
"devDependencies": {
"@cdktf/provider-aws": "^18.0.6",
"@tsconfig/node20": "^20.1.2",
"@types/aws-lambda": "^8.10.129",
"@types/jest": "^29.5.10",
"@types/node": "^20.10.0",
"@typescript-eslint/eslint-plugin": "^6.13.0",
"@typescript-eslint/parser": "^6.13.0",
"cdktf": "^0.19.1",
"constructs": "^10.3.0",
"copyfiles": "^2.4.1",
"eslint": "^8.54.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-require-extensions": "^0.1.3",
"jest": "^29.7.0",
"rimraf": "^5.0.5",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typescript": "^5.3.2"
},
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.460.0",
"@aws-sdk/client-secrets-manager": "^3.460.0",
"@aws-sdk/lib-dynamodb": "^3.460.0",
"@middy/core": "^5.0.3",
"axios": "^1.6.2",
"dotenv": "^16.3.1"
}
}
handler:
import type { APIGatewayEvent } from 'aws-lambda';
import middy from '@middy/core';
const handler = async (event: APIGatewayEvent) => {
return {
statusCode: 200,
};
};
// eslint-disable-next-line import/prefer-default-export
export const lambda = middy().handler(handler);
running on node 20.9.0
what I have tried so far step by step:
changing test
script in packge.json
to this
"NODE_OPTIONS=--experimental-vm-modules jest -c jest.config.cjs"
as mentioned here
result was the same
changing jest.config.cjs
to this:
module.exports = {
preset: 'ts-jest/presets/default-esm',
testEnvironment: 'node',
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(ts?)$',
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
transform: {
// '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
// '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest`
'^.+\\.tsx?$': [
'ts-jest',
{
useESM: true,
},
],
},
};
as mentioned here result was the same
jest.config.cjs
to this:const esModules = ["@middy"].join("|")
module.exports = {
preset: 'ts-jest/presets/default-esm',
testEnvironment: 'node',
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(ts?)$',
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
transform: {
'^.+\.ts?$': [
'ts-jest',
{
useESM: true,
},
],
},
transformIgnorePatterns: [`node_modules/(?!${esModules})`],
};
as mentioned here result was the same
tsconfig.json
to this:{
"compilerOptions": {
"incremental": true,
"target": "es2020",
"module": "es2020",
"declaration": true,
"sourceMap": true,
"composite": true,
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"preserveConstEnums": true,
"resolveJsonModule": true,
"allowJs": true,
"rootDir": ".",
"outDir": "lib"
},
"include": ["src/**/*", "tests/**/*"],
"exclude": ["node_modules"]
}
as mentioned here got this error:
ReferenceError: jest is not defined
9 | console.log(res);
10 |
> 11 | const x = jest.fn();
| ^
12 | x();
13 | expect(x).toHaveBeenCalled();
14 | });
at Object.<anonymous> (app/src/handlers/get-by-id/tests/index.test.ts:11:19)
Upvotes: 9
Views: 1931
Reputation: 96
If someone still has the same issue, I have found a solution. This is a common issue when working with ESM (ECMAScript Modules) in Jest. The error occurs because @middy/core is using ES modules (import syntax), but Jest is configured to handle CommonJS modules by default. in jest.config you should have something like this :
{
...other configs,
"transformIgnorePatterns": [
"/node_modules/(?!(@middy/core)/)"
],
"moduleNameMapper": {
"^@middy/core$": "/node_modules/@middy/core"
},
"transform": {
"^.+\.tsx?$": [
"ts-jest",
{
"tsconfig": "tsconfig.dev.json",
"useESM": true
}
],
"^.+\.jsx?$": "babel-jest"
}
}
after that, you will need two packages in devdep:
@babel/preset-env: "^7.x.x", babel-jest: "^29.x.x"
then you will need a babel.config.js file with this content:
module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
],
};
Upvotes: 0