How do I access mocha's json report from a script? - node.js

Mocha can be invoked from a script and it has a useful JSON reporter, but how can one access that reported structure from the invoking script? Redirecting stdout worked:
var Mocha = require('mocha');
var stats = {};
var oldWrite = process.stdout.write;
process.stdout.write = function(txt) {
stats = JSON.parse(txt).stats; // write invoked in one gulp.
};
new Mocha().
addFile("test/toyTest").
reporter("json", {stats: stats}).
run(function (failures) {
process.on('exit', function () {
process.stdout.write = oldWrite;
console.log("percentage: " + stats.passes/(stats.passes+stats.failures));
process.exit(failures > 0 ? 1 : 0);
});
});
but I'd have expected a more direct solution.

According to the code, the answer was "you can't":
process.stdout.write(JSON.stringify(obj, null, 2));
Since the my solution above is somewhat less than obvious, I created a pull request to add a reporter option to pass in a target object:
var Mocha = require('mocha');
var report = {};
new Mocha().
addFile("test/toyTest").
reporter("json", {"output-object": report}).
run(function (failures) {
process.on('exit', function () {
var s = report.stats;
console.log("percentage: " + s.passes/(s.passes+s.failures));
process.exit(failures > 0 ? 1 : 0);
});
});
which saves capturing process.stdout.write as well as the needless serialization and deserialization of the report structure. I also added a command line to set the output file so you can run:
mocha -R json --reporter-options output-file=rpt.json

An alternative solution is to create your own reporter like it has been suggested in the documentation. I simply copied the json reporter in node_modules/mocha/lib/reporters/json.js to new file in my project folder companyReporter.js and replaced this line
Ln 69: process.stdout.write(JSON.stringify(obj, null, 2));
With
Ln 69: process.send(obj);
It was also necessary to update the paths to the requires on line 9 and 10 of myReporter.js. I prefer this way because I'm not messing with process.stdout.write.
The last change to the code was updating the reporter path:
var Mocha = require('mocha');
process.on('message', function (message) {
const test = message.toString('utf8').replace(/\n$/,'');
new Mocha().
addFile(test).
reporter(__dirname + '/path/to/myReporter.js').
run(function(failures) {
process.exit(failures > 0 ? 1 : 0);
});
});

Related

Mocha testing javascript with inheritance

I am new to Mocha and Node but trying to write some Mocha tests on some JavaScript classes.
I have the following class:
function FormField() {
}
FormField.prototype.sanitizeFieldValue = function(value) {
if (value == null || value.replace == null) {
return null;
}
return value
.replace(/ /g, " ")
.replace(/&/g, '&')
.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/[\n\r]+/g, " ");
};
module.exports = FormField;
When I run my mocha tests on this file, everything works fine and the tests pass.
var expect = require("chai").expect;
var formfield = require("FormField");
describe("new Form Field Tests", function() {
var ff = new formfield();
describe("sanitizeFieldValue", function() {
it("escapes apostrophe", function() {
expect(ff.sanitizeFieldValue("'")).to.equal("\\\'");
});
});
});
However, I have another file which references the first:
TargetDateField.prototype = new FormField();
function TargetDateField() {
// some functions
}
module.exports = TargetDateField;
But I am not sure how to test this
I have tried the following but I keep getting FormField is not defined.
var expect = require("chai").expect;
var FormField = require("FormField").FormField;
var targetdatefield = require("TargetDateField");
Any ideas on how to resolve this?
Everywhere where you want to use FormField you require the module that defines it.... except in your TargetDateField.js file, where you don't require it. That's why you are getting an error. You need to require it there too:
var FormField = require("FormField");
TargetDateField.prototype = new FormField();
// etc...
By the way, I strongly suggest writing your code to use relative paths when you want to load other modules that are part of the same package. At first glance, I'd expect require("FormField") to load something from node_modules. (Like when you do require("chai").)

Writing test cases using Mocha and Chai

I have a following simple function:
var moment = require('moment-timezone');
exports.splitIntoDays = function(from,to) {
var timeIntervals = [];
var interval = {};
var start = moment(from);
var end = moment(to);
if(start.isAfter(end)) {
throw new Error('From date ('+from+') is after To date ('+to+').Enter a valid date range.');
}
var initial = start;
console.log("Before loop"+initial.format("YYYY/MM/DD-HH:mm:ss")+" "+initial.diff(end,'hours'));
while(end.diff(initial,'hours') > 24) {
timeIntervals.push({"from" : initial.format("YYYY/MM/DD-HH:mm:ss"), "to" : initial.add(24,'hours').format("YYYY/MM/DD-HH:mm:ss")});
initial = initial.add(1,'hours');
}
timeIntervals.push({"from" : initial.format("YYYY/MM/DD-HH:mm:ss"), "to" : end.format("YYYY/MM/DD-HH:mm:ss")});
console.info(JSON.stringify(timeIntervals));
return timeIntervals;
}
So, if I call it, splitIntoDays('2014/09/13-10:00:00','2014/09/14-09:00:00'),
I get the following response:
[ { from: '2014/09/13-10:00:00', to: '2014/09/14-09:00:00' } ]
I wrote the following test using Mocha and Chai:
var expect = require("chai").expect;
var utils = require("../Utils.js");
describe("Utils", function(){
describe("#splitIntoDays()", function(){
var timeIntervals = [];
var results = utils.splitIntoDays('2014/09/13-10:00:00','2014/09/14-09:00:00');
timeIntervals.push({ "from": '2014/09/13-10:00:00', "to": '2014/09/14-09:00:00' });
expect(results).to.equal(timeIntervals);
});
});
But, this one fails. Can you please help me in pointing out a mistake?
You need to wrap your test in an it call and you need to use deep.equal. For instance:
it("equal", function () {
expect(results).to.deep.equal(timeIntervals);
});
equal by itself will check that the objects are strictly equal with ===. Start Node on your computer and type [] === [] at the prompt. You'll get the result false. This is because you have two Array objects and a strict equality check will fail if the objects are not the same instance.
The it call is necessary because this is how you tell Mocha "here's a test for you to run". The describe calls declare test suites but do not themselves declare any tests.

Send parameters to jshint reporter in Gulp

I have Gulpfile with jshint configured to use jshint-stylish reporter. I need to pass option verbose to reporter in order to display warning codes. Is it possible to do it using Gulp?
Current my gulpfile.js looks like below:
var gulp = require('gulp');
var jshint = require('gulp-jshint');
var compass = require('gulp-compass');
var path = require('path');
require('shelljs/global');
var jsFiles = ['www/js/**/*.js', '!www/js/libraries/**/*.js', 'www/spec/**/*.js', '!www/spec/lib/**/*.js'];
var sassFiles = 'www/sass/*.scss';
gulp.task('lint', function () {
return gulp
.src(jsFiles)
.pipe(jshint())
.pipe(jshint.reporter('jshint-stylish'));
});
gulp.task('compass', function () {
gulp.src(sassFiles)
.pipe(compass({
project: path.join(__dirname, 'www'),
css: 'css',
sass: 'sass',
image: 'img',
font: 'fonts'
})).on('error', function() {});
});
var phonegapBuild = function (platform) {
if (!which('phonegap')) {
console.log('phonegap command not found')
return 1;
}
exec('phonegap local build ' + platform);
};
gulp.task('build:android', ['lint', 'compass'], function () {
phonegapBuild('android');
});
gulp.task('build:ios', ['lint', 'compass'], function () {
phonegapBuild('ios');
});
gulp.task('watch', function() {
gulp.watch(jsFiles, ['lint']);
gulp.watch(sassFiles, ['compass']);
});
gulp.task('default', ['lint', 'compass']);
Well, this, plus the fact that the output of the stylish reporter is hardly readable on Windows due to the darkness of the blue text, so I have to keep going in an manually changing the colour after installing it, has made me do something about it. So you should hopefully have more luck with this reporter I've just written:
https://github.com/spiralx/jshint-summary
You basically use it like this;
var summary = require('jshint-summary');
// ...
.pipe(jshint.reporter(summary({
verbose: true,
reasonCol: 'cyan,bold',
codeCol: 'green'
})
and the summary function will initialise the function passed to JSHint with those settings - see the page on Github for a bit more documentation.
It's got some very basic tests, and the library's gulpfile.js uses it to show its own JSHint output :)
How about using similar technique, as you already did with phonegap?
var jshint = function (parameter) {
// todo: define paths with js files, or pass them as parameter too
exec('jshint ' + paths + ' ' + parameter);
};
Based on https://github.com/wearefractal/gulp-jshint/blob/master/index.js#L99 it appears that gulp-jshint doesn't facilitate passing more than the name to the reporter if you load it with a string. It seems a simple thing to extend though. I'll race you to a pull request. :D
Alternatively, try something like this:
var stylish = require('jshint-stylish');
// ...
.pipe(jshint.reporter(stylish(opt)));
I'm pretty sure I have the syntax wrong, but this may get you unstuck.
It's annoying, and makes any decent reporter somewhat tricky to use within the existing framework. I've come up with this hack for the Stylish reporter, it's just currently in my gulpfile.js:
function wrapStylishReporter(reporterOptions) {
var reporter = require(stylish).reporter,
reporterOptions = reporterOptions || {};
var wrapped = function(results, data, config) {
var opts = [config, reporterOptions].reduce(function(dest, src) {
if (src) {
for (var k in src) {
dest[k] = src[k];
}
}
return dest;
}, {});
reporter(results, data, opts);
};
return jshint.reporter(wrapped);
}
And then for the task definition itself:
gulp.task('lint', function() {
return gulp.src('+(bin|lib)/**/*.js')
.pipe(jshint())
.pipe(wrapStylishReporter({ verbose: true }))
.pipe(jshint.reporter('fail'));
});
Ideally reporters would either be a function that takes an options parameter and returns the reporter function, or a fairly basic class so you could have options as well as state.

In mocha testing framework is there any possibility to store the test reports in local file

Im using mocha framework in my node application.If i run my test file using mocha means i'm getting error reports in terminal but i want to store the reports in local file.How can i do that.Is there any builtin method for this in mocha.
describe('Test with mocha', function(){
it ('result should be true',function(done){
var finalResult=false;
expect(finalResult).to.be(true);
done();
});
});
no there no built-in feature for mocha to do this, but you can do some other stuff by working with pipes which are standard in OSes.
open up a terminal, and give the command
mocha TEST.js > report.txt
the command above simply pipes all output to report.txt file.
You can also try working with child_process module from node, where you pretty much do the same thing as the command line, but in pure js.
*code is from node.js docs
var fs = require('fs'),
spawn = require('child_process').spawn,
out = fs.openSync('./out.log', 'a'),
err = fs.openSync('./out.log', 'a');
var child = spawn('prg', [], {
stdio: [ 'ignore', out, err ]
});
child.unref();
Ideally you can use mocha-junit-reporter and write output to XML file.
mocha tests --recursive --reporter mocha-junit-reporter --reporter-options mochaFile=./myJUnitfile.xml
If by local file you mean simple txt file then I have a sample code for you on which you can extend your own implementation. Please note that following code is only for knowledge sake. It doesn't handle many of the reporter events such as test suite start, and end etc., and exceptions that may be thrown by file operations.
For complete list of reporter events refer to https://github.com/mochajs/mocha/wiki/Third-party-reporters.
Also the following code is extended from the sample code present in above link.
// This code is extended on the orginal source present
// at https://github.com/mochajs/mocha/wiki/Third-party-reporters
// my-file-reporter.js
const mocha = require('mocha');
const fs = require('fs');
const os = require("os");
module.exports = MyFileReporter;
function MyFileReporter(runner, options) {
this._mochaFile = options.reporterOptions.mochaFile || process.env.MOCHA_FILE || 'test-results.txt';
mocha.reporters.Base.call(this, runner);
var passes = 0;
var failures = 0;
runner.on('start', function() {
if (fs.existsSync(this._mochaFile)) {
console.log('Removing existing test result file!');
fs.unlinkSync(this._mochaFile);
}
}.bind(this));
runner.on('pass', function(test){
passes++;
console.log('Pass: %s', test.fullTitle()); // Remove console.log statements if you dont need it (also below in code)
fs.appendFileSync(this._mochaFile, 'Pass: ' + test.fullTitle() + os.EOL); // Note: Exception not handled
}.bind(this));
runner.on('fail', function(test, err){
failures++;
console.log('Fail: %s -- error: %s', test.fullTitle(), err.message);
fs.appendFileSync(this._mochaFile, 'Fail: ' + test.fullTitle() + 'Error: ' + err.message + os.EOL); // Note: Exception not handled
}.bind(this));
runner.on('end', function(){
console.log('Test Summary: %d/%d', passes, passes + failures);
fs.appendFileSync(this._mochaFile, os.EOL + 'Test Summary: ' + passes + '/' + (passes + failures) + os.EOL); // Note: Exception not handled
}.bind(this));
}
Use the following command line (it assumes that my-file-reporter.js is present in the same folder where this command is executed)
mocha tests --recursive --reporter my-file-reporter --reporter-options mochaFile=./myfile.txt

Assert inner function called

I have a file foo.js that looks like this:
var exec = require('child_process').exec
...
function start(){
...
exec('open -g http://localhost:<port>'); // opens a browser window
}
// EOF
I want to test that when I call the start() function, a browser window gets opened. Ideally, I'd like to use Sinon to stub out exec (so that we don't actually open a browser window during automated tests), and assert that exec was called. I've tried many ways, none of which work. For example in foo_test.js:
var subject = require('../lib/foo');
describe('foo', function(){
describe('start', function(){
it('opens a browser page to the listening address', function(){
var stub = sinon.stub(subject, 'exec', function(){
console.log('stubbed exec called');
}); // fails with "TypeError: Attempted to wrap undefined property exec as function"
});
});
});
How would I go about doing this?
Not sure if this is what you're looking for, but a quick search returned:
https://github.com/arunoda/horaa
Basically, it allows you to stub out libraries. From the examples:
Your Code
//stored in abc.js
exports.welcome = function() {
var os = require('os');
if(os.type() == 'linux') {
return 'this is a linux box';
} else {
return 'meka nam linux nemei :)';
}
};
Test Code (note the mocking of OS)
//stored in test.js
var horaa = require('horaa');
var lib = require('./abc'); // your code
var assert = require('assert');
//do the hijacking
var osHoraa = horaa('os');
osHoraa.hijack('type', function() {
return 'linux';
});
assert.equal(lib.welcome(), 'this is a linux box');
//restore the method
osHoraa.restore('type');
Hope that helps.

Resources