new Builder().forBrowser('chrome').build() hangs indefinitely - node.js

I am using mocha and selenium to do end to end integration testing. I am using beforeEach and afterEach hook to initialize browser and kill it after each test. Here is my js file which has both the methods
require('chromedriver')
// globals
global.assert = require('assert')
global.driver = null
const { Builder, By, until, promise } = require('selenium-webdriver')
// setup
beforeEach(async function () {
this.timeout(utils.DEFAULT_TEST_TIMEOUT)
while (driver !== null) {
console.log('waiting for driver to quit from last test')
await utils.sleep(1000)
}
try {
driver = await new Builder().forBrowser('chrome').build()
await driver.manage().window().setSize(1600, 900)
} catch (ex) {
console.log(ex.stack)
}
})
afterEach(async function () {
this.timeout(utils.DEFAULT_TEST_TIMEOUT)
if (driver === null) {
console.log('some problem in before each of the test ' + this.currentTest.title + ' returning...')
return
}
await saveScreenShot(SCREENSHOTS_PATH + this.currentTest.parent.title, this.currentTest.title)
await driver.quit()
driver = null
})
Now when i run the test suit on jenkins i very frequently see this ("email already registered" is a test name)
βœ“ email domain not allowed (4167ms)
1) "before each" hook for "email already registered"
some problem in before each of the test email already registered returning...
39 passing (9m)
1 pending
1 failing
1) "before each" hook for "email already registered":
Error: Timeout of 60000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
The thing to notice is that once this error comes the subsequent tests do not run. My test suit has 100s of tests and in the run above it quit once it encountered this beforeEach error and didn't run next set of tests.
I have tried various things but nothing seems to work. Any pointer in debugging this will be very helpful. Thanks in advance.

The beforeEach is never resolving and causing the test to timeout. You must signify either through a resolved promise or calling a done() callback function that the beforeEach has finished.
You can do either one of the following:
wrap the body of the beforeEach in returning a new Promise as such:
beforeEach(async function () {
return new Promise(async (resolve,reject) => {
this.timeout(60000)
try {
driver = await new webdriver.Builder().forBrowser('chrome').build()
await driver.manage().window().setSize(1600, 900)
resolve()
} catch (ex) {
reject(ex.stack)
}
})
})
OR you can add the done() callback function to your beforeEach definition:
beforeEach(async function (done) {
this.timeout(60000)
try {
driver = await new webdriver.Builder().forBrowser('chrome').build()
await driver.manage().window().setSize(1600, 900)
done();
} catch (ex) {
done(ex.stack)
}
})
I didn't test these, but either of them should work.

Related

Jest in Node.js. How to test a function that executes shell commands

I have not been able to test this function with Jest, I would appreciate your help. Thank you.
/**
* #function now
* #description When a Bash script is executed, it instantly displays the responses that appear on the screen.
* #param {string} script Bash script
* #example now('echo "Hello World!"')
*/
function now(script) {
let execute = exec(script)
execute.stdout.on('data', (data) => {
if (data.charAt(data.length - 1) === '\n') {
console.log(data.toString().slice(0, -1))
} else {
console.log(data.toString())
}
})
}
Since exec executes asynchronously, you'll need to change the now function a bit in order to test it. As it's written, you don't have a way for the caller to know when now is finished. The simplest ways to change this are:
Make it return a Promise that resolves when it's done
Make it take a callback that is called when it's done
Promise
function now(script) {
return new Promise((resolve, reject) => {
let execute = exec(script)
execute.stdout.on('data', (data) => {
if (data.charAt(data.length - 1) === '\n') {
console.log(data.toString().slice(0, -1))
} else {
console.log(data.toString())
}
})
// Resolve the Promise when the shell exits
execute.on('exit', resolve);
// Error handling omitted for brevity, you can call reject() on failure
});
}
Here's the test. There are various ways to check that console.log is called. This one uses a spy but you could also inject a log function and default it to console.log.
it('logs from shell command', async () => {
jest.spyOn(console, "log");
await greet('echo "foo"');
expect(console.log).toBeCalledWith('foo');
});
Callback
function now(script, callback) {
let execute = exec(script)
execute.stdout.on('data', (data) => {
if (data.charAt(data.length - 1) === '\n') {
console.log(data.toString().slice(0, -1))
} else {
console.log(data.toString())
}
})
// Resolve the Promise when the shell exits
execute.on('exit', callback);
// Error handling omitted for brevity, you can pass an error to the callback on failure
}
The expect needs to be in the callback so the test waits until the shell has exited. Note that you need to call done for the test to finish. Promises are generally more ergonomic, but callbacks have their uses as well so I included them both.
it('logs from shell command', (done) => {
jest.spyOn(console, "log");
greet('echo "foo"', () => {
expect(console.log).toBeCalledWith('foo');
done()
});
});
Using information from #helloitsjoe I got what I was looking for.
Modify the promise so that it correctly displays the messages on the screen.
function now(script) {
return new Promise((resolve, reject) => {
let execute = exec(script)
execute.stdout.on('data', (data) => {
console.log(data.toString().slice(0, -1))
process.stdout.cursorTo(0)
})
execute.on('exit', resolve);
})
}
I use the test with the promise of #helloitsjoe, for it to work I need to install npm i -D regenerator-runtime and import it with import 'regenerator-runtime/runtime'.
Thank you very much #helloitsjoe.

NodeJs & mongodb - script does not end

I need my script to end/exit after it is finished and after several tests I thought the problem was my mongodb-class, which connection i never closed. (when I commented out the class btw it's usage, the script ran through and exited like I want it)
But after I have implemented a closing-method, my script still is alive and I don't know why?
this is my mongo-class:
const MongoClient = require('mongodb').MongoClient
class MongodbClient {
constructor(cfg) {
// CONNECT TO MONGO-ENGINE
this.client = new MongoClient(cfg.mongoUrl, { useUnifiedTopology: true });
this.client.connect();
// CONNECT TO DB
this.db = this.client.db(cfg.mongoDbName);
}
// Close connection
async end() {
this.client.close()
return new Promise((resolve, reject) => {
resolve(true)
})
}
// .. some mehtods
}
module.exports = {
MongodbClient: MongodbClient
}
in my main-script I call a function dosomething() at which end the script needs to exit:
parser.dosomething().then(async() => {
await mongo.end()
})
but the sctipt still lives? why is that?
Promise-Ception 😯😯
Your end method returns another Promise within a Promise
async end() {
/* ... */
return true
}
πŸ‘† This async function returns a Promise by itself. For async functions it's important to return something at some point.
In your dosomething method you do the correct thing and use await to resolve the Promise.
await mongo.end();
However it doesn't stop there. The first Promise (async end) returns another Promise
// ...
return new Promise((resolve, reject) => {
return resolve(true)
});
// ...
To completely resolve everything, your dosomething method should eventually do this :
const anotherPromise = await mongo.end();
await anotherPromise();
By this time you will realize that client.close() as well returns a Promise and should be resolved. The whole thing is a bit messy IMHO.
Simplify things
Try this
async end() {
try {
await this.client.close();
return true;
} catch (ex) {
throw ex;
}
}
parser.dosomething().then(async () => {
try {
const closed = await mongo.end();
console.log("connection closed");
} catch (ex) {
console.log(ex.message);
}
});
Remember to use try ... catch blocks when using async/await . If the result is still the same πŸ‘† then the problem lies somewhere else probably.
Simplify some more
end() { return this.client.close() }
Now your end method just returns the unresolved Promise from client.close. Please Note, I removed the async prefix from the end method as it is not needed.
await mongo.end();

How to use both should and done in mocha and chai with node.js

I'm just starting learning mocha and chai. And I get stuck here
const UserService = new Service();
describe("user-services.spec", () => {
describe("Services testing", () => {
before((done) => {
db();
userModel.remove({}, done);
})
after((done) => {
mongoose.connection.close();
done();
})
it("should add user to db", () => {
let id = 4;
let name = "Alen";
(async () => {
let result = await UserService.addUser(id, name);
console.log("result", result);
result.should.have.property("_i");
//done();
})();
})
})
})
Now I have two question based on above code
Is that this test case always pass even if I change "_id" to "_i" I don't know how ?
If I want to use done with above code and uncomment the done() then it gives error
Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
For your first question, I've tested using result.should.have.property("_i"); and it fails.
I've mocked the method like this:
async function addUser(id, name){
return {_id:""}
}
And it throws
Uncaught AssertionError: expected { _id: '' } to have property '_i'
As expected.
So check the value returned. Also you can check chai documentation
And for the second question, done() is the callback to say Mocha where the function is done. Also Mocha has a timeout (max. time the test will be waiting). If the maximum time is reached without a done() calling, Mocha will thrown an error saying it has not been finished (done is not called).
If you don't call done() Mocha will stuck waiting without knowing when the function is completed.
You get the error Timeout of 2000ms exceeded because 2 seconds is the default value as documentation says.
Specifies the test case timeout, defaulting to two (2) seconds (2000 milliseconds). Tests taking longer than this amount of time will be marked as failed.

Mocha test it() terminate before Promise resolved. (Timeout)

I have a function:
const createUser = (name, email) => {
const namePromise = db.findOne({name}).exec();
const emailPromise = db.findOne({email}).exec();
return Promise.all([namePromise, emailPromise])
.then(([foundName, foundEmail]) => {
if (foundName || foundEmail) {
return Promise.reject(new Error("Already exists."));
}
// In the real implementation, a new row is added to db and returns a Promise.
return Promise.resolve({uuid: "uuid1"});
})
}
And I have a mocha test:
describe('Test', function() {
it('succeed', function() {
return createUser("user1", "name1")
.then(function(result) {
// should print '{uuid: "uuid1"}'
console.log(result);
expect(result.uuid).to.equal("uuid1");
})
.catch(function(err) {
expect(err).to.equal(null);
});
});
})
npm test result
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Users/Federer-HYJ/Documents/coding/NodeJS/projects/chatroom_nodejs/backend_v2/test/models_user_test.js)
{ uuid: "uuid1" }
The correct result { uuid: "uuid1" } is printed. But timeout comes before that.
Theoretical, the returned promise from createUser will be captured in it(). Then the test will pass. But it seems it() terminates before createUser() has been resolved.
What's wrong here???

How do I properly test promises with mocha and chai?

The following test is behaving oddly:
it('Should return the exchange rates for btc_ltc', function(done) {
var pair = 'btc_ltc';
shapeshift.getRate(pair)
.then(function(data){
expect(data.pair).to.equal(pair);
expect(data.rate).to.have.length(400);
done();
})
.catch(function(err){
//this should really be `.catch` for a failed request, but
//instead it looks like chai is picking this up when a test fails
done(err);
})
});
How should I properly handle a rejected promise (and test it)?
How should I properly handle a failed test (ie: expect(data.rate).to.have.length(400);?
Here is the implementation I'm testing:
var requestp = require('request-promise');
var shapeshift = module.exports = {};
var url = 'http://shapeshift.io';
shapeshift.getRate = function(pair){
return requestp({
url: url + '/rate/' + pair,
json: true
});
};
The easiest thing to do would be to use the built in promises support Mocha has in recent versions:
it('Should return the exchange rates for btc_ltc', function() { // no done
var pair = 'btc_ltc';
// note the return
return shapeshift.getRate(pair).then(function(data){
expect(data.pair).to.equal(pair);
expect(data.rate).to.have.length(400);
});// no catch, it'll figure it out since the promise is rejected
});
Or with modern Node and async/await:
it('Should return the exchange rates for btc_ltc', async () => { // no done
const pair = 'btc_ltc';
const data = await shapeshift.getRate(pair);
expect(data.pair).to.equal(pair);
expect(data.rate).to.have.length(400);
});
Since this approach is promises end to end it is easier to test and you won't have to think about the strange cases you're thinking about like the odd done() calls everywhere.
This is an advantage Mocha has over other libraries like Jasmine at the moment. You might also want to check Chai As Promised which would make it even easier (no .then) but personally I prefer the clarity and simplicity of the current version
As already pointed out here, the newer versions of Mocha are already Promise-aware. But since the OP asked specifically about Chai, it's only fair to point out the chai-as-promised package which provides a clean syntax for testing promises:
using chai-as-promised
Here's how you can use chai-as-promised to test both resolve and reject cases for a Promise:
var chai = require('chai');
var expect = chai.expect;
var chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
...
it('resolves as promised', function() {
return expect(Promise.resolve('woof')).to.eventually.equal('woof');
});
it('rejects as promised', function() {
return expect(Promise.reject('caw')).to.be.rejectedWith('caw');
});
without chai-as-promised
To make it really clear as to what's getting tested, here's the same example coded without chai-as-promised:
it('resolves as promised', function() {
return Promise.resolve("woof")
.then(function(m) { expect(m).to.equal('woof'); })
.catch(function(m) { throw new Error('was not supposed to fail'); })
;
});
it('rejects as promised', function() {
return Promise.reject("caw")
.then(function(m) { throw new Error('was not supposed to succeed'); })
.catch(function(m) { expect(m).to.equal('caw'); })
;
});
Here's my take:
using async/await
not needing extra chai modules
avoiding the catch issue, #TheCrazyProgrammer pointed out above
A delayed promise function, that fails, if given a delay of 0:
const timeoutPromise = (time) => {
return new Promise((resolve, reject) => {
if (time === 0)
reject({ 'message': 'invalid time 0' })
setTimeout(() => resolve('done', time))
})
}
// ↓ ↓ ↓
it('promise selftest', async () => {
// positive test
let r = await timeoutPromise(500)
assert.equal(r, 'done')
// negative test
try {
await timeoutPromise(0)
// a failing assert here is a bad idea, since it would lead into the catch clause…
} catch (err) {
// optional, check for specific error (or error.type, error. message to contain …)
assert.deepEqual(err, { 'message': 'invalid time 0' })
return // this is important
}
assert.isOk(false, 'timeOut must throw')
log('last')
})
Positive test is rather simple. Unexpected failure (simulate by 500β†’0) will fail the test automatically, as rejected promise escalates.
Negative test uses the try-catch-idea. However: 'complaining' about an undesired pass happens only after the catch clause (that way, it does not end up in the catch() clause, triggering further but misleading errors.
For this strategy to work, one must return the test from the catch clause. If you want't to test anything else, use another it()-block.
Thre is a better solution. Just return the error with done in a catch block.
// ...
it('fail', (done) => {
// any async call that will return a Promise
ajaxJson({})
.then((req) => {
expect(1).to.equal(11); //this will throw a error
done(); //this will resove the test if there is no error
}).catch((e) => {
done(e); //this will catch the thrown error
});
});
this test will fail with following message: AssertionError: expected 1 to equal 11

Resources