Avoid the task completion callback called too many times? - node.js

Given the following gulp tasks I'm able to successfully start the gulp, webpack and nodemon process, but the webpack tasks are open ended, so they will continue to fire the completion handler when their watch / compile cycle is complete.
The server task depends on the client task output, so I need these operations to be synchronous, hence the done
function onBuild(done) {
return function(err, stats) {
if(err) {
gutil.log('Error', err);
if(done) {
done();
}
} else {
Object.keys(stats.compilation.assets).forEach(function(key){
gutil.log('Webpack: output ', gutil.colors.green(key));
});
gutil.log('Webpack: ', gutil.colors.blue('finished ', stats.compilation.name));
if(done) {
done();
}
}
}
}
//dev watch
gulp.task('webpack-client-watch', function(done) {
webpack(devConfig[0]).watch(100, function(err, stats) {
onBuild(done)(err, stats);
});
});
gulp.task('webpack-server-watch', function(done) {
webpack(devConfig[1]).watch(100, function(err, stats) {
onBuild(done)(err, stats);
nodemon.restart();
});
});
gulp.task('webpack-watch',function(callback) {
runSequence(
'webpack-client-watch',
'webpack-server-watch',
callback
);
});
gulp.task('nodemon', ['webpack-watch'], function() {
nodemon({
script: path.join('server/dist/index.js'),
//ignore everything
ignore: ['*'],
watch: ['foo/'],
ext: 'noop'
}).on('restart', function() {
gutil.log(gutil.colors.cyan('Restarted'));
});
});
When I change a file, the watcher does its thing and gulp complains about the callback being called yet again.
[15:00:25] Error: task completion callback called too many times
I've looked at this, but not sure if its applicable.
Why might I be getting "task completion callback called too many times" in gulp?
Basically, I just want this to work synchronously and continuously without error.
gulp nodemon

This solved it for me: Just don't call the callback parameter in your webpack-watch task. Leave it out completely.
After that, the watcher works fine and fast without complaining.

If public folder exists in your application. Please remove and re-run, after you can see this issue resolved.

Related

NodeJS mailparser not being executed

I am new to NodeJS. The following code snippet from my script is not executing nor it is logging any errors.
console.log('Process attachment');
const simpleParser = require('mailparser').simpleParser;
console.log('Process attachment');
simpleParser(data.Body, (err, mail) => {
if (err) {
console.log('attachment error');
console.log(err)
callback(null, null);
} else {
console.log('attachment success');
console.log(mail)
console.log(mail.attachments[0])
console.log(mail.attachments[0].content)
console.log(mail.attachments[0].content.toString('ascii'))
callback(null, null);
}
})
console.log('Exit');
process.exit();
Process attachment and Exit are being logged in the console but for some reason the code never goes in either the if or the else. So it looks like the simpleParser function is not being executed for some reason. data.Body contains a full email body. Is there anything obvious i am missing ? Thanks.
Why don't you use promises instead ?
This will work
simpleParser(data.Body).then(mail=>{
console.log('attachment success');
console.log(mail)
console.log(mail.attachments[0])
console.log(mail.attachments[0].content)
console.log(mail.attachments[0].content.toString('ascii'))
}).then(()=>{
console.log('Exit');
process.exit();
}).catch(err=>{
console.log('attachment error');
console.log(err);
})
And if you want to make it look simpler, cleaner use Async/Await like this
const parseMail = async ()=>{
try {
let mail = await simpleParser(data.Body);
console.log('attachment success');
console.log(mail)
console.log(mail.attachments[0])
console.log(mail.attachments[0].content)
console.log(mail.attachments[0].content.toString('ascii'))
}
catch(err) {
console.log('attachment error');
console.log(err);
}
console.log('Exit');
process.exit();
}
You are terminating the script prematurely.
simpleParser is being executed asynchronously. Therefore this bit console.log('Exit'); process.exit(); is being called before your simpleParser has finished.
We experienced this exact same behavior. The above responders are correct in that the console.log commands are not logging anything within the mailparser function because it is running asynchronously and the calling function is exiting without waiting on the mailparser to do its thing.
The simple solution for us was to just call the mailparser with await so the calling function waits on mailparser to complete before it continues.
So, instead of:
simpleParser(data.Body, (err, mail) => {console.log('message');});
Try this:
let mail = await simpleParser(data.Body);
if (mail != null) {console.log('message');};
For what it's worth, I think the asynchronous simpleParser function without the await should still be running through its internal code. It's just the logging messages won't be recorded as the calling function may have exited at the time.

Webdriver.io - how to use beforeEach hooks in the config

I am building an app using the MEAN stack and Webdriver for testing.
At the moment I am cleaning the database between tests by using Mocha's beforeEach and afterEach hooks, e.g.:
describe('Links', function() {
// drop DB collections
beforeEach(function(done){
//create database objects
});
afterEach(function(done){
// drop DB collections
});
});
Is there a way of setting up wdio.conf.js for this to happen before and after each of my tests automatically? The config's before: and after: function() {} run as a beforeAll / afterAll rather than for each test.
Yes, see this url: http://webdriver.io/guide/testrunner/gettingstarted.html
if you launch the wdio config, will generate a file named wdio.conf.js, on this file exists functions to launch scripts after or before the tests, i show the example of the functions contains this file:
// =====
// Hooks
// =====
// Run functions before or after the test. If one of them returns with a promise, WebdriverIO
// will wait until that promise got resolved to continue.
//
// Gets executed before all workers get launched.
onPrepare: function() {
// do something
},
//
// Gets executed before test execution begins. At this point you will have access to all global
// variables like `browser`. It is the perfect place to define custom commands.
before: function() {
// do something
},
//
// Gets executed after all tests are done. You still have access to all global variables from
// the test.
after: function(failures, pid) {
// do something
},
//
// Gets executed after all workers got shut down and the process is about to exit. It is not
// possible to defer the end of the process using a promise.
onComplete: function() {
// do something
}
Is important, if you launch a script asynchronous, and you wait the callback in the moment of clean the database so you need a promise, else the next step hook will launch not waiting the previous function hook, example:
onPrepare: function() {
return new Promise(function(resolve, reject) {
sauceConnectLauncher({
username: 'the_pianist2',
accessKey: '67224e83a-1cf7440d-8c88-857c4d3cde49',
}, function (err, sauceConnectProcess) {
if (err) {
return reject(err);
}
console.log('success');
resolve();
});
});
},

How to test setTimeout with Mocha

I'm doing some integration testing in my Node app and at some point in my code I call the following function:
async.parallel([
function foo(callback){
setTimeout(function(){
//DO SOMETHING HERE;
callback(null, result);
}, 500);
},
function bar(callback){
//DO SOMETHING HERE;
callback(null, result);
}],
function(err, results){
//Process results here and continue
});
This code is part of a larger node app.
Now when I try to test my code with Mocha the test hangs because the timeout in foo() is never fired and therefore the parallel execution never finishes. When I remove the setTimeout the execution is completed just fine.
Here's the test code:
it("test something", function(done) {
request(app)
.post(requestUrl)
.send(testRequest)
.expect(200)
.end(function(err, res){
(res.body.text).should.equal('Hello World');
done();
});
});
What I tried:
Apparently, the clock is disbaled during testing so I tried using Sinon to simulate the passing of time but to no avail.
How can I solve this?
Well I had a similar problem today.
I was using var clock = sinon.useFakeTimers() without clock.restore().
To try that, you can use:
console.log(new Date().getTime());
console.log(new Date().getTime());
If you get twice the same value, it means that you may have change the clock.

Nodeunit not detecting done()

Trying to get up to speed with node.js and nodeunit but am finding an issue with nodeunit where it's not seeing the call to test.done() in one of the tests.
The code:
// Added for clarity.
var client = require("restify").createJsonClient({
"version": "*",
"url": "http://localhost:" + server.Port
});
exports["tests"] = {
"setUp": function (callback) {
server.StartServer();
callback();
},
"tearDown": function (callback) {
callback();
},
"CanIHaveSomeTeaPlease?": function (test) {
test.expect(4);
client.get("/tea", function (err, req, res, data) {
test.equal(err.statusCode, 418, "Expected ImATeapot Error.");
test.equal(err.message, "Want a biscuit?", "Expected to be asked if I want a buscuit.");
test.equal(err.restCode, "ImATeapotError");
test.equal(err.name, "ImATeapotError");
test.done();
});
},
// Note: I expect this test to fail as it is a copy of the above
// test on a different url that doesn't return the ImATeapot
// HTTP error. But it doesn't look like it's detecting it
// properly.
"TakeThisInfo": function (test) {
test.expect(4);
client.put("/push", {
"hello": "world"
}, function (err, req, res, data) {
test.equal(err.statusCode, 418, "Expected ImATeapot Error.");
test.equal(err.message, "Want a biscuit?", "Expected to be asked if I want a buscuit.");
test.equal(err.restCode, "ImATeapotError");
test.equal(err.name, "ImATeapotError");
test.done();
});
}
};
Output:
FAILURES: Undone tests (or their setups/teardowns):
- tests - TakeThisInfo
To fix this, make sure all tests call test.done()
I'm hoping it is something stupid.
Versions:-
Node: 0.10.21
NPM: 1.3.11
Nodeunit: 0.8.2
Grunt-CLI: 0.1.10
Grunt: 0.4.1
First, I don't know what "server" is in your code, but I would expect it to be asynchronous, so to have something more like this in your setUp function:
function (callback) {
server.StartServer(function(){
callback();
});
}
Second, keep present that nodeunit executes the startUp and the tearDown functions after and before EVERY test so I suspect you are starting your server 2 times (as in the tearDown you are not really closing it).
I've spent the last couple of hours messing with this issue, and what has become clear is that nodeunit has no ability to catch and display exceptions thrown in functions which are triggered later by an IO or setTimeout type process. Considering the way JavaScript runs, this isn't surprising. Everything works once you are sure there are no exceptions, but if you have an error in your code, you will get "undone tests" message and nothing else. Here is what I did to resolve my issues (using a restify route as an example):
function myRoute(req, res, next) {
try {
// your code goes here...
}
catch (err) {
// write it to the console (for unit testing)
console.log(err);
// pass the error to the next function.
next(err);
}
}
Once I understood the problem in this way, fixing it because a lot more clear and I was able to get all of my tests to pass!
I suspect you're not actually calling test.done() in that second test. Put a console.log() call in there to verify you're actually making that call.
FWIW, I repro'd the described problem using a simplified version of your test, below. If you omit the on('error', function() {...}) handler, then the 2nd test fails to complete. Thus, my theory is that your /push endpoint is triggering a different behavior in the restify module. I.e. are you sure restify is invoking your callback with an err property there, or is it doing something different? ... like, for example, emitting an event like http.get does, below.
var http = require('http');
exports.test1 = function (test) {
test.expect(1);
http.get({hostname: "www.broofa.com", path: "/"}, function (res) {
test.equal(res.statusCode, 200, 'got 200');
test.done();
});
};
exports.test2 = function (test) {
test.expect(1);
http.get({hostname: "www.no-such-domain.com", path: "/"}, function (res) {
test.equal(res.statusCode, 200, 'got 200');
test.done();
}).on('error', function() {
// Comment line below out to repro the "Undone tests" error
test.done();
});
};
I'm working around it by forking the server into it's own process in the setup and then killing it in the teardown. Think the issue was to do with the server being created and not shutdown. Thanks #matteofigus for that.
var cp = null; // child process
exports["tests"] = {
"setUp": function (callback) {
cp = fork("./lib/server.js", {"silent": true});
callback();
},
"tearDown": function (callback) {
cp.kill("SIGHUP");
callback();
},
"CanIHaveSomeTeaPlease?": function (test) {
test.expect(4);
client.get("/tea", function (err, req, res, data) {
test.equal(err.statusCode, 418, "Expected ImATeapot Error.");
test.equal(err.message, "Want a biscuit?", "Expected to be asked if I want a buscuit.");
test.equal(err.restCode, "ImATeapotError");
test.equal(err.name, "ImATeapotError");
test.done();
});
},
"TakeThisInfo": function (test) {
test.expect(1);
client.put("/push", {
"hello": "world"
}, function (err, req, res, data) {
test.ok(false);
test.done();
});
}
};

In mocha testing while calling asynchronous function how to avoid the timeout Error: timeout of 2000ms exceeded

In my node application I'm using mocha to test my code. While calling many asynchronous functions using mocha, I'm getting timeout error (Error: timeout of 2000ms exceeded.). How can I resolve this?
var module = require('../lib/myModule');
var should = require('chai').should();
describe('Testing Module', function() {
it('Save Data', function(done) {
this.timeout(15000);
var data = {
a: 'aa',
b: 'bb'
};
module.save(data, function(err, res) {
should.not.exist(err);
done();
});
});
it('Get Data By Id', function(done) {
var id = "28ca9";
module.get(id, function(err, res) {
console.log(res);
should.not.exist(err);
done();
});
});
});
You can either set the timeout when running your test:
mocha --timeout 15000
Or you can set the timeout for each suite or each test programmatically:
describe('...', function(){
this.timeout(15000);
it('...', function(done){
this.timeout(15000);
setTimeout(done, 15000);
});
});
For more info see the docs.
I find that the "solution" of just increasing the timeouts obscures what's really going on here, which is either
Your code and/or network calls are way too slow (should be sub 100 ms for a good user experience)
The assertions (tests) are failing and something is swallowing the errors before Mocha is able to act on them.
You usually encounter #2 when Mocha doesn't receive assertion errors from a callback. This is caused by some other code swallowing the exception further up the stack. The right way of dealing with this is to fix the code and not swallow the error.
When external code swallows your errors
In case it's a library function that you are unable to modify, you need to catch the assertion error and pass it onto Mocha yourself. You do this by wrapping your assertion callback in a try/catch block and pass any exceptions to the done handler.
it('should not fail', function (done) { // Pass reference here!
i_swallow_errors(function (err, result) {
try { // boilerplate to be able to get the assert failures
assert.ok(true);
assert.equal(result, 'bar');
done();
} catch (error) {
done(error);
}
});
});
This boilerplate can of course be extracted into some utility function to make the test a little more pleasing to the eye:
it('should not fail', function (done) { // Pass reference here!
i_swallow_errors(handleError(done, function (err, result) {
assert.equal(result, 'bar');
}));
});
// reusable boilerplate to be able to get the assert failures
function handleError(done, fn) {
try {
fn();
done();
} catch (error) {
done(error);
}
}
Speeding up network tests
Other than that I suggest you pick up the advice on starting to use test stubs for network calls to make tests pass without having to rely on a functioning network. Using Mocha, Chai and Sinon the tests might look something like this
describe('api tests normally involving network calls', function() {
beforeEach: function () {
this.xhr = sinon.useFakeXMLHttpRequest();
var requests = this.requests = [];
this.xhr.onCreate = function (xhr) {
requests.push(xhr);
};
},
afterEach: function () {
this.xhr.restore();
}
it("should fetch comments from server", function () {
var callback = sinon.spy();
myLib.getCommentsFor("/some/article", callback);
assertEquals(1, this.requests.length);
this.requests[0].respond(200, { "Content-Type": "application/json" },
'[{ "id": 12, "comment": "Hey there" }]');
expect(callback.calledWith([{ id: 12, comment: "Hey there" }])).to.be.true;
});
});
See Sinon's nise docs for more info.
If you are using arrow functions:
it('should do something', async () => {
// do your testing
}).timeout(15000)
A little late but someone can use this in future...You can increase your test timeout by updating scripts in your package.json with the following:
"scripts": {
"test": "test --timeout 10000" //Adjust to a value you need
}
Run your tests using the command test
For me the problem was actually the describe function,
which when provided an arrow function, causes mocha to miss the
timeout, and behave not consistently. (Using ES6)
since no promise was rejected I was getting this error all the time for different tests that were failing inside the describe block
so this how it looks when not working properly:
describe('test', () => {
assert(...)
})
and this works using the anonymous function
describe('test', function() {
assert(...)
})
Hope it helps someone, my configuration for the above:
(nodejs: 8.4.0, npm: 5.3.0, mocha: 3.3.0)
My issue was not sending the response back, so it was hanging. If you are using express make sure that res.send(data), res.json(data) or whatever the api method you wanna use is executed for the route you are testing.
Make sure to resolve/reject the promises used in the test cases, be it spies or stubs make sure they resolve/reject.

Resources