Skip to content
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,11 @@ build/Release
# Deployed apps should consider commenting this line out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules


# Webstorm IDE metadata directory
.idea

# test report files
test/report/*.xml*
test/report/*.log
77 changes: 67 additions & 10 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
"use strict";

var gulp = require('gulp'),
mochaStream = require('./lib').mochaStream,
SpawnMocha = require('./lib').SpawnMocha,
_ = require('lodash'),
through = require('through'),
Q = require('q'),
runSequence = Q.denodeify(require('run-sequence')),
assert = require('assert'),
File = require('vinyl'),
from = require('from');
mochaStream = require('./lib').mochaStream,
SpawnMocha = require('./lib').SpawnMocha,
_ = require('lodash'),
through = require('through'),
Q = require('q'),
runSequence = Q.denodeify(require('run-sequence')),
assert = require('assert'),
File = require('vinyl'),
from = require('from'),
path = require('path'),
glob = require('glob');

function customMocha(opts) {
opts = opts || {};
Expand Down Expand Up @@ -129,6 +131,60 @@ gulp.task('test-live-output-with-prepend', function() {
.pipe(mocha);
});

gulp.task('test-mocha-opts-override', function (cb) {
function mochaIteration(opts, overrides, files, cb) {
opts = opts || {};
var spawnMocha = new SpawnMocha(opts);
overrides.forEach(function (override) {
spawnMocha.add(files, override);
});
var errors = [];
spawnMocha.on('error', function (err) {
console.error(err.toString());
errors.push(err);
}).on('end', function () {
if (errors.length > 0) {
console.error('ERROR SUMMARY: ');
_(errors).each(function (err) {
console.error(err);
console.error(err.stack);
}).value();
return cb(new Error('some tests failed'));
}
cb(null);
});
}

function setEnv(envs) {
var env = process.env;
env = _.clone(env);
env = _.merge(env, envs, {JUNIT_REPORT_PATH: path.resolve(__dirname, 'test/report/report.xml')});
return env;
}

var opts = {
concurrency: 3,
flags: {R: 'mocha-jenkins-reporter'}
};
var overrides = [{
env: setEnv({NODE_ENV: 'groupa'}),
flags: {grep: "@groupA@"}
}, {
env: setEnv({NODE_ENV: 'groupb'}),
flags: {grep: "@groupB@"}
}, {
env: setEnv({NODE_ENV: 'groupc'}),
flags: {grep: "@groupC@"}
}];

glob('test/group/*-specs.js', function (err, files) {
if (err) {
return cb(err);
}
console.log('files', files);
mochaIteration(opts, overrides, files, cb);
});
});

gulp.task('test', function() {
return runSequence(
Expand All @@ -137,6 +193,7 @@ gulp.task('test', function() {
'test-live-output',
'test-live-output-with-prepend',
'test-live-output-with-file',
'test-with-file'
'test-with-file',
'test-mocha-opts-override'
);
});
75 changes: 39 additions & 36 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@

// Module Requirements
var _ = require('lodash'),
proc = require('child_process'),
join = require('path').join,
async = require('async'),
util = require('util'),
EventEmitter = require('events').EventEmitter,
streamBuffers = require("stream-buffers"),
through = require('through'),
split = require('split'),
fs = require('fs');
proc = require('child_process'),
join = require('path').join,
async = require('async'),
util = require('util'),
EventEmitter = require('events').EventEmitter,
streamBuffers = require("stream-buffers"),
through = require('through'),
split = require('split'),
fs = require('fs');

require('colors');

function newStreamBuffer() {
var stream = new streamBuffers.WritableStreamBuffer({
initialSize: (25 * 1024),
incrementAmount: (10 * 1024)
initialSize: (25 * 1024),
incrementAmount: (10 * 1024)
});
return stream;
}
Expand All @@ -30,15 +30,16 @@ var SpawnMocha = function (opts) {
});
var queue = async.queue(function (task, done) {
// Setup
var overrideOpts = _.merge(_.clone(opts), task.overrideOpts);
var bin = _.isFunction(opts.bin) ? opts.bin() : opts.bin ||
join(__dirname, '..', 'node_modules', '.bin', 'mocha');
var env = _.isFunction(opts.env) ? opts.env() : opts.env || process.env;
join(__dirname, '..', 'node_modules', '.bin', 'mocha');
var env = _.isFunction(overrideOpts.env) ? overrideOpts.env() : overrideOpts.env || process.env;
env = _.clone(env);

// Generate arguments
var args = [];
_(opts.flags).each(function (val, key) {
if(_.isFunction(val)) val = val();
_(overrideOpts.flags).each(function (val, key) {
if (_.isFunction(val)) val = val();
args.push((key.length > 1 ? '--' : '-') + key);
if (_.isString(val) || _.isNumber(val)) {
args.push(val);
Expand All @@ -47,27 +48,26 @@ var SpawnMocha = function (opts) {

var stdout = newStreamBuffer();
var stderr = newStreamBuffer();
var fsStream = opts.fileOutput ?
fs.createWriteStream(opts.fileOutput, { flags: 'a', encoding: 'utf8' }) : null;
var fsStream = overrideOpts.fileOutput ?
fs.createWriteStream(overrideOpts.fileOutput, {flags: 'a', encoding: 'utf8'}) : null;

// Split xunit test report in several files if required
if(env.JUNIT_REPORT_PATH) {
env.JUNIT_REPORT_PATH = env.JUNIT_REPORT_PATH + '.' + task.taskNum;
if (env.JUNIT_REPORT_PATH) {
env.JUNIT_REPORT_PATH = env.JUNIT_REPORT_PATH.replace(/xml$/, task.taskNum + '.xml');
}

// Execute Mocha
var child = proc.spawn(bin, args.concat(task.files), {env: env});

if (opts.liveOutput) {
child.stdout.pipe(split())
.on('data', function (line) {
console.log((opts.liveOutputPrepend || '') + line);
console.log((overrideOpts.liveOutputPrepend || '') + line);
});
child.stderr.pipe(split())
.on('data', function (line) {
console.error((opts.liveOutputPrepend || '') + line);
console.error((overrideOpts.liveOutputPrepend || '') + line);
});
if(fsStream) {
if (fsStream) {
child.stdout.pipe(fsStream);
child.stderr.pipe(fsStream);
}
Expand All @@ -77,25 +77,27 @@ var SpawnMocha = function (opts) {
}

// When done...
child.on('close', function(errCode) {
if(stdout.size()) {
child.on('close', function (errCode) {
if (stdout.size()) {
var contentOut = stdout.getContentsAsString("utf8");
console.log(contentOut);
if(fsStream) {
if (fsStream) {
fsStream.write(contentOut + '\n', 'utf8');
}
}
if(stderr.size()) {
if (stderr.size()) {
var contentErr = stdout.getContentsAsString("utf8");
console.error(contentErr);
if(fsStream) {
if (fsStream) {
fsStream.write(contentErr + '\n', 'utf8');
}
}
if(fsStream) { fsStream.close(); }
if (fsStream) {
fsStream.close();
}

var err = null;
if(errCode && opts.errorSummary) {
if (errCode && opts.errorSummary) {
err = new Error('Error for files: ' + task.files.join(', '));
err.files = task.files;
err.stderr = stderr.size() ? stderr.getContentsAsString("utf8") : '';
Expand All @@ -105,25 +107,26 @@ var SpawnMocha = function (opts) {
});
}, opts.concurrency || 1);

queue.drain = function() {
queue.drain = function () {
_this.emit('end');
};

var taskNum = 0;
this.add = function(files) {
taskNum ++;
this.add = function (files, overrideOpts) {
taskNum++;
if (!_.isArray(files)) {
files = [files];
}
var task = {taskNum: taskNum, files: files};
queue.push(task, function(err) {
if(err){
var task = {taskNum: taskNum, files: files, overrideOpts: overrideOpts || {}};
queue.push(task, function (err) {
if (err) {
_this.emit('error', err, files);
}
});
};
};


util.inherits(SpawnMocha, EventEmitter);

var mochaStream = function mocha(opts) {
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
"devDependencies": {
"fixture-stdout": "^0.2.1",
"from": "^0.1.3",
"glob": "^5.0.5",
"gulp": "^3.8.8",
"mocha-jenkins-reporter": "^0.1.8",
"q": "^1.1.2",
"run-sequence": "^1.0.1",
"vinyl": "^0.4.3"
Expand Down
27 changes: 27 additions & 0 deletions test/group/a-group-specs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use strict";
/* global describe, it */

describe('@suiteA@groupA@', function() {
this.timeout(3000);
it('should work, NODE_ENV: ' + process.env.NODE_ENV, function(done) {
setTimeout(function() {
done();
}, 2000);
});
});
describe('@suiteA@groupB@', function() {
this.timeout(3000);
it('should work, NODE_ENV: ' + process.env.NODE_ENV, function(done) {
setTimeout(function() {
done();
}, 2000);
});
});
describe('@suiteA@groupC@', function() {
this.timeout(3000);
it('should work, NODE_ENV: ' + process.env.NODE_ENV, function(done) {
setTimeout(function() {
done();
}, 2000);
});
});
27 changes: 27 additions & 0 deletions test/group/b-group-specs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use strict";
/* global describe, it */

describe('@suiteB@groupA@', function() {
this.timeout(3000);
it('should work', function(done) {
setTimeout(function() {
done();
}, 2000);
});
});
describe('@suiteB@groupB@', function() {
this.timeout(3000);
it('should work', function(done) {
setTimeout(function() {
done();
}, 2000);
});
});
describe('@suiteB@groupC@', function() {
this.timeout(3000);
it('should work', function(done) {
setTimeout(function() {
done();
}, 2000);
});
});
27 changes: 27 additions & 0 deletions test/group/c-group-specs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use strict";
/* global describe, it */

describe('@suiteC@groupA@', function() {
this.timeout(3000);
it('should work', function(done) {
setTimeout(function() {
done();
}, 2000);
});
});
describe('@suiteC@groupB@', function() {
this.timeout(3000);
it('should work', function(done) {
setTimeout(function() {
done();
}, 2000);
});
});
describe('@suiteC@groupC@', function() {
this.timeout(3000);
it('should work', function(done) {
setTimeout(function() {
done();
}, 2000);
});
});
2 changes: 2 additions & 0 deletions test/report/reports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# reports
This directory for .log, .xml, etc reports