Using a node module within a Grunt Task fails - node.js

I'm trying to extract meta data from files read within a Grunt task.
executing: node test.js on this file:
var exif = require('exif2');
exif('fixtures/forest.png', function (err, o) {
console.log(arguments);
});
Produces the expected output
However, executing the grunt process: grunt projectJSON
module.exports = function (grunt) {
var exif = require('exif2');
return grunt.registerMultiTask("projectJSON", "Creates project JSON file.", function () {
exif('fixtures/forest.png', function (err, o) {
console.log(arguments);
});
});
}
** note that I am just testing with the fixtures/forest.png file
Produces no output whatsoever. The callback isn't even fired.
When I console.log exif, I get: [Function]
What am I missing? I think that the doesn't work is because of the grunt task, but I have no idea how to fix it. Wrapping it in a try-catch block produces nothing.

You need to make your projectJSON task asynchronous - Grunt is exiting before your exif callback is being invoked.
Have a look at the Grunt documentation on asynchronous tasks.
This is how you can make your task asynchronous:
module.exports = function (grunt) {
var exif = require('exif2');
grunt.registerMultiTask("projectJSON", "Creates project JSON file.", function () {
// Make task asynchronous.
var done = this.async();
exif('fixtures/forest.png', function (err, o) {
console.log(arguments);
// Invoke the task callback to continue with
// other Grunt tasks.
done();
});
});
}

Related

mocha/chai return undefined

I have a working node script and am now trying to learn mocha/chai to add some unit tests.
//Module Code
var path = require('path');
var recursive = require('recursive-readdir');
function findData(folderPath) {
recursive(folderPath, function (err, files) {
return files;
});
};
module.exports.findData = findData;
My mocha test code:
var expect = require('chai').expect;
describe('checkData', function () {
var findData = require('../custom_modules/findData').findData;
var path;
before (function () {
path = '/Data'
});
it('should have 53 files in array', function () {
expect(findData(path)).to.have.lengthOf(53);
})
});
However, it always fails because the return seems to be undefined. So i stripped my module code back to test a return true and that worked.
So it must the the asynchronous nature of the recursive module so I then tried add in callbacks to my module code:
var path = require('path');
var recursive = require('recursive-readdir');
function findData(folderPath, cb) {
recursive(folderPath, function (err, files) {
cb(null, files);
});
};
module.exports.findData = findData;
But that didn't work either.
The weird thing is if I run node index.js i get the list of files.
Can anyone explain to me how this code can work normally but when I try to use mocha/chai to test I get undefined?
Thanks
EDITED:
So based on what #Louis said in the comments I have added a callback to the function.
describe('checkData', function () {
var findData = require('../custom_modules/findData').findData;
var path;
var files;
before (function () {
path = '/Users/tjmartin/Documents/OSData/OpenNames/Data'
});
it('should have 53 files in array', function () {
expect(findData(path, function(results) {
files = results;
})).to.have.lengthOf(53);
})
});
But this still returns an undefined.
First up, I would log the err result in your findData implementation. Even if only during development, so you can easily see if any errors are reported (you may be doing this already, just wanted to mention it).
As you have spotted, one of the main causes of problems for you is that the callback is asynchronous. So you can't simply return it from your findData method as in your original example.
Next, I wouldn't hard-code the path as you have in the before function. Instead use a local path, so that the test can be run as part of your CI (if you ever have one) or even so you can grab it on another machine and have it work there.
before(function() {
path = './tests/TestData';
});
In your revised example, although you are using a callback you are testing the return result still. You need to alter your test to use the result of the callback.
it('should have 53 files in array', function(done) {
findData(path, function(results) {
expect(results).to.have.lengthOf(53);
done();
});
});

Node.js streams => Delete contents of folder before transpiling to it

I am using Gulp and I would like to delete/"clean" all contents of a directory before writing transpiled source to it.
Is there a way to connect the stream that runs the delete operation to the stream that does the read/transform/write operation?
Right now I have this:
function transpileJSX() {
var d = domain.create();
d.run(function () {
gulp.src('./public/static/app/js/views/**/*.js').pipe(react({harmony: true}))
.pipe(gulp.dest('./public/static/app/js/jsx')).on('end', function () {
d.emit('end');
d.exit();
});
});
return d;
}
function deleteJSX() {
return gulp.src('./public/static/app/js/jsx/**/*.js')
.pipe(clean({force: true}));
}
gulp.task('transpile-jsx', function () {
return transpileJSX().on('error', function (err) {
console.error(err);
});
});
gulp.task('delete-jsx', function (cb) {
return deleteJSX();
});
the problem is that this doesn't seem to work:
deleteJSX().pipe(transpileJSX());
the transpileJSX operation seems to start before the deleteJSX operation begins. And that might be expected when it comes to streams. But I think I want to make sure the whole thing works properly by ensuring the entire delete operation completes first?
For example, if I do this:
gulp.task('some-task', ['delete-jsx','transpile-jsx'], function (done) {
someTask(done);
});
the gulp logs show that the transpile-jsx task starts before the delete-jsx task ends.
If someone has a pattern to use when it comes to cleaning out a folder before transpiling to it, I'd like to copy it thanks!
You're almost there already. You just have to rearrange task dependencies, since gulp runs tasks for maximum concurrency:
gulp.task('transpile-jsx', ['delete-jsx'], function () {
//...
});
gulp.task('some-task', ['transpile-jsx'], function () {
//...
});
This will be easier once Gulp 4 hits. There you will be able to write stuff like:
gulp.task('some-task', gulp.series('delete-jsx', 'transpile-jsx', function () {
//...
}));

Yeoman generator: how to run async command after all files copied

I'm writing a yeoman generator.
I need to run some shell script after all files copied.
The generator is being called as a child generator so it should wait till script finished.
The script is some command file being run via spawn:
that.spawnCommand('createdb.cmd');
As the script depends on files created by the generator it cannot run right inside generator's methods as all copy/template action are async and have not executed yet:
MyGenerator.prototype.serverApp = function serverApp() {
if (this.useLocalDb) {
this.copy('App_Data/createdb.cmd', 'App_Data/createdb.cmd');
// here I cannot run spawn with createdb.cmd as it doesn't exist
}
}
So the only place I found where I can run spawn is the 'end' event handler:
var MyGenerator = module.exports = function MyGenerator (args, options, config) {
this.on('end', function () {
if (that.useLocalDb) {
that.spawnCommand('createdb.cmd')
}
}
}
The script runs successfully but the generator finishes earlier than the child process. I need to tell Yeoman to wait for my child process.
Something like this:
this.on('end', function (done) {
this.spawnCommand('createdb.cmd')
.on('close', function () {
done();
});
}.bind(this));
But 'end' handler doesn't have the argument with 'dine' callback.
How to do this?
UPDATE:
thanks to #SimonBoudrias I got it working.
The full working code is below.
BTW: end method is described in the docs
var MyGenerator = module.exports = yeoman.generators.Base.extend({
constructor: function (args, options, config) {
yeoman.generators.Base.apply(this, arguments);
this.appName = this.options.appName;
},
prompting : function () {
// asking user
},
writing : function () {
// copying files
},
end: function () {
var that = this;
if (this.useLocalDb) {
var done = this.async();
process.chdir('App_Data');
this.spawnCommand('createdb.cmd').on('close', function () {
that._sayGoodbay();
done();
});
process.chdir('..');
} else {
this._sayGoodbay();
}
},
_sayGoodbay: funciton () {
// final words to user
}
});
Never trigger any action in the end event. This event is to be used by implementors, not generator themselves.
In your case:
module.exports = generators.Base({
end: function () {
var done = this.async();
this.spawnCommand('createdb.cmd').on('close', done);
}
});

Unit Testing Node Script with chai-spy ... function spied on is undefined

Writing my first node.js test for my first gulp-plugin....using mocha and chai. I get TypeError: Cannot read property 'Assertion' of undefined for ar errorCheckArg = spy(errorCheckArg);. It seems the errorCheckArg function is not available in the testing enviroment(I did a console.log(errorCheckArg) and it showed undefined).
Any idea what I am doing wrong?
describe('gulp-foobar', function() {
var fakeFile;
beforeEach(function() {
var fakeFile = new File({
contents: new Buffer('abufferwiththiscontent')
});
});
describe('get files', function() {
it('should do something', function(done) {
var foo = function() { };
foobar(foo);
var errorCheckArg = spy(errorCheckArg);
expect(errorCheckArg).to.have.been.called.with(arguments);
done();
});
});
});
The Gulp plugin/node script being tested:
function foorbar(callback) {
var destinationFile;
errorCheckArg(arguments);
return through2.obj(function(file, enc, next) {
...
If errorCheckArg is a function inside of foobar, you won't be able to spy on it unless you somehow expose it to the outside world. If it looks like so:
function foobar(callback) {
errorCheckArg(arguments);
.
.
.
function errorCheckArg(args){};
}
you've made it a private function.
If it's important enough to test it in in isolation, then you'll need to expose it somehow, and you'll need to refactor your code.
Without refactoring, your option would be to test errorCheckArg based on its effects on the outputs of foobar. If it were important to me to keep the idiom of just calling a function, that's what I would do.

How do I sinon.stub a nested method with a callback?

I need to test a method that includes a sub-method which makes a call to an API server. I’d like to stud this internal sub-method, but I can’t seem to do that. Here’s an example:
var requests = require('./requests.js');
var utilityClass = {
methodCreatesObject: function (callback) {
// Here’s the method I’m trying to stub:
requests.makeCallToAPI(requestObject, function (err, responseFromAPI) {
doSomethingWithResponse(responseFromAPI, function (err, finalObject) {
if (err) {
callback(err, null);
} else {
callback(null, finalObject); // <- Want to test the value of finalObject
}
});
});
}
}
So, my test looks something like this (updated to show loading requests.js before utility.js):
var should = require('should'),
Joi = require('joi'),
sinon = require('sinon'),
requests = require('../lib/modules/requests.js'),
utility = require('../lib/modules/utility.js')
;
// Start my tests:
describe('Method', function () {
before(function () {
var fakeAPIresponse = { ... }
sinon.stub(requests, 'makeCallToAPI').yield(null, fakeAPIresponse);
});
it('should produce a well-formed finalObject', function (done) {
utilityClass.methodCreatesObject(function (err, response) {
if (err) {
done(err);
} else {
response.should.do.this.or.that;
done();
}
});
});
});
As I understand it, .yields() should try to run the first callback it detects in the arguments and feed its own arguments to it (resulting in doSomethingWithResponse(responseFromAPI, function () {...})). However, when running mocha, I’m getting an error indicating that the API server could not be reached, which suggests that the real requests.makeCallToAPI() is being called, and not my stub.
I must be missing something. What am I doing wrong here?
Where are you requiring the request.js? You will need to require request.js before you load up the module you want to test.
Edit 1: Using sinon.js
Here is a gist of what I meant: https://gist.github.com/limianwang/1114249de99c6a189384
Edit 2: Using proxyquire
If you are intending to test simply the utilities without concern of what actually happens within the requests.makeAPICall, you can use something like proxyquire to do the trick. If you are concerned with the actual logic within requests.js, you can use sinon.stub to stub out the actual request.get api.

Resources