Test Grunt Tasks - node.js

I am using Yeoman to generate some projects and also grunt-tasks.
Now I would also like to test the generated grunt tasks using Mocha, but I only find some information how to use Mocha tests in Grunt ;-)
Can anybody help?

Not an elegant solution but I took the approach of installing my dependencies (npm install) and consequently running the corresponding grunt task (for e.g. grunt less) and then writing test logic post that operation. I've used nested exec calls for this.
describe('less grunt tasks tests', function () {
var prompts = {
workFolder: 'temp',
fiddleDesc: 'mocha test'
};
var testGlobal = {};
beforeEach(function(done) {
testGlobal.app = helpers.run(path.join(__dirname, '../app'))
.inTmpDir(function(dir, err) {
if(err) { done(err); return; }
testGlobal.dir = dir;
// console.log(dir);
})
.withArguments(['skip-install'])
.withOptions({ less: true })
.withPrompts(prompts)
.on('end', function(){
done();
});
});
it('should modify app/styles/style.css', function(done){
this.timeout(60000 * 10); //10 minutes - my network is f**ked up
var opts = {
cwd : testGlobal.dir,
env: process.env,
detached: true
};
var gen = testGlobal.app.generator;
var devdeps = gen.devDependencies.join(' ');
var rootPath = testGlobal.dir;
var getPath = function(fpath) {
var s = path.join(rootPath, fpath);
// console.log(s); ;
return s;
};
exec('npm install ' + devdeps, opts, function(err, stdout, stderr) {
if(err) {
done(err);
return;
}
var h1 = fs.readFileSync(getPath('app/less/h1.less'), 'utf8');
var css = fs.readFileSync(getPath('app/styles/style.css'), 'utf8');
// expect(css).to.not.contain(h1);
expect(css).to.not.contain('h1');
exec('grunt less', opts, function(e, out, serr){
if(e) {
done(e);
return;
}
// console.log(out);
var h1 = fs.readFileSync(getPath('app/less/h1.less'), 'utf8');
var css = fs.readFileSync(getPath('app/styles/style.css'), 'utf8');
// expect(css).to.contain(h1); //this expect fails since for some reason \r are stripped out
expect(css).to.contain('h1');
done();
});
});
});
});
For more reference you can see more test code in the repo I contribute against.
Ps: I'd appreciate your comments on the approach I've taken.

Related

Use mocha programmatically via rest

currently I try to figure out, how I can run mocha tests on a GET request of localhost:3000/run/tests resource.
Most things seems to work fine, but mocha .on('end') is probably not fired correctly so my res.json(...) function is not called and node still hangs.
var Mocha = require('mocha');
...
app.get('/run/tests', (req, res) => {
var mocha = new Mocha({
reporter: 'json'
});
myTestFiles.forEach(testfile => mocha.addFile('./tests/' + testfile + '.js'));
mocha.run()
.on('end', function() {
console.log(this.testResults);
res.json(this.testResults);
});
});
I get expected output except the "0 passing" lines :
> ...
> CALL element.text()
> GET /session/:sessionID/element/9/text
> RESPONSE element.text() "Username"
√ App login finished: 37685ms
0 passing (0ms)
0 passing (16ms)
My testfile looks like this:
'use strict';
require('./helpers/setup');
var wd = require('wd');
var serverConfigs = require('./helpers/appium-servers');
describe('Carnet App', function () {
this.timeout(120000);
var driver;
var allPassed = true;
before(function () {
var serverConfig = serverConfigs.local;
driver = wd.promiseChainRemote(serverConfig);
require('./helpers/logging').configure(driver);
var desired = {
'appium-version': '1.4.16',
platformVersion: '6.0.1',
device: 'Android',
platformName: 'Android',
app: myapp.apk'
};
return driver
.init(desired)
.setImplicitWaitTimeout(120000);
});
after(function (done) {
return driver
.quit()
.done();
});
afterEach(function () {
allPassed = allPassed && this.currentTest.state === 'passed';
});
it('App login finished', function () {
return driver
.elementById('...')
.click()
.sendKeys('...')
.elementById('...')
.text().should.become('Username');
});
});
Do I make a mistake? Does anybody have expirence with wd + mocha programmatically? Thanks for help!
Ok I solved my problem by only modify the following:
mocha.run(function(failures){
res.json({ ... })
})
.on('test', function() {
// do some logging stuff (into a var retured by res.json above)
})
// ... all mocha events possible
// see https://github.com/mochajs/mocha/blob/master/lib/runner.js#L49
.on('test end', function() {
// do some advanced logging stuff
});

TDD/ testing with streams in NodeJS

I've been trying to find a reasonable way to test code that uses streams. Has anyone found a reasonable way/ framework to help testing code that uses streams in nodejs?
For example:
var fs = require('fs'),
request = require('request');
module.exports = function (url, path, callback) {
request(url)
.pipe(fs.createWriteStream(path))
.on('finish', function () {
callback();
});
};
My current way of testing this type of code either involves simplifying the code with streams so much that I can abstract it out to a non-tested chunk of code or by writing something like this:
var rewire = require('rewire'),
download = rewire('../lib/download'),
stream = require('stream'),
util = require('util');
describe('download', function () {
it('should download a url', function (done) {
var fakeRequest, fakeFs, FakeStream;
FakeStream = function () {
stream.Writable.call(this);
};
util.inherits(FakeStream, stream.Writable);
FakeStream.prototype._write = function (data, encoding, cb) {
expect(data.toString()).toEqual("hello world")
cb();
};
fakeRequest = function (url) {
var output = new stream.Readable();
output.push("hello world");
output.push(null);
expect(url).toEqual('http://hello');
return output;
};
fakeFs = {
createWriteStream: function (path) {
expect(path).toEqual('hello.txt');
return new FakeStream();
}
};
download.__set__('fs', fakeFs);
download.__set__('request', fakeRequest);
download('http://hello', 'hello.txt', function () {
done();
});
});
});
Has anyone come up with more elegant ways of testing streams?
Made streamtest for that purpose. It not only make streams tests cleaner but also allows to test V1 and V2 streams https://www.npmjs.com/package/streamtest
I've also been using memorystream, but then putting my assertions into the finish event. That way it looks more like a real use of the stream being tested:
require('chai').should();
var fs = require('fs');
var path = require('path');
var MemoryStream = require('memorystream');
var memStream = MemoryStream.createWriteStream();
/**
* This is the Transform that we want to test:
*/
var Parser = require('../lib/parser');
var parser = new Parser();
describe('Parser', function(){
it('something', function(done){
fs.createReadStream(path.join(__dirname, 'something.txt'))
.pipe(parser)
.pipe(memStream)
.on('finish', function() {
/**
* Check that our parser has created the right output:
*/
memStream
.toString()
.should.eql('something');
done();
});
});
});
Checking objects can be done like this:
var memStream = MemoryStream.createWriteStream(null, {objectMode: true});
.
.
.
.on('finish', function() {
memStream
.queue[0]
.should.eql({ some: 'thing' });
done();
});
.
.
.
Read the Stream into memory and compare it with the expected Buffer.
it('should output a valid Stream', (done) => {
const stream = getStreamToTest();
const expectedBuffer = Buffer.from(...);
let bytes = new Buffer('');
stream.on('data', (chunk) => {
bytes = Buffer.concat([bytes, chunk]);
});
stream.on('end', () => {
try {
expect(bytes).to.deep.equal(expectedBuffer);
done();
} catch (err) {
done(err);
}
});
});
I feel you pain.
I don't know any framework to help out testing with streams, but if take a look here,
where I'm developing a stream library, you can see how I approach this problem.
here is a idea of what I'm doing.
var chai = require("chai")
, sinon = require("sinon")
, chai.use(require("sinon-chai"))
, expect = chai.expect
, through2 = require('through2')
;
chai.config.showDiff = false
function spy (stream) {
var agent, fn
;
if (spy.free.length === 0) {
agent = sinon.spy();
} else {
agent = spy.free.pop();
agent.reset();
}
spy.used.push(agent);
fn = stream._transform;
stream.spy = agent;
stream._transform = function(c) {
agent(c);
return fn.apply(this, arguments);
};
stream._transform = transform;
return agent;
};
spy.free = [];
spy.used = [];
describe('basic through2 stream', function(){
beforeEach(function(){
this.streamA = through2()
this.StreamB = through2.obj()
// other kind of streams...
spy(this.streamA)
spy(this.StreamB)
})
afterEach(function(){
spy.used.map(function(agent){
spy.free.push(spy.used.pop())
})
})
it("must call transform with the data", function(){
var ctx = this
, dataA = new Buffer('some data')
, dataB = 'some data'
;
this.streamA.pipe(through2(function(chunk, enc, next){
expect(ctx.streamA.spy).to.have.been.calledOnce.and.calledWidth(dataA)
}))
this.streamB.pipe(through2(function(chunk, enc, next){
expect(ctx.streamB.spy).to.have.been.calledOnce.and.calledWidth(dataB)
}))
this.streamA.write(dataA)
this.streamB.write(dataB)
})
})
Note that my spy function wraps the _transform method and call my spy and call the original _transform
Also, The afterEach function is recycling the spies, because you can end up creating hundreds of them.
The problem gets hard is when you want to test async code. Then promises your best friend. The link I gave above have some sample that.
I haven't used this, and it's quite old, but https://github.com/dominictarr/stream-spec might help.
You can test streams using MemoryStream and sinon by using spies. Here is how I tested some of my code.
describe('some spec', function() {
it('some test', function(done) {
var outputStream = new MemoryStream();
var spyCB = sinon.spy();
outputStream.on('data', spyCB);
doSomething(param, param2, outputStream, function() {
sinon.assert.calledWith(spyCB, 'blah');
done();
});
});
});
Best way I have found is to use events
const byline = require('byline');
const fs = require('fs');
it('should process all lines in file', function(done){
//arrange
let lines = 0;
//file with 1000 lines
let reader = fs.readFileStream('./input.txt');
let writer = fs.writeFileStream('./output.txt');
//act
reader.pipe(byline).pipe(writer);
byline.on('line', function() {
lines++;
});
//assert
writer.on('close', function() {
expect(lines).to.equal(1000);
done();
});
});
by passing done as a callback, mocha waits until it is called before moving on.

nodeJS: running a batch of childprocess commands in a waterfall

I am trying to elegantly run five git commands one after the other while maintaining the ability to catch error and progress:
git status
git pull
git add .
git commit -am "commit message"
git push
Open source note: I've studied different node-git libraries and decided for different reasons to implement it on my own.
Using Q, I've created a deferred method to run child processes:
var exec = require('child_process').exec,
path = require('path'),
Q = require('q'),
gitPath = path.resolve(__dirname + "/../projects/langs");
function run(command) {
var deferred = Q.defer();
exec(command, {cwd: gitPath}, function puts(error, stdout, stderr) {
if (error) {
deferred.reject(new Error(error));
} else {
deferred.resolve(stdout);
}
});
return deferred.promise;
}
However, I want to avoid the Pyramid of doom:
function option1() {
// Pyramid of doom
run("git status").then(function (output) {
console.log(output);
run("git pull").then(function (output) {
console.log(output);
run("git add .").then(function (output) {
console.log(output);
// etc.
});
});
});
}
And doesn't feel quite elegant:
function options1a() {
// Pyramid of doom
run("git status").then(function (output) {
console.log(output);
run("git pull");
}).then(function (output) {
console.log(output);
run("git add .")
}).then(function (output) {
console.log(output);
});
}
I saw a third option but can't seem to get it to work:
function promiseWaterfall(tasks) {
var resolvedPromise = Q(undefined);
var finalTaskPromise = tasks.reduce(function (prevTaskPromise, task) {
return prevTaskPromise.then(task);
}, resolvedPromise); // initial value
return finalTaskPromise;
}
promiseWaterfall([
run("git status"),
run("git pull"),
run("git add .")
]).then(function () {
console.log(arguments);
});
And I'm playing with a fourth option of using the async library:
async.waterfall([
function(callback){
callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
callback(null, 'three');
},
function(arg1, callback){
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
But this seems to take me towards a non-promises path.
How do I get it to work elegantly? any best practices?
I'm familiar with when.js promises so I will answer your question with that promise library. It provides helper functions for this sort of thing similar to the callback based async lib. Check out their API documentation for more examples.
In the following code I am using the when/sequence module to perform what you are looking for. I've also modified your code organization a little to keep things somewhat modular (e.g. not embedding the git cwd inside of the run function in your example).
Here is a fully working implementation. Make sure to change out the git cwd to your own git repository as it's currently pointing to one of my own.
var exec = require('child_process').exec
, when = require('when')
, sequence = require('when/sequence');
// simple promise wrapper for exec
function exec_p(command, options) {
options = options || {};
var defer = when.defer();
exec(command, options, function(error, stdout, stderr) {
return error
? defer.reject(stderr + new Error(error.stack || error))
: defer.resolve(stdout);
});
return defer.promise;
}
// Some simple git wrapper
function Git(config) {
var self = this;
self.config = config;
return function(gitCommand) {
return exec_p('git ' + gitCommand, self.config);
};
}
// create a new instnace of git and specify our options
var git = new Git({ cwd: "/home/trev/git/tsenior" });
// we can now use sequence & our newly created git wrapper to easily
// can things in order one after another
sequence([
function() { return git('status'); },
function() { return git('status'); },
function() { return git('status'); },
function() { return git('status'); }
]).then(function(results) { // handle the results here
console.log(results);
}).otherwise(function(error) { // handle any errors here
console.error(error.stack || error);
process.exit(1);
});
The code provided doesn't console.log after every step (it just logs out the results at the end), but it can be easily modified to do so.

Test binary file upload with mocha

I'm currently working on a small API with nodejs and restify that requires a file upload done, by receiving a binary string.
What I dont know how to do, is test it with mocha, so Ive been doing some search and found this on stack overflow Unit test file upload with mocha, its a fine start but it wont work because its sending a multipart form, and what I require the client to send on the api is the file as a stream.
Heres my controller:
exports.uploadVideo = function(req, res, next) {
var video = "public/video/" + req.params.videoId + ".mp4",
util = require('util'),
exec = require('child_process').exec;
var newFile = fs.createWriteStream("./uploads/" + video);
req.pipe(newFile);
req.on('end', function () {
var cmd = 'qtfaststart ' + './uploads/' + video;
var qtfaststart = exec(cmd, function(error, stdout, stderr){
if (error === "atom not found, is this a valid MOV/MP4 file?\n" || error !== null) {
return next(new restify.ConflictError("Error: " + stdout));
} else {
fs.chmodSync('./uploads/' + video, '644');
Video.findOne( { _id: req.params.videoId }, function(err, video) {
if (err) return next(new restify.ConflictError(err));
if (!video) {
newVideo = new Video({
_id: req.params.videoId,
file: video});
newVideo.save()
} else {
video.file = video;
video.increment();
video.save();
}
});
}
});
});
req.on('error', function(err){
return next(new restify.NetworkConnectTimeoutError(err));
});
};
So given this controller which receives a stream (binary file), and puts the stream together on the backend, how would I test this controller with mocha?
You could just use http for that:
it('should be possible to upload a file', function(done) {
var http = require('http');
var options = require('url').parse(YOUR_URL);
options.method = 'POST';
var req = http.request(options, function(response) {
// TODO: check for errors, correct response, etc...
done(...);
});
require('fs').createReadStream(YOUR_TEST_FILE).pipe(req);
});
You want to use the request module from within mocha. It supports multi-part forms.

Grunt task output then calling grunt-notify

Grunt notify: https://github.com/dylang/grunt-notify is great. However, it seems a bit limited. As far as I can tell all my messages need to be pre-generated. So the first question is how can I generate notifications?
Next, It seems that grunt notify triggers based on error or success of some task. I guess based on std in/out/err? The problem where this breaks down is if some task doesn't use these. grunt compass doesn't use stderr if there are compile errors. So how can I run grunt notify when it has an error? This then leads to the next question. How can I grab the console output or stderr from a grunt task?
First of all, use growl. It is easy and flexible to use. To install growl:
npm install growl --save-dev
Then you need to hook into the stderr/out stream of the process. This way you can create a notification each time a new message arrives at the stderr/out stream.
This is what I've created. I've made a CommonJs module which adds hooks to:
grunt.fail.warn(), grunt.fail.fatal()
grunt.log.warn(), grunt.log.error()
grunt.warn()
process.stderr.write()
process.stdout.write() (error lines)
(child) process.stderr.write()
(child) process.stdout.write() (error lines)
It works more or less, but it may need some tweaking.
tasks/lib/notify.js
(function (module) {
var grunt = require('grunt'),
growl = require('growl'),
Buffer = require('buffer').Buffer;
function notify(obj, title) {
if (obj) {
var message = Buffer.isBuffer(obj) ? obj.toString() : (obj.message || obj);
var msg = grunt.log.uncolor(message);
if (msg.length > 0) {
growl(msg, {
title: title,
image: 'Console'
});
}
}
}
// add a hook to grunt.fail.warn(), grunt.fail.fatal()
['warn', 'fatal'].forEach(function (level) {
grunt.util.hooker.hook(grunt.fail, level, function(obj) {
notify(obj);
});
});
// add a hook to grunt.log.warn(), grunt.log.error()
['warn', 'error'].forEach(function (level) {
grunt.util.hooker.hook(grunt.log, level, function(obj) {
notify(obj, level);
});
});
// add a hook to grunt.warn()
grunt.util.hooker.hook(grunt, 'warn', function(obj) {
notify(obj, 'warn');
});
// add a hook to process.stderr.write()
grunt.util.hooker.hook(process.stderr, 'write', function(obj) {
var messages = grunt.log.uncolor((Buffer.isBuffer(obj) ? obj.toString() : (obj.message || obj))).split('\n');
messages.forEach(function (message) {
notify(message, 'stderr');
});
});
// add a hook to process.stdout.write() (only error lines)
grunt.util.hooker.hook(process.stdout, 'write', function(obj) {
var messages = grunt.log.uncolor((Buffer.isBuffer(obj) ? obj.toString() : (obj.message || obj))).split('\n');
messages.forEach(function (message) {
if (message && message.indexOf('error ') > -1) {
notify(message, 'stdout');
}
});
});
// add a hook to child process stdout/stderr write() (only error lines)
grunt.util.hooker.hook(grunt.util, 'spawn', {
post: function(child) {
child.stderr.on('data', function (data) {
var messages = grunt.log.uncolor(data.toString()).split('\n');
messages.forEach(function (message) {
notify(message, 'stderr');
});
});
child.stdout.on('data', function (data) {
var messages = grunt.log.uncolor(data.toString()).split('\n');
messages.forEach(function (message) {
if (message && message.indexOf('error ') > -1) {
notify(message, 'stdout');
}
});
});
}
});
}) (module);
Then you need to include it in your Gruntfile.js with a require statement:
module.exports = function (grunt) {
grunt.initConfig({
...
});
require('./tasks/lib/notify');
...
};
PS I've placed the file in tasks/lib/notify.js, but feel free to place it somewhere else.

Resources