TCPSERVERWRAP error can not let jest close after completion of test - javascript

I have written unit tests using jest. In unit test I am opening one websocket server using http server.
I am getting following error -
TCPSERVERWARP
beforeAll(async () => {
> 27 | server = new http.Server().listen(5000);
| ^
28 | wss = new WebSocketServer({ server: server });
29 | await wss.on('connection',async()=>{
30 | await wss.on('close',()=>{})
This is my test suite -
import http from 'http';
import WebSocket, { WebSocketServer } from "ws";
describe("test", () => {
beforeAll(async () => {
server = new http.Server().listen(5000);
wss = new WebSocketServer({ server: server });
await wss.on('connection',async()=>{
await wss.on('close',()=>{})
await wss.on('error',()=>{})
})
});
afterEach(() => {
jest.clearAllMocks();
jest.resetModules();
});
afterAll( () => {
jest.resetAllMocks();
wss.close();
})
it("some test", ()=>{
// test details
});
})
I got this message in the console -
Jest has detected the following 1 open handle potentially keeping Jest from exiting:
I need websocket server for my test as the class on which I am writing unit tests, needs one websocket server so I need to open one websocket server in local.
This is the error message without --detectOpenHandles option-
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.

Related

Mocha and Chai test never starts when running an Express app

I'm trying to test an Express API with Mocha and Chai.
My structure is as follows:
server.js
const express = require('express');
...
const loadCredentials = () => ({
...
})
const application = () => {
console.log('This is a log i should always see');
const app = express();
app.use('/api', authentication, authorization('#data/accounts'), router);
...
return app;
};
if (require.main === module) {
application.listen(443)
...
}
module.exports = { application };
test.js
const { application } = require('../server/src/server');
describe('Some async test', async () => {
it(, async () => {
console.log('I should really see this log!!');
server = application();
const res = await chai.request(server).get('/api');
...test stuff...
}
}
When I lerna run test (which runs mocha ./test.js --timeout 60000) the test never executes.
lerna notice cli v3.10.7
lerna info Executing command in 1 package: "yarn run test"
However, if I disable the call to application, the test starts (and fails because server is undefined).
I also tried refactoring application and passing an express() parameter to application(app) from the test, and I get the same behavior.
The test runs if I run it from WebStorm as an individual test.
Needless to say the server works when I yarn run it.
Any help would be appreciated.

nodejs MySQL - Server requests authentication using unknown plugin

When attempting to connect to MySQL 8.0.21 server running Ubuntu 20.04 using NodeJS and mysql2 package, I receive the common error below: Server requests authentication using unknown plugin sha256_password I know that mysqljs and mysql2 do not support sha256, so I confirmed my user was setup for mysql_native_password:
ALTER USER 'userName'#'%' IDENTIFIED WITH mysql_native_password BY 'password';
And have confirmed that default_authentication_plugin is set as mysql_native_password.
What makes this a strange issue, is that it only occurs when attempting to unit test the function in Mocha or Jest. When running the app normally, I am able to connect and make DB calls with no issues. To simplify troubleshooting, I created a new app.js file that only calls the dbQuery.getRow() function. Contents of those files and the output is given below.
app.js
(async function main () {
require('dotenv').config({ path: __dirname + '/config/.env' });
const dbQuery = require('./src/js/dbQuery');
let result = await dbQuery.getRow('table', 'c9024a7aead711eab20be6a68ff5219c');
console.log(result);
})();
dbQuery.js
const dbPool = require('./dbPool');
async function getRow(tableName, guid) {
try {
let sql = `
SELECT *
FROM \`${tableName}\`
WHERE guid='${guid}'`;
let [rows] = await dbPool.execute(sql);
return rows[0];
} catch (ex) {
console.log('dbQuery getRow failed with error: ' + ex);
return { error: true, message: ex };
}
}
dbPool.js
const { env } = require('process');
const mysql = require('mysql2/promise');
const dbPool = mysql.createPool({
host: env.DB_HOST,
port: env.DB_PORT,
database: env.DB_NAME,
user: env.DB_USER,
password: env.DB_PW,
// waitForConnections: env.WAIT_FOR_CONNECTIONS.toUpperCase() == 'TRUE' ? true : false,
connectTimeout: 10000,
connectionLimit: parseInt(env.CONNECTION_LIMIT),
queueLimit: parseInt(env.QUEUE_LIMIT)
});
module.exports = dbPool;
Terminal Output - Running the simplified app now returns the row as expected
node app.js
BinaryRow {
guid: 'c9024a7aead711eab20be6a68ff5219c',
name: 'spiffyRow',
displayValue: 'Spiffy Display Value'
}
However, when I attempt to do the same DB call in either Jest or Mocha, I run into the issue again, where it appears mysql2 is attempting to use the wrong authentication plugin.
dbQuery.test.js - currently setup for Mocha, but Jest exposed the same issue
const dbQuery = require('../src/js/dbQuery');
describe('MySQL DB Operations', function () {
describe('#getRow()', function () {
it('Should return row with guid specified', async function (done) {
let result = await dbQuery.getRow('table', 'c9024a7aead711eab20be6a68ff5219c');
if (result.guid == 'c9024a7aead711eab20be6a68ff5219c') done();
else done(result.error);
});
});
});
Terminal Output
npm test
MySQL DB Operations
#getRow()
dbQuery getRow failed with error: Error: Server requests authentication using unknown plugin sha256_password. See TODO: add plugins doco here on how to configure or author authentication plugins.
1) Should return row with guid specified
0 passing (49ms)
1 failing
Thanks in advance for any help, please let me know if any additional information is needed.
When executing the tests, my env variables were not being populated. The fix was as simple as adding require('dotenv').config({ path: 'path/to/.env' }); to my test file. I was thrown off by the error message returned by MySQL. I'm still not sure why MySQL responds stating sha256_password is requested when no credentials are provided, even when the default_auth_plugin is set to mysql_native_password, but once valid credentials were provided everything works as expected.

How to test with Jest after connecting to MongoDB?

I'm trying to set up testing for various routes in my Express server that require connectivity to my MongoDB database.
I'm not sure how to structure the Jest file in order to allow for testing. In my normal index.js file, I'm importing the app, and running app.listen within the connect .then call, like this:
const connect = require("../dbs/mongodb/connect");
connect()
.then(_ => {
app.listen(process.env.PORT, _ => logger.info('this is running')
})
.catch(_ => logger.error('The app could not connect.');
I've tried running the same setup in my test.js files, but it's not working.
For example:
const connect = require("../dbs/mongodb/connect");
const request = require("supertest");
const runTests = () => {
describe("Test the home page", () => {
test("It should give a 200 response.", async () => {
let res = await request(app).get("/");
expect(res.statusCode).toBe(200);
});
});
};
connect()
.then(_ => app.listen(process.env.PORT))
.then(runTests)
.catch(err => {
console.error(`Could not connect to mongodb`, err);
});
How is it possible to wait for a connection to MongoDB before running my tests?
So, turns out there were a few changes that I had to make. Firstly, I had to load in my .env file before running the tests. I did this by creating a jest.config.js file in the root of my project:
module.exports = {
verbose: true,
setupFiles: ["dotenv/config"]
};
Then within the actual testing suite, I'm running beforeEach to connect to the MongoDB server.
const connect = require("../dbs/mongodb/connect");
const app = require("../app");
const request = require("supertest");
beforeEach(async() => {
await connect();
});
describe("This is the test", () => {
test("This should work", async done => {
let res = await request(app).get("/home");
expect(res.statusCode).toBe(200);
done();
})
});

How to write integration test with Jest and Express?

I am trying to write integration tests with a third party microservice (Assume it is call Service). When a request is sent to Service, it will fire a webhook. What I am trying to do is to test it the webhook parsed correctly.
import express from "express";
import bodyParser from "body-parser";
import { handleHook} from "./hook";
export const app = express();
app.use(bodyParser.json(), handleHook);
describe("integration", () => {
test("hook", async () => {
const mock = jest.fn();
app.post("/", (req, res) => {
mock(req);
res.send("");
});
const p = new Promise<Server>(resolve => {
const server = app.listen(3000, () => {
resolve(server);
});
});
const server = await p;
await upload();
jest.useFakeTimers();
setTimeout(() => {
server.close();
}, 15000);
jest.runAllTimers();
expect(mock).toHaveBeenCalledTimes(4);
});
});
However, I got two problems. First, I got
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
but using --detectOpenHandles does not show how to troubleshoot this issue.
Second, because the webhook is fire asynchronously, how can I ensure to catch all of them or wait for a period of time to timeout.

How to stop webdriver without crashing node js

is it possible to stop the selenium webdriver without stopping node?
I have following issue:
I try to create a API tool that does some web automation when getting a get request. So I am basically running selenium webdriver on a get request to /start via Express. I want the tool to check for different elements and when it somehow fails I want it to stop selenium but NOT the node instance (since a new get request could be send).
This is my code so far:
"use strict";
const webdriver = require('selenium-webdriver'),
Express = require('express'),
By = webdriver.By,
until = webdriver.until,
Keys = webdriver.Key,
app = new Express();
app.get("/automate", (req, res) => {
start(res);
});
function start(res) {
const driver = new webdriver.Builder().forBrowser('chrome').build();
driver.get('https://www.google.com/');
// # --- foo --- #
let errMessage = {pos: "FOO", message: "Ooops friendly robot has some troubles!"}
checkStep("#foo", errMessage);
driver.findElement(By.id("foo"))
.sendKeys("fooz");
// # --- bar --- #
errMessage = {pos: "BAR", message: "Ooops friendly robot has some troubles!"}
checkStep("#bar", errMessage);
driver.findElement(By.id("bar"))
.sendKeys("baz");
// etc…
function checkStep(selector, errMessage) {
driver.findElement(By.css(selector))
.then(() => {
console.log(`${selector} => found`);
})
.catch(err => {
console.log(`Error: ${err}`);
res.send(errMessage);
driver.quit();
});
}
}
app.get("*", (req, res) => {
res.send("Hello World");
});
// start the server
const port = process.env.PORT || 3000;
const env = process.env.NODE_ENV || 'production';
app.listen(port, err => {
if (err) { return console.error(err); }
console.info(`Server running on http://localhost:${port} [${env}]`);
});
it is actually working so far that when selenium do not find the element the response from the API is correct. In Selenium I get back:
{
"pos": "FOO",
"message": "Ooops friendly robot has some troubles!"
}
So far all good.
BUT unfortunately stopping selenium is also stopping Node from running.
The error I get is following:
throw error;
^
WebDriverError: no such session
(Driver info: chromedriver=2.30.477690 (c53f4ad87510ee97b5c3425a14c0e79780cdf262),platform=Ma
c OS X 10.12.5 x86_64)
at WebDriverError
Please help, thank you!
ps: I am not using webdriverio as you can see I use this package: https://www.npmjs.com/package/selenium-webdriver
Ok, I got this working. It’s a bit difficult solution but it works:
Using Child Processes
Basically, every time the app gets a get request to /automate it will now create a child process in node which runs the selenium scripts (a child process is kind of like using another thread. Here is a very good tutorial on child processes):
index.js
"use strict";
const Express = require('express');
const { spawn } = require('child_process');
const data = require('./data.json');
const app = new Express();
app.get("/automate", (req, res) => {
const child = spawn(
process.execPath,
[`${__dirname}/test.js`, JSON.stringify(data)],
{ stdio: ['inherit', 'inherit', 'inherit', 'pipe'] }
);
child.stdio[3].on('data', data => {
const response = JSON.parse(data.toString());
res.send(response);
console.log(response);
child.kill();
});
});
app.get("*", (req, res) => {
res.send("Hello World");
});
const port = process.env.PORT || 3000;
const env = process.env.NODE_ENV || 'production';
app.listen(port, err => {
if (err) { return console.error(err); }
console.info(`Server running on http://localhost:${port} [${env}]`);
});
test.js
"use strict";
// hook with argument 3, that is "pipe" from parent
const Net = require('net'),
pipe = new Net.Socket({ fd: 3 });
const data = JSON.parse(process.argv[2]);
const webdriver = require('selenium-webdriver'),
By = webdriver.By,
until = webdriver.until,
Keys = webdriver.Key;
function start() {
const driver = new webdriver.Builder().forBrowser('chrome').build();
driver.get('https://www.google.com/');
// # --- foo --- #
let errMessage = {pos: "lst-ib", message: "Ooops friendly robot has some troubles!"}
checkStep("#lst-ib")
.sendKeys("fooz");
driver.get('https://www.facebook.com/');
driver.get('https://www.google.com/');
driver.get('https://www.facebook.com/');
// # --- bar --- #
errMessage = {pos: "BAR", message: "Ooops friendly robot has some troubles!"}
checkStep("#bar")
.sendKeys("baz");
function checkStep(selector) {
driver.findElement(By.css(selector))
.then(() => {
console.log(`${selector} => found`);
})
.catch(err => {
console.log(`${selector} => not found`);
publish(errMessage);
driver.quit();
});
}
}
function publish(message) {
pipe.write(JSON.stringify(message));
}
start();
It is working like a charm: on each request opening a new child process and killing that child process if it sends some message while also responding with the message to the client. Like this you can easily have several selenium instances simultaneously.
You’re welcome.
ps: If you hate all this asyncron stuff from Selenium webdriver-sync seems like a good choice. It basically wraps the selenium code to be syncon instead of asyncron. Like that I am able to use try {} catch {} and to driver.quit(); without any errors for code that comes later. (But this comes with a disadvantage: it is actually blocking your other nodejs code.)

Categories