I have set up an express application using typescript classes and have run into a strange issue. All tests have been passing, and today when I went to update some of the routes, my tests no longer run. When I run my test script, I get this error message back in the console:
$ mocha -c --reporter spec --compilers ts:ts-node/register ./test/*.test.ts --
timeout 20000
/Users/christiantodd/Development/projects/bby-react-api/src/index.ts:8
const app: Api = new Api();
^
TypeError: Api_1.default is not a constructor
at Object.<anonymous> (/Users/christiantodd/Development/projects/bby-react-api/src/index.ts:8:18)
at Module._compile (module.js:635:30)
at Module.m._compile (/Users/christiantodd/Development/projects/bby-react-api/node_modules/ts-node/src/index.ts:392:23)
at Module._extensions..js (module.js:646:10)
at Object.require.extensions.(anonymous function) [as .ts] (/Users/christiantodd/Development/projects/bby-react-api/node_modules/ts-node/src/index.ts:395:12)
My Api.ts file looks as follows:
import * as bodyParser from 'body-parser';
import * as express from 'express';
import * as expressValidator from 'express-validator';
import * as helmet from 'helmet';
import * as morgan from 'morgan';
import * as passport from 'passport';
import * as compression from 'compression';
/* import all routers */
import BestBuyRouter from './routes/BestBuyRouter';
import UserRouter from './routes/UserRouter';
export default class Api {
/* reference to the express instance */
public express: express.Application;
/* create the express instance and attach app level middleware and routes */
constructor() {
this.express = express();
this.middleware();
this.routes();
}
/* get current environment */
public currentEnv(): string {
return this.express.get('env');
}
/* apply middleware */
private middleware(): void {
this.express.use((req, res, next) => {
/* Don't allow caching. Needed for IE support :/ */
res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
res.header('Pragma', 'no-cache');
res.header('Access-Control-Allow-Origin', '*');
res.header(
'Access-Control-Allow-Methods',
'PUT, GET, POST, DELETE, OPTIONS'
);
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept, Authorization, Access-Control-Allow-Credentials'
);
res.header('Access-Control-Allow-Credentials', 'true');
next();
});
this.express.use(compression());
this.express.use(helmet());
this.express.use(morgan('dev'));
this.express.use(bodyParser.json());
this.express.use(bodyParser.urlencoded({ extended: false }));
this.express.use(passport.initialize());
this.express.use(expressValidator());
this.express.use((err, req, res, next) => {
console.error(err);
res.status(err.status || 500).json({
message: err.message,
error: err
});
});
}
/* connect resource routers */
private routes(): void {
/* create an instance of the each of our routers */
const userRouter = new UserRouter();
const bestBuyRouter = new BestBuyRouter();
/* attach all routers to our express app */
this.express.use(userRouter.path, userRouter.router);
this.express.use(bestBuyRouter.path, bestBuyRouter.router);
}
}
and my index.ts:
import Api from './Api';
require('dotenv').config();
const mongoose = require('mongoose');
/* Set mongoose promise to native ES6 promise */
mongoose.Promise = global.Promise;
/* Instantiate our app instance */
const app: Api = new Api();
const connectOptions = {
useMongoClient: true,
keepAlive: true,
reconnectTries: Number.MAX_VALUE
};
/* Get current environment */
export const ENV = app.currentEnv();
let DATABASE_URL;
let PORT;
/* set environment variables */
if (ENV === 'production') {
DATABASE_URL = process.env.MONGODB_URI;
PORT = parseInt(process.env.PORT, 10);
} else {
DATABASE_URL = process.env.TEST_DATABASE_URL;
PORT = 3000;
}
let server;
export const runServer = async (
dbURL: string = DATABASE_URL,
port: number = PORT
) => {
try {
await mongoose.connect(dbURL, connectOptions);
await new Promise((resolve, reject) => {
server = app.express
.listen(port, () => {
console.info(`The ${ENV} server is listening on port ${port} 🤔`);
resolve();
})
.on('error', err => {
mongoose.disconnect();
reject(err);
});
});
} catch (err) {
console.error(err);
}
};
export const closeServer = async () => {
try {
await mongoose.disconnect();
await new Promise((resolve, reject) => {
console.info(`Closing server. Goodbye old friend.`);
server.close(err => (err ? reject(err) : resolve()));
});
} catch (err) {
console.error(err);
}
};
require.main === module && runServer().catch(err => console.error(err));
Lastly, my tsconfig.json
{
"compilerOptions": {
"lib": ["dom", "es7"],
"allowJs": true,
"watch": true,
"noImplicitAny": false,
"removeComments": true,
"sourceMap": false,
"target": "es6",
"module": "commonjs",
"outDir": "./lib",
"types": [
"body-parser",
"mongodb",
"mongoose",
"passport",
"node",
"nodemailer",
"mocha",
"chai",
"express",
"express-validator",
"chai-http"
],
"typeRoots": ["./node_modules/@types"]
},
"compileOnSave": true,
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "**/*.test.ts"]
}
To me it's really strange to get this behavior all of the sudden when this config has worked for me just fine in the past. I can still start my server just fine, but for some reason ts-node doesn't want to compile my *test.ts files for mocha to run my tests. Any idea what this could be?
is not a ..." errorsSo this is likely NOT going to be the answer but this question was the top result for the error I was debugging and here is a debugging tip.
I was running mocha with the following:
test/mocha.opts
--require ts-node/register
--require source-map-support/register
--watch-extensions ts
But no matter how I imported ./app I could not get classes or functions to work despite tsc compiling fine and node and mocha working fine on the compiled .js files.
import * as app from './app'
console.log({app}); // Pretty print object
As is would happen I had a Heroku project with app.json and app.ts.
The combination of mocha and ts-node were loading the .json file extension as a higher priority instead of the .ts file and Typescript won't allow me to specify a file extension. So this behaviour is different in tsc vs mocha + ts-node.
Unit tests: nyc mocha src/**/*-test.ts
Integration tests: nyc mocha test/**/*.ts
package.json
{
"nyc": {
"extension": [
".ts"
],
"include": [
"src/**/*.ts"
],
"exclude": [
"src/**/*-test.ts",
"test/**/*.ts"
],
"require": [
"ts-node/register"
],
"reporter": [
"text-summary"
],
"sourceMap": true,
"instrument": true,
"all": true
}
}
tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"target":"es2017",
"esModuleInterop":true,
// "strict": true,
"sourceMap": true,
"outDir": "dist",
"baseUrl": ".",
"types":["node"],
"rootDirs":[
"src"
]
},
"include": [
"src/**/*",
"test/**/*"
],
"exclude":[
"**/node_modules/**"
]
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With