Jest and supertest: Test keep exceeding timeout - node.js

Hello I am a bit confused by this error I have encountered.
I am working on an Universal React App using Webpack 5 and Express.
I want to implement Jest support by using the React-testing-Library for the frontend (which work) and supertest for the backend (this is where I am blocked).
I am following this basic tutorial recommended by the jest doc himself in order to use jest on an node express environment.
But everytime I get this error:
thrown: "Exceeded timeout of 5000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
Here are my code:
server.js
import app from './app.js';
import { mongooseConnection, disconnectMongoDB } from "./routers/services/url/urlDB.js"; // we call mongooseConnect var to connect only once into the mongoDB database
const PORT = process.env.PORT || 8080;
// the server listen on the port set by node on localhost.
app.listen(PORT, () => {
console.log(
`Server listening on \x1b[42m\x1b[1mhttp://localhost:${PORT}\x1b[0m in \x1b[41m${process.env.NODE_ENV}\x1b[0m`,
);
});
// when when we shut down the app we execute a callback function before closing the server
process.on('exit', function() {
disconnectMongoDB();
});
app.js
import express from 'express';
import path from 'path';
import cors from 'cors';
import {envIsProduction, envIsDevelopment} from './envmode/envUtil.js';
import { enableHMR } from './reload/hotReload.js';
let app = express();
// if we have set the environnent on production then:
if (envIsProduction()) {
console.log(" _______________________________________ ");
console.log("| |");
console.log("| ( PRODUCTION ) |");
console.log("|_______________________________________|");
console.log(" ");
app.use(express.static(path.join(__dirname,'../client'))); // we serve static file like the bundle-app.js to the browser from the current directory where the server is executed and we move to the top root to access the file
}
else if (envIsDevelopment()) {
console.log(" _______________________________________ ");
console.log("| |");
console.log("| ( DEVELOPMENT ) |");
console.log("|_______________________________________|");
console.log(" ");
enableHMR(app); // we enable the Hot MPodule Reload on the frontend and the backend
}
app.use(cors());
app.use(express.urlencoded({extended:false}));
app.use(express.json());
//Hot reload!
//ALL server routes are in this module!
app.use((req, res, next) => {
require("./routers/routers")(req, res, next);
});
export default app;
routers.js
import renderPage from "./renderpage/renderPage.js";
import { serverRoutes, reactRouterRoutes, getReactRouterRoutesString } from "./routes.js";
import express from "express";
import routerLoginDB from "./request/routerLoginDB.js";
import routerSignupDB from "./request/routerSignupDB.js";
const router = express.Router();
// Put all your server routes in here
// When the user connect to the root of the server we send the page
router.get(serverRoutes.root, renderPage);
// When the user send a get request by the /click route a console.log and a respone is send.
router.get(serverRoutes.click, (req, res)=>{
res.status(200).send("Click");
});
// when this user want to login into his account, we ask for the routerLoginDB to handle it
router.post(serverRoutes.login,routerLoginDB);
// when this user want to signup into his account, we ask for the routerSignupDB to handle it
router.post(serverRoutes.signup, routerSignupDB);
// For all the routes that only react-router need to use, if we refresh on a nested route of the react-router from the client side then we redirect it to the root route "/"
router.get(reactRouterRoutes,(req,res) => {
res.redirect("/");
});
router.get("*", (req,res) =>{
res.status(404).send('page not found');
}); //For all other type of request excluding the one specified here, we send back a 404 page;
module.exports = router;
app.test.js
import request from '../utils/test-node-utils.js'
describe("Test the /click path", () => {
test("It should response the GET method", () => {
return request
.get("/click")
.expect(200);
});
});
and finally test-node-utils.js
import supertest from "supertest";
import app from "../serverside/app.js";
const request = supertest(app);
export default request;
Don't believe what the error say because I think it is more deep than that.
I have tried to increased the jest timeout value but it keep being stuck and reach the timeout limit.
I have done exactly like the tutorial say without using my project structure and it worked but when I try to implement the tutorial in my backend structure, it don't work with supertest.
I think it is related to my files or backend structure that make it don't work with the test.
Thanks in advance for your help

I've recently debugged a similar issue where my Jest tests would run successfully (or not) in my dev. environment but when I would try and package the app as a Docker image all my tests would time out.
It turned out that by commenting out the line which setup CORS, which for me I only turned on in production builds (should have been a clue), the tests started to run again when building the image.
...
const NODE_ENV = process.env.NODE_ENV;
const app = express();
NODE_ENV.toUpperCase() === 'PRODUCTION' && app.use(cors);
...
I mentioned this as I can see from your snippet above that you are also using the cors middleware and that, in your case, it's set all the time.
Perhaps not your issue, and you may want CORS in your tests for some reason, but try commenting it out and see if your tests run.

Related

Universal React: HMR for the backend (express.js)

i am been struggling for days, on this problem.
I am making an Universal React App with webpack 5.
I have enabled HMR on the client side by webpack-dev-middleware and webpack-hot-middleware
Now i want to do the same things for the backend witch is an express server.
I have tried nodemon but my biggest complain is that does auto restart the server and it doesn't match well with webpack-hot-middleware.
Then I have found webpack-hot-server-middleware witch just what i am looking for but it's not working and it is no longer maintained.
It is stuck in an infinite loop because of webpack-dev-middleware.
I have found this amazing github example linked by webpack-hot-middleware himseld on the troubleshooting section.
But again it is not quite working.
The longer it stay on this problem, I realise how much complex I try to accomplish.
Because I am on a webpack project where there two configuration (development and production) and each one does build theirs bundles.
I launch my node server on the server bundle and I want this to reload when I change the source code witch is in a another directory.
webpack-dev-middleware and webpack-hot-middleware does the job on the frontend because they both use the webpack configurations as a parameter but on backend it is much harder.
Here is what I have done so far:
Here is my main express server file:
server.js
import express from 'express';
import path from 'path';
import cors from 'cors';
import routerPage from './renderpage/routerPage.js';
import {envIsProduction, envIsDevelopment} from './envmode/envUtil.js';
import chokidar from 'chokidar';
const PORT = process.env.PORT || 8080;
let server = express();
if (envIsProduction()) {
console.log(" _______________________________________ ");
console.log("| |");
console.log("| ( PRODUCTION ) |");
console.log("|_______________________________________|");
server.use(express.static(path.join(__dirname,'..'))); // we serve static file like the bundle-app.js to the browser from the current directory where the server is executed and we move to the top root to access the file
}
else if (envIsDevelopment()) {
console.log(" _______________________________________ ");
console.log("| |");
console.log("| ( DEVELOPMENT ) |");
console.log("|_______________________________________|");
// TODO: Régler cohabitation webpack-dev-middleware qui rafraichit en boucle
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackHotServerMiddleware = require('webpack-hot-server-middleware');
const config = require('../../webpack.conf.dev.js');
const compiler = webpack(config);
server.use(webpackDevMiddleware(compiler, {
serverSideRender: true,
}));
server.use(webpackHotMiddleware(compiler.compilers.find(compiler => compiler.name === 'client')));
// Do "hot-reloading" of express stuff on the server
// Throw away cached modules and re-require next time
// Ensure there's no important state in there!
const watcher = chokidar.watch('.');
watcher.on('ready', function() {
watcher.on('all', function() {
console.log("OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO Clearing /server/ module cache from server");
Object.keys(require.cache).forEach(function(id) {
console.log(id)
console.log("exist: " + /.[\/\\]/.test(id));
if (/.[\/\\]/.test(id)) delete require.cache[id];
});
});
});
// Do "hot-reloading" of react stuff on the server
// Throw away the cached client modules and let them be re-required next time
compiler.hooks.done.tap('done', (stats) => {
console.log("Clearing /client/ module cache from server");
Object.keys(require.cache).forEach(function(id) {
if (/[\/\\]client[\/\\]/.test(id)) delete require.cache[id];
});
})
}
server.use(cors());
server.use(express.urlencoded({extended:false}));
server.use(express.json());
server.get('/', routerPage); // when the user connect to the root of the server we send the page
server.get('/click', (req, res)=>{
console.log("IUHpzeijgàzhgzieghziêhg");
res.send("I got ur six");
});
server.get("*",function(req,res){
res.status(404).send('page not found');
}); //For all other type of request excluding the one specified here, we send back a 404 page;
server.listen(PORT, () => {
console.log(
`Server listening on \x1b[42m\x1b[1mhttp://localhost:${PORT}\x1b[0m in \x1b[41m${process.env.NODE_ENV}\x1b[0m`,
);
});
I use chokidar to watch all the file on the current directory including the file where the code is and I clear the cache to refresh the server.
Here is the tree structure of my project:
As you can see, I launch node on the bundle-server.js file but I want reload to occur when I change all the file in the serverside directory
But even the console appear, nothing change when I check.
Do you have any advices?
How can I make HMR work on the server side?
Thanks in advance for your responses.

Proxy error: Could not proxy request /users from localhost:3000 to http://localhost:5000/

I am trying to create a simple react app with node/express for the backend. When I start my app I get this error:
Proxy error: Could not proxy request /users from localhost:3000 to http://localhost:5000/.
See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNREFUSED).
My front-end package.json looks like this
The front-end is pretty simple. fetchUserData() under useEffect() is what calls the backend.
import React, { useEffect, useState } from "react";
function App() {
let [userData, setUserData] = useState([{}]);
useEffect(() => {
const fetchUserData = async () => {
const response = await fetch(`/users`);
const data = await response.json();
setUserData(data);
};
fetchUserData();
}, []);
return (
<div>
<h1>Hello React World!</h1>
</div>
);
}
export default App;
The backend is pretty barebone as I just started this project. I have no problem getting the correct response if I just request http://localhost:5000/users directly from the browser or postman:
const express = require("express");
const app = express();
const port = process.env.PORT || 5000;
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get("/users", (req, res) => {
console.log(req);
res.json({ users: ["Bob", "Sally"] });
});
app.listen(port, () => {
console.log(`Listening on port ${port}`);
});
The error message appears when I try to load the front end on http://localhost:3000 which should then fetch the backend resource. I look at the network tab of chrome and for some reason it is going to port 3000 instead of 5000:
I've tried the below but no luck:
Closing my app. Deleting package-lock.json and node_modules, reinstalling them.
Add '/' to the end of the string for the "proxy" setting in package.json
Replacing localhost with 127.0.0.1 in package.json
Tried adding "--ignore client" to dev script in server package.json
Tried adding "secure": false in client package.json
Edit 1: This issue is driving me crazy. If I remove the proxy from package.json, add cors to the server side, and use the absolute path of the endpoint instead of the relative path in my client-side fetch request, it works. But I would much rather use the relative path.
import React, { useEffect, useState } from "react";
function App() {
let [userData, setUserData] = useState([{}]);
useEffect(() => {
const fetchUserData = async () => {
const response = await fetch(`http://localhost:5000/users`);
const data = await response.json();
setUserData(data);
};
fetchUserData();
}, []);
return (
<div>
<h1>Hello React World!</h1>
</div>
);
}
export default App;
Just don't use 'localhost'. Put everything as 127.0.0.1
Font: hours trying every solution possible.
From trying to replicate your issue I think in your proxy you have "proxy": "http://localhost:5000/", but then you also pass a fetch request for "/users" which would lead to a "http://localhost:5000//users.
I would imagine that when you were trying to figure this out that the issue was that you didn't restart your React App after changing the package.json to include the Proxy, and then by the time you restarted the React App, you had already added the extra "/".
Also in your browser console.log when, no matter where your proxy is it will come up with http://localhost:3000 as the address rather than your actual endpoint - it can be a bit of a red herring
Hope it helps
I finally got it to work!
After a lot of experimenting, I realized that this was indeed an environment problem. That explains a lot since the many other suggestions I tried worked for other people but not for me.
What had happened was that my client-side was using wsl for the terminal yet my backend was using PowerShell. When I switched both to wsl it worked. Or when I switched both to Powershell it also worked.

What is the correct order of requiring and mocking files using Jest?

I'm trying to create an integration test using Jest for my Express app. I think I have a conceptual misunderstanding as my tests are behaving strangely. My goal is to test the following scenario. I'm hitting a specific endpoint using Supertest, and I want to check whether an error handler middleware is called if there is a mocked error. I want to check whether the error handler is not called, if there is no error present. I have the following test file:
test.js
const request = require('supertest')
describe('Error handler', () => {
let server
let router
beforeEach(() => {
jest.resetModules()
jest.resetAllMocks()
})
afterEach(async () => {
await server.close()
})
it('should be triggered if there is a router error', async () => {
jest.mock('../../routes/')
router = require('../../routes/')
router.mockImplementation(() => {
throw new Error()
})
server = require('../../server')
const res = await request(server)
.get('')
.expect(500)
.expect('Content-Type', /json/)
expect(res.body.error).toBe('Error')
expect(res.body.message).toBe('Something went wrong!')
expect(res.body.status).toBe(500 )
})
it('should not be triggered if there is no router error', async () => {
server = require('../../server')
const res = await request(server)
.get('')
.expect(201)
.expect('Content-Type', /text/)
})
})
What I think is happening is the following. Before each test I reset all modules, because I don't want to have the cached version of my server from the first require, I want to overwrite it. I also reset all mocks, so when the second test runs, no mock is used, no fake error is forced, so the middleware is not called and I'm getting back a vanilla 200 result.
After this is done, I start testing the scenario when there is an error. I mock the routes file that exports my routes so I can force a fake error. Then I require the server, this way, I suppose, it's loading the server up with the fake, error throwing route. Then I wait for the response with Supertest, and assert that I indeed got an error back - hence the error handler middleware has been triggered and worked.
The afterEach hook is called, the server is closed, then the beforeEach hook initializes everything, again. Now I have my vanilla implementation without the mock. I require my server, hit the homepage with a get request, and I get back the correct response.
The strange thing is that for some reason the second test seems to not exit gracefully. If I change my implementation from async - await in the second test, to specify the done callback, and then if I call it at the end of the test, it seems to be working.
I tried a lot of possible permutations, including putting the mocking part to the beforeEach hook, starting the server before / after mocking, and I got weird results. I feel like I have conceptual misunderstandings, but I don't know where, because there are so many moving parts.
Any help to make me understand what is wrong would be greatly appreciated
EDIT:
I thought that most parts can be considered a black box, but now I realize that the fact that I'm trying to create an app using Socket.IO makes the setup process a bit more convoluted.
I don't want Express to automatically create a server for me, because I want to use socketIO. So for now I only create a function with the appropiate signature, and that is 'app'. This can be given as an argument to http.Server(). I configure it with options and the middlewares that I want to use. I do not want to call app.listen, because that way Socket.IO could not do its own thing.
config.js
const path = require('path')
const express = require('express')
const indexRouter = require('./routes/')
const errorHandler = require('./middlewares/express/errorHandler')
const app = express()
app.set('views', path.join(__dirname + '/views'))
app.set('view engine', 'ejs')
app.use(express.static('public'))
app.use('', indexRouter)
app.use(errorHandler)
module.exports = app
In server.js I require this app, and then I create a HTTP server using it. After that, I feed it to 'socket.io', so it is connected to the proper instance. In server.js I do not call server.listen, I want to export it to a file that actually starts up the server (index.js) and I want to export it to my tests, so Supertest can spin it up.
server.js
// App is an Express server set up to use specific middlewares
const app = require('./config')
// Create a server instance so it can be used by to SocketIO
const server = require('http').Server(app)
const io = require('socket.io')(server)
const logger = require('./utils/logger')
const Game = require('./service/game')
const game = new Game()
io.on('connection', (socket) => {
logger.info(`There is a new connection! Socket ID: ${socket.id}`)
// If this is the first connection in the game, start the clock
if (!game.clockStarted) {
game.startClock(io)
game.clockStarted = true
}
game.addPlayer(socket)
socket.on('increaseTime', game.increaseTime.bind(game))
})
module.exports = server
If I understand everything correctly, basically the same thing happens, expect for a few additional steps in the example that you provided. There is no need to start the server, and then use Supertest on it, Supertest handles the process of starting up the server when I use request(server).get, etc.
EDIT 2
Right now I'm not sure whether mocking like that is enough. Some mysterious things leaves the Supertest requests hanging, and it might be that somewhere along the way it can not be ended, although I do not see why would that be the case. Anyway, here is the router:
routes/index.js
const express = require('express')
const router = express.Router()
router.get('', (req, res, next) => {
try {
res.status(200).render('../views/')
} catch (error) {
next(error)
}
})
router.get('*', (req, res, next) => {
try {
res.status(404).render('../views/not-found')
} catch (error) {
next(error)
}
})
module.exports = router
The order of requiring and mocking is correct but the order of setting up and shutting down a server probably isn't.
A safe way is to make sure the server is available before doing requests. Since Node http is asynchronous and callback-based, errors cannot be expected to be handled in async functions without promisification. Considering that server.listen(...) was called in server.js, it can be:
...
server = require('../../server')
expect(server.listening).toBe(true);
await new Promise((resolve, reject) => {
server.once('listening', resolve).once('error', reject);
});
const res = await request(server)
...
close is asynchronous and doesn't return a promise so there's nothing to await. Since it's in a dedicated block, a short way is to use done callback:
afterEach(done => {
server.close(done)
})
In case errors are suppressed in error listener, server.on('error', console.error) can make troubleshooting easier.
Supertest can handle server creation itself:
You may pass an http.Server, or a Function to request() - if the server is not already listening for connections then it is bound to an ephemeral port for you so there is no need to keep track of ports.
And can be provided with Express instance instead of Node server, this eliminates the need to handle server instances manually:
await request(app)
...

In what file do we connect react front end folder files with back-end folder files with server.js and mysql database connection?

I have front-end folder files with react components and front-end css libraries.
In different folder, I have back-end files with server.js routing with mysql connection.
When I enter inputs on the screen, the inputs are not saved to mysql database.
Questions:
In what file, do I connect my front-end with back-end?
What statement should I use to connect my front-end with back-end?
To start Front-end, I used: npm start and To start Back-end, I used: nodemon server.js.
Question: When I connect front-end and back-end, what file should I open so that the front-end talks with the back-end -> both are starting?
Question 1 answer
You can do this a number of ways. I've done this with Serverjs and react in the
following manner.
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const port = 80085;
const app = express();
const token = 'impossiblylongimportanttokenhere';
let nextId = 7;
You'll need to import CORS, a Parser, Express for routing purposes.
Assuming you'll have a login you'll want a authenticator function. Please note this is just for development.
function authenticator(req, res, next) {
const { authorization } = req.headers;
if (authorization === token) {
next();
} else {
res.status(403).json({ error: 'User must be logged in to do that.' });
}
}
You'll probably want to replace that with something more secure once you're don with development.
with app as our router express has several CORS methods to get us started likepostget
Here's an example of one if we had an array called friends and if we wanted to find that friend by ID.
app.get('/api/friends/:id', authenticator, (req, res) => {
const friend = friends.find(f => f.id == req.params.id);
if (friend) {
res.status(200).json(friend);
} else {
res.status(404).send({ msg: 'Friend not found' });
}
});
The best part is that express has a method called listen that will start as soon as we hit NPM RUN on a cli that's parked in the same file location as server.js. Make sure to specify a port that your system isn't already using
app.listen(port, () => {
console.log(`server listening on port ${port}`);
});
Question 2
In order to get connect to Server.js on our side you'll want use axios to make a GET/POST etc. call to whatever route you've made in your server.js above in the example it would be
.post('/api/friends', this.state.addFriend)
The biggest thing is you'll want multiple terminals running in order to have both the backend and the front end running at the same time. Start with backend first.

Express API Test with Jest and Mongoose

I am working on an API server in express with using mongoose to connect to a MongoDB.
node kicks off index.js which starts an app.js where I do the MongoDB setup and the express setup.
Works fine in normal development, but when I try to use jest to test the APIs I have issues with the mongoose finishing it's connection after the test is complete, and thus jest gives me the following error.
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't
stopped in your tests. Consider running Jest with
--detectOpenHandles to troubleshoot this issue.
I know logically it is due to node.js's async nature. I just don't know how to fix it. I don't know if the solution is restructuring the way I'm running the calls or doing something with promises (which I'm still really weak on).
So how can I fix this issue?
App.js posted below with the connect string altered for public posting.
app.js
import express from 'express';
import path from 'path';
import logger from 'morgan';
import bodyParser from 'body-parser';
import cors from 'cors';
import mongoose from 'mongoose';
import fs from 'fs';
//Bring DB Online
console.log("Brining MongoDB Online")
mongoose.set('debug', true)
const devurl = 'mongodb://mlabuname:mlabpass#ds247170.mlab.com:47170/devdb'
mongoose.connect(devurl, { useNewUrlParser:true }, function() {
console.log('\tMongoDB - Connection has been made');
})
.catch(err => {
console.error('\tApp starting error:', err.stack);
process.exit(1);
});
//Load Models
console.log("\tLoading Mongoose Models")
import User from './models/user'
import Wiki from './models/wiki'
//Express Items
console.log("Brining Express Online")
const app = express();
app.disable('x-powered-by');
app.use(cors());
// View engine setup
app.set('views', path.join(__dirname, '../views'));
app.set('view engine', 'pug');
app.use(logger('dev', {
skip: () => app.get('env') === 'test'
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, '../public')));
// Routes
// Dynamic routes setup by controllers
console.log("\tInstalling Controller Routes")
fs.readdirSync("src/controllers").forEach(function (file) {
if(file.substr(-3) == ".js") {
const route = require("./controllers/" + file)
route.controller(app)
}
})
// Temp ResetDB Route for DEV.
console.log("\tInstalling ResetDB Route")
import resetDB from './routes/resetDB'
app.use('/resetDB', resetDB);
// Default Routes set by the framework, fine for now.
console.log("\tInstalling Default")
import defaultRoutes from './routes/defaultRoutes'
app.use('/', defaultRoutes);
// Catch 404 and forward to error handler
app.use((req, res, next) => {
const err = new Error('Not Found');
err.status = 404;
next(err);
});
// Error handler
app.use((err, req, res, next) => { // eslint-disable-line no-unused-vars
res
.status(err.status || 500)
.render('error', {
message: err.message
});
});
export default app;
Update
I have been pointed to this being a duplicate. I saw that answer and it isn't exactly the same issue. First they are actually testing the model. In this case I am not. I'm simply trying to run the test on the express routes, and the error is showing up after the test ran (and passed).
All that is happening is the system is brining up the MongoDB it isn't doing any transactions to it as part of the test. (As the other question talks about).
Finally in that example they are trying to bring up the mongoose db in the test itself. I'm brining it up as part of the app startup (as it works when I run the routes in dev).
"scripts": {
"test": "jest --watchAll --detectOpenHandles --forceExit"
}
In package.json you can re-write the test script.

Resources