TypeORM RepositoryNotFoundError when searching for entities using the class in Jest - node.js

I am having an issue here difficult to debug. I upgraded all my project dependencies and suddenly all my tests (Jest 25.5.4 or 26.x) started to fail with the "RepositoryNotFoundError".
The strange behavior is that all the entities are loaded in the metadata storage:
import { Connection, getMetadataArgsStorage } from 'typeorm';
let connection = await createConnection(); //<- The connection is creating according to my config
console.log(getMetadataArgsStorage()); //<- All the entities are here
console.log(getRepository('User')); //<- This works
console.log(getRepository(User)); //<- But this will raise the error
After some time debugging, I noticed the error is at https://github.com/typeorm/typeorm/blob/0.2.24/src/connection/Connection.ts#L482 and I created a repository for you to replicate the issue.
The comparison (metadata.target === target) always returns false. The targets are from the same class, but they are somewhat different. Using toString() return different versions of the class, one with comments stripped, another without comments (if I am using removeComments: true in my tsc config):
const targetMetadata = connection.entityMetadatas[7].target; // 7 is the index in my debug, it can be anything else in the array
console.log(targetMetadata === User); // prints false
I still did not figure out what caused the issue after the upgrade. Unfortunately I cannot share the code of the project, but I can give more information if you need. Could you help me to figure out what is the problem?
My jest config (in package.json):
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}

I still haven't tried tinkering with the example repository you provided, but I see that the paths in your TypeORM configuration files start with dist/. If you're using ts-jest and also have set src as your rootDir I think that could be the cause of your trouble.

Related

Mongoose + Typescript - Unable to require file: mongodb\index.ts

I have a Node + Express application with Typescript, which was running fine, until I decided to include Mongoose in the equation. When I try running it, I get the following error:
TypeError: Unable to require file: mongodb\index.ts
This is usually the result of a faulty configuration or import. Make sure there is a '.js', '.json' or other executable extension with loader attached before 'ts-node' available.
I'm running the application with Nodemon, and have the following configuration in nodemon.json:
{
"execMap": {
"ts": "ts-node"
}
}
Here's my tsconfig.json:
{
"compilerOptions": {
"esModuleInterop": true,
"moduleResolution":"node",
"baseUrl": ".",
"target": "es6",
"paths": {
"#controllers/*": ["./controllers/*"],
"#services/*": ["./services/*"],
"#routes/*": ["./routes/*"],
"#customTypes/*": ["./types/*"],
"#utils/*": ["./utils/*"],
"#graphql/*": ["./graphql/*"]
}
}
}
I'm kind of new to Node with Typescript, so I probably made some mistakes, but cannot find any info regarding what exactly is wrong.
Tried downgrading Mongoose, installing MongoDB manually and changed versions of #types/mongoose, but to no avail.

Unit test for aws lambda using jest

const invokeApi = require("/opt/nodejs/kiwiCall");
const decrypt = require("/opt/nodejs/encryption");
const cors = require("/opt/nodejs/cors");
When I am testing my index.js file by manual mocking these dependencies in mocks directory as follows:
__mocks__
|_invokeApi
|_decrypt
|_cors
it says
FAIL ./index.test.js
● Test suite failed to run
Cannot find module '/opt/nodejs/kiwiCall' from 'index.js'
However, Jest was able to find:
'../../../../lambdas/Flights/Locations/index.js'
You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'json', 'jsx', 'ts', 'tsx', 'node'].
See https://jestjs.io/docs/en/configuration#modulefileextensions-array-string
1 | "use strict";
2 |
> 3 | const invokeApi = require("/opt/nodejs/kiwiCall");
Wanted to know how can I mock the dependencies of AWS lambda in inedx.test.js file
In your package.json or jest.config you could add a moduleNameMapper for that directory.
"jest": {
"moduleNameMapper": {
"/opt/nodejs/(.*)": "<rootDir>/../nodejs/$1"
},
},
So I managed to figure out something based on my repository.
I'm using the moduleNameMapper to map the absolute path to another location in my repository to where I have the layer stored.
Eg.
moduleNameMapper: {'^/opt/config/config': '<rootDir>/src/layers/layers-core/config/config'}
In your case you could use a regex expression to match /opt/nodejs/ and map it elsewhere. Hope that helped.
EDIT:
I completely changed my approach and used babel-plugin-module-resolver with babel-rewire. I did this because the above method was incompatible with rewire. It's quite easy setup and you just need to setup a babel alias within .babelrc.
eg.
{
"plugins": [
["rewire"],
["babel-plugin-module-resolver", {
"alias": {
"/opt/config/config": "./src/layers/layers-core/config/config",
"/opt/utils/util-logger": "./src/layers/layers-core/utils/util-logger",
"/opt/slack": "./src/layers/layers-slack/slack"
}
}]
]
}
Combine this with IDE jsconfig.json path alias and you get full IDE support.
{
"compilerOptions": {
"module": "commonjs",
"target": "es2018",
"baseUrl": "./",
"paths": {
"/opt/config/config": ["src/layers/layers-core/config/config"],
"/opt/utils/util-logger": ["src/layers/layers-core/utils/util-logger"],
"/opt/slack/*": ["src/layers/layers-slack/slack/*"],
}
},
"exclude": ["node_modules", "dist"]
}
You can then reference your layers with jest.doMock('/opt/config/config', mockConfig);
EDIT 2:
Found a way to get Jest to mock it. Just slip {virtual: true} into the mock!
jest.doMock('/opt/config/config', mockConfig, {virtual: true});
I have pretty much the same issue. I have defined a layer which contains common code that's shared between other functions in my project. My project structure looks something like this:
project/
functions/
function1/
app.js
function2/
app.js
shared/
shared.js
I import my shared library like this:
const { doSomething } = require('/opt/shared');
exports.handler = async (event) => {
const result = await doSomething();
// etc...
return {statusCode: 200};
}
This works when I deploy to AWS Lambda because the /opt/shared exists and it can be referenced correctly. It also works if I run this on my machine using sam local invoke Function1 because it's running in a container, which makes /opt/shared available to the code.
However, I'm struggling to work out how I can mock this dependency in a unit test. If I simply do this: jest.mock('/opt/shared'), I'm getting: Cannot find module '/opt/shared' from app.test.js
You can use the modulePaths option, from this post.
Documentation
jest.config.js
"jest": {
"modulePaths": [
"<rootDir>/src/layers/base/nodejs/node_modules/"
]
}
You can dynamically create this array by scanning a directory
const testFolder = './functions/';
const fs = require('fs');
const modulePaths = fs.readdirSync(testFolder)
.reduce((modulePaths, dirName) => {
modulePaths.push(`functions/${dirName}/dependencies/nodejs/node_modules/`);
return modulePaths;
}, []);

Three.js with typescript autocomplete

I have a Node.js project that uses Typescript and Three.js. To import modules, I use the commonjs syntax, which I configured via
{
"compilerOptions": {
"module": "commonjs"
}
}
in my tsconfig.json. I downloaded Three.js via NPM have a typescript file like this:
const THREE = require('three');
const scene = new THREE.Scene();
which compiles fine, but I do not get any autocomplete. I don't think this specific to the editor used, as both Visual Studio Code as well as Neovim with YouCompleteMe don't work. Both work if I use the ES6 module syntax:
import * as THREE from 'node_modules/three/src/Three';
const scene = new THREE.Scene();
Here however I cannot get it to work without giving the actual path to the library (which is a problem later on when using webpack). What did I forget to configure to get autocomplete (or the ES6 syntax without explicitly defining the path, at this point I am fine with both solutions)?
EDIT
As mentioned in the comments to accepted answer, I was not able to find my mistake, but found a working solution while trying to create a minimal working project. So I will post this here, in case it might help someone else. If you have the same problem, please still read the answer, as it is correct.
My source file (in src/main.ts):
import * as THREE from 'three';
const scene = new THREE.Scene();
package.json (with webpack to test if the library can be resolved there):
{
"devDependencies": {
"#types/node": "^12.0.4",
"three": "^0.105.2",
"ts-loader": "^6.0.2",
"typescript": "^3.5.1",
"webpack": "^4.32.2",
"webpack-cli": "^3.3.2"
}
}
tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": { "*": ["types/*"] },
"target": "es6",
"module": "es6",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"sourceMap": true
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules/"
]
}
webpack.config.js:
const path = require('path');
module.exports = {
entry: './src/main.ts',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
resolve: {
extensions: [ '.ts', '.js' ]
},
module: {
rules : [
{ test: /\.ts$/, use: [ 'ts-loader' ], exclude: /node_modules/ }
]
}
};
What version of three is installed in your package.json file? Make sure it's 0.101 or later, since that's when TypeScript support began. I recommend you use the latest (105 as of this writing), since it gets updated definition files on each release.
Then, in your .ts files, you can import it with:
import * as THREE from "three";
// After importing, THREE is available for auto-complete.
var lala = new THREE.WebGLRenderer();
Edit:
You might need to perform path-mapping in your .tsconfig file to force the compiler to find the correct module address. I've never had to do this, but the Typescript documentation suggests something like this:
{
"compilerOptions": {
"baseUrl": ".", // This must be specified if "paths" is.
"paths": {
"three": ["node_modules/three/src/Three"] // relative to "baseUrl"
}
}
}
Update r126
As of revision r126, Three.js has moved Typescript declaration files to a separate repository. If you're using r126 or later, you'll have to run npm install #types/three as a second step. Make sure you install the version of #types/three that targets your version of Three.js. For example:
"three": "0.129.0",
"#types/three": "^0.129.1",

ReferenceError: Node is not defined (trying to use Node interface in typescript function in nodejs application)

While extending Cheerio library, I implemented the following static function (other extension functions work fine):
$.nodeType = function (elem: CheerioElement): number {
switch (elem.type) {
case "comment":
return Node.COMMENT_NODE; // <--- it fails here
case "tag":
return Node.ELEMENT_NODE; // <--- it fails here
case "text":
return Node.TEXT_NODE; // <--- it fails here
default:
return -1;
}
};
The following error appears during runtime (compilation with tsc -b succeed):
ReferenceError: Node is not defined
Node interface is part of the DOM API. Thus, I realized the need of explicitly include the DOM API under compilerOptions section of tsconfig.json.
However, I still get that runtime error.
Minimal relevant part of tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"incremental": true,
"lib": [
"esnext",
"dom"
],
"module": "commonjs",
"noImplicitAny": true,
"outDir": "./lib/",
"sourceMap": true,
"target": "esnext",
"watch": true
},
"include": [
"./src/**/*.ts",
]
}
I thought of explicitly import Node lib in the specific .ts file which contains the function, but I didn't find any "include-able" standard DOM lib.
Including a typescript lib doesn't polyfill the feature in an environment where it is not available.
While including "dom" type definitions will make the types available (at the compile time), but it doesn't actually make the Node API (typically provided by the browser runtime) available at runtime.
If you really need this functionality at runtime, you will need to also include an implementation of DOM for node.js such as jsdom which provides this API.
lorefnon explained the problem; here's a slightly hacky way to fix it:
Install jsdom
npm install jsdom
Add this to the top of your file:
const { JSDOM } = require('jsdom'); // or import { JSDOM } from 'jsdom';
const Node = new JSDOM('').window.Node;

Using mysql types in typescript

I have a nodejs typescript project that requires the use of mysqljs (https://www.npmjs.com/package/mysql), I've imported the DefinitelyTyped package (https://www.npmjs.com/package/#types/mysql) and included them in my tsconfig file
tsconfig.json
{
"compilerOptions": {
"noImplicitAny": false,
"module": "commonjs",
"noEmitOnError": true,
"removeComments": false,
"sourceMap": true,
"target": "es6"
},
"exclude": [
"node_modules"
],
"typeRoots": [
"node_modules/#types",
"Scripts/typings/node"
],
"types": [
"mysql",
"node"
]
}
I can correctly use the mysql module functions but I cannot access the types (IConnection, IQuery, etc).
I can also see the parameter and return types from intellisense.
Example
import * as mysql from 'mysql'
...
getUser(username: string): User {
mysql.createConnection({ host: "...", user: "...", password: "..." });
}
But I'd like to make a method that returns a type defined in the mysql typings (IQuery for example)
Something like
getUser(username:string): IQuery{
}
Being a beginner in typescript coming from a c# background, I don't see what's going on here.
Thanks for the help.
EDIT:
I have tried prefixing he type without any success as well as importing through this format import {IConnection} from 'mysql'
Thanks again.
It seems as though ReSharper was my issue, I still haven't found how to omit the errors or fix them though.
I reinstalled Visual Studio 2017 and it worked without Resharper, but when I did install it, I started having problems again.
Thanks for the help!
I'll edit this if I can find a solution.
You can access it by prefixing the interface with mysql:
getUser(username:string): mysql.IQuery {
}

Resources