Here is my code:
babel.config.cjs
module.exports = {
plugins: ['#babel/plugin-transform-modules-commonjs'],
presets: [['#babel/preset-env', { targets: { node: 'current' } }]]
}
jest.config.cjs
module.exports = {
globals: {
"ts-jest": {
isolatedModules: true,
},
},
};
Setup.ts
import awilix from 'awilix';
import express from 'express';
import CognitoSC from './Service/Cognito.js'
import authController from './Controller/Auth.js';
const app = express();
const container = awilix.createContainer({
injectionMode: awilix.InjectionMode.CLASSIC
});
auth.test.ts
import request from 'supertest';
import app from '../Setup.js';
describe('Testing Authentication/Authorization', function() {
it('responds with json', async function() {
const response = await request(app)
.post('/signin')
.send('username=Test')
.send('password=Testtest123$')
expect(response.status).toEqual(200);
expect(response.body.data.success).toEqual(true);
});
});
and I build it with tsc ("module": "es2022"). When I run npx jest I get an error saying
TypeError: Cannot read properties of undefined (reading 'createContainer')
> 8 | const container = awilix.createContainer({
Interesting thing that I noticed is that if I open Setup.js file which is generated by tsc and change the code from
import awilix from 'awilix';
to
const awilix = require('awilix');
and run npx jest, the tests pass without a problem. I'm little lost and can't figure out what is the problem. I also checked inside Setup.js and express is imported without a problem using ES6 syntax but why it can't do the same with awilix?
It doesn't appear that awilix supports modules.
From their readme they say to use const awilix = require('awilix') .
See here for more information about import/require/ES modules/commonJS.
Related
I have a AWS dependency layer on on folder nodejs/
There is node_modules/ and package.json with npm dependencies
I created a folder called utils/ and my file is util.js
Since it's a layer on AWS, I import using const utils = require('/opt/nodejs/utils/util'); on my app.js
Problem is that my test cases started failing Cannot find module '/opt/nodejs/utils/util' from 'backend/lambdas/cars/app.js'
How can I fix my test case??
const app = require('./app');
describe('lambda', function () {
it('something', async () => {
const response = await app.lambdaHandler();
....
});
});
app.js
const httpStatusCode = require('http-status-codes');
const cors = require('/opt/nodejs/utils/util');
exports.lambdaHandler = async (event) => {
return {
statusCode: httpStatusCode.OK
};
};
PS: This nodejs folder is on the same level as the lambdas folder
You should import like this const utils = require('../nodejs/utils/util')
I have an application that uses vue and express and I have tests written for each, I can run either a vue test on it's own or the express test on it's own.
Below is the jest config file this will run fine for Vue, if I remove the preset it will work fine for my supertest/express test .
module.exports = {
// TODO: This line is needed for vue tests but breaks js tests
preset: "#vue/cli-plugin-unit-jest",
coverageDirectory: "test-reports/",
coveragePathIgnorePatterns: [
"/node_modules/"
],
reporters: [
"default",
[
"jest-junit",
{
outputName: "vue_junit.xml",
outputDirectory: "test-reports/",
},
],
],
};
For reference here is both the tests if it helps
// Libraries
import Vuetify from "vuetify";
import Vue from "vue";
// Components
import UserBar from "#/components/userBar.vue"
// Utilities
import { createLocalVue, shallowMount } from "#vue/test-utils";
// Setup Up
const localVue = createLocalVue();
const vuetify = new Vuetify();
Vue.use(Vuetify);
describe("userBar.vue", () => {
let wrapper;
beforeEach(() => {
wrapper = shallowMount(UserBar, {
localVue,
vuetify,
});
});
afterEach(() => {
wrapper.destroy();
});
it("wrapper created properly", () => {
expect(typeof wrapper).toBe("object");
});
});
express test
var request = require('supertest');
import * as local from '../../server';
var express = require('express');
var app = express();
describe('loading express', function () {
var server;
beforeEach(function () {
delete require.cache[require.resolve('../../server/index')];
server = local.default(app);
});
afterEach(function (done) {
server.close(done);
});
it('responds to /client', function testSlashHealth(done) {
request(server)
.get('/client')
.expect(200, done);
});
it('404 everything else', function testPath(done) {
request(server)
.get('/foo/bar')
.expect(404, done);
});
});
Got around this by specifying 2 config files when testing
jest -c jest.config.express.js
jest -c jest.config.vue.js
I am working on a piece where I am basically refactoring existing code. I have two files: index and server. My index is:
const md5File = require('md5-file');
const fs = require('fs');
const path = require('path');
const ignoreStyles = require('ignore-styles');
const register = ignoreStyles.default;
const extensions = ['.gif', '.jpeg', '.jpg', '.png', '.svg'];
...
require('#babel/polyfill');
require('#babel/register')({
ignore: [/\/(build|node_modules)\//],
presets: ['#babel/preset-env', '#babel/preset-react'],
plugins: [
'#babel/plugin-syntax-dynamic-import',
'#babel/plugin-proposal-class-properties',
'dynamic-import-node',
'react-loadable/babel'
]
});
// Now that the nonsense is over... load up the server entry point
require('./server');
My server is like:
import path from 'path';
import Loadable from 'react-loadable';
...
const main = async () => {
// tell React Loadable to load all required assets
await Loadable.preloadAll();
process.on('unhandledRejection', err => {
console.error(err);
process.exit(1);
});
const server = fastify(config.fastify);
server.register(require('./routes'), config);
server.register(fastifyStatic, {
root: path.resolve(__dirname, '../build')
});
if (require.main === module) {
// called directly i.e. $ node index.js
const address = await server.listen(config.address);
// start listening - ROCK AND ROLL!
server.log.info(`Server running at: ${address}`);
} else {
// required as a module => executed on aws lambda
module.exports = server;
}
};
main();
The server should run a REST service when executed locally, and export the server instance for the inject method. This way a proxy can attach to it when running under AWS Lambda.
I used the same setup before, multiple times. Only the two pieces were in the same file eg the server was inside the index. A single file version works fine - require.main comparison tells the program how it is running and module.exports exposes the server instance [server] with the needed inject method when running under Lambda and runs the REST service with a direct invocation.
However, since I need to import react-loadable this time, I split the files in two pieces.
Now, I probably need to figure out how the code is running inside index.js as server.js is not being invoked directly then pass it to the server. It is probably not too difficult.
My main problem is that when if I do console.log(require('./server')) from index, it prints {}. While the server instance is created successfully, and the inject() is present; I am somehow unable to export it from the server.js file and import into index.js correctly, and therefore [re-]export it from index.js for the proxy to attach.
Obviously, I am doing something incorrectly. To me, seems that my require does not have the server instance because the instance gets created after the require finished. Since my main() is async, it is plausible.
What is the right way to accomplish this?
First, notice that your main() function in ./server.js is async and you're defining module.exports from within that asynchronous function. Second, you're calling require('./server.js') from ./index.js without waiting for the asynchronous work to finish. Node resolves require()-d modules as a blank object immediately (that's the {} you're getting), and then extends the object when any async or cyclic material becomes available. So that's why you're seeing what you're seeing.
Which solutions will or will not fit your use case will depend on the details of how your AWS/direct invocation is supposed to work. Here's a suggestion:
const md5File = require('md5-file');
const fs = require('fs');
const path = require('path');
const ignoreStyles = require('ignore-styles');
const register = ignoreStyles.default;
const extensions = ['.gif', '.jpeg', '.jpg', '.png', '.svg'];
// ...
require('#babel/polyfill');
require('#babel/register')({
ignore: [/\/(build|node_modules)\//],
presets: ['#babel/preset-env', '#babel/preset-react'],
plugins: [
'#babel/plugin-syntax-dynamic-import',
'#babel/plugin-proposal-class-properties',
'dynamic-import-node',
'react-loadable/babel'
]
});
process.env.serverRunLocally = require.main === module;
// Now that the nonsense is over... load up the server entry point
require('./server').then(listen => listen());
import path from 'path';
import Loadable from 'react-loadable';
// ...
const main = async () => {
// tell React Loadable to load all required assets
await Loadable.preloadAll();
process.on('unhandledRejection', err => {
console.error(err);
process.exit(1);
});
const server = fastify(config.fastify);
server.register(require('./routes'), config);
server.register(fastifyStatic, {
root: path.resolve(__dirname, '../build')
});
if (process.env.serverRunLocally) {
// called directly i.e. $ node index.js
return () => {
const address = await server.listen(config.address);
// start listening - ROCK AND ROLL!
server.log.info(`Server running at: ${address}`);
}
} else {
// required as a module => executed on aws lambda
return server;
}
};
module.exports = main();
I added tests to my node js project using jest but for each test suite there's a beforeAll method that creates a new test server and connects to a mongo database and an afterAll method that closes both test server and the database. I would like to perform the above tasks globally for all the test suites not one at a time. Below is a sample of my code.
app.js
const express = require("express");
const app = express();
const { connectToDb } = require("./startup/db");
require("./startup/routes")(app);
connectToDb();
...
const port = process.env.PORT || 3000;
if (process.env.NODE_ENV !== "test") {
app.listen(port, () => winston.info(`Listening on port ${port}...`));
}
module.exports = app;
auth.test.js
const request = require("supertest");
const http = require("http");
const { disconnectDb } = require("../../startup/db");
describe("auth middleware", () => {
let server;
beforeAll((done) => {
const app = require("../../app");
server = http.createServer(app);
server.listen(done);
});
afterAll((done) => {
server.close(done);
disconnectDb();
});
it("should return 401 if no token is provided", async () => {
const res = request(server)
.post("/api/genres")
.set("x-auth-token", "")
.send({ name: "genre1" });
expect(res.status).toBe(401);
});
...
jest.config.js
module.exports = {
testEnvironment: "node",
};
Try with this jest.config.js:
module.exports = {
testEnvironment: "node",
globalSetup: '<rootDir>/src/testSetup.ts'
};
And in testSetup.ts you can do:
// testSetup.ts
module.exports = async () => {
const app = require("../../app");
server = http.createServer(app);
server.listen(done);
};
use this config: setupFiles: ['./tests/setup.js']
your setup file should look like this:
// setup.js
(async () => {
const app = require('../app.js')
global.app = app
})()
then you will be able to use app globally in every test suite
I had the same problem, I wanted to make one database connection before all test files and close the connection after all tests in all files.
But....I did not achieve what I wanted and MAYBE we don't need to do this.
I found a solution to launch functions beforeAll(),afterAll() etc... really before ALL TEST FILES and after ALL TEST FILES etc..
So you define these functions once in a certain file and they run for every test file.
To do that, all we need is to create a setupFile.ts and add path to this file in jest.config or in package.json "setupFilesAfterEnv": ["<rootDir>/__tests__/settings/setupTests.ts"],
Here is an example of my jest configuration.
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"setupFilesAfterEnv": ["<rootDir>/__tests__/settings/setupTests.ts"],
"rootDir": "src",
"verbose": true,
"clearMocks": true,
"testMatch": [
"**/**/*.test.ts"
]
},
Here is an example of setupFile.ts
import usersCollection from "../../database/user-schema";
import mongoose from "mongoose";
beforeAll(async () => {
try {
await mongoose.connect(process.env.MONGODB_URL!);
await usersCollection.deleteMany({});
} catch (error) {
console.log(error);
}
});
afterAll(async () => {
try {
await mongoose.disconnect();
} catch (error) {
console.log(error);
}
});
It means that we will establish a connection to the database FOR EVERY TEST FILE BEFORE ALL TESTS IN THAT FILE and close connection after all tests in every test file.
What I realized for myself:
In real life we have many test files and not every file needs a connection to a database.
It's perfectly fine to open a connection to a database in files which need a connection and close after all tests in that file, for example integration tests when we test API endpoints.
In other tests to not use real database for many unit tests we can consider to mock(simulate) a database. It's another very interesting topic 😊
If I say something wrong you can correct me
P.S
I also want to mention what is written in the Mongoose documentation
Do not use globalSetup to call mongoose.connect() or
mongoose.createConnection(). Jest runs globalSetup in a separate
environment, so you cannot use any connections you create in
globalSetup in your tests.
https://mongoosejs.com/docs/jest.html
Why I have This error:
TypeError: node_telegram_bot_api_1.default is not a constructor
This is My Code in TypeScript:
import * as dotenv from 'dotenv';
dotenv.config({ path: __dirname + '/.env'})
console.log('Hello TypeScript')
import TelegramBot from 'node-telegram-bot-api';
const bot = new TelegramBot(process.env.BOT_TOKEN, {polling: true});
And This is My Output Code after Compile:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const dotenv = require("dotenv");
dotenv.config({ path: __dirname + '/.env' });
console.log('Hello TypeScript');
const node_telegram_bot_api_1 = require("node-telegram-bot-api");
const bot = new node_telegram_bot_api_1.default(process.env.BOT_TOKEN, { polling: true });
I have the same problem and I solved it by replacing
const Telegraf = require('telegraf')
with
const { Telegraf } = require('telegraf')
It seems that the import is incorrectly done. The documentation of node-telegram-bot-api says that the import needs to be done as follows:
const TelegramBot = require('node-telegram-bot-api');
This means that the whole module is being imported, which translates to ES6 import as follows:
import * as TelegramBot from 'node-telegram-bot-api';
For different syntax and semantics of import please refer this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import