Skip to content

Commit

Permalink
Add unified diff (close #862)
Browse files Browse the repository at this point in the history
  • Loading branch information
rprieto authored and travisjeffery committed Sep 15, 2013
1 parent 0a0a9c1 commit eaaace7
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 77 deletions.
5 changes: 5 additions & 0 deletions bin/_mocha
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ program
.option('--interfaces', 'display available interfaces')
.option('--reporters', 'display available reporters')
.option('--compilers <ext>:<module>,...', 'use the given module(s) to compile files', list, [])
.option('--inline-diffs', 'display actual/expected differences inline within each string')

program.name = 'mocha';

Expand Down Expand Up @@ -211,6 +212,10 @@ if (~process.argv.indexOf('--colors') ||
Base.useColors = true;
}

// --inline-diffs

if (program.inlineDiffs) Base.inlineDiffs = true;

// --slow <ms>

if (program.slow) mocha.suite.slow(program.slow);
Expand Down
125 changes: 94 additions & 31 deletions lib/reporters/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ exports = module.exports = Base;

exports.useColors = isatty;

/**
* Inline diffs instead of +/-
*/

exports.inlineDiffs = false;

/**
* Default color map.
*/
Expand Down Expand Up @@ -175,31 +181,12 @@ exports.list = function(failures){

// actual / expected diff
if ('string' == typeof actual && 'string' == typeof expected) {
msg = errorDiff(err, 'WordsWithSpace', escape);

// linenos
var lines = msg.split('\n');
if (lines.length > 4) {
var width = String(lines.length).length;
msg = lines.map(function(str, i){
return pad(++i, width) + ' |' + ' ' + str;
}).join('\n');
fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
if (exports.inlineDiffs) {
msg = inlineDiff(err, escape);
} else {
msg = unifiedDiff(err, escape);
}

// legend
msg = '\n'
+ color('diff removed', 'actual')
+ ' '
+ color('diff added', 'expected')
+ '\n\n'
+ msg
+ '\n';

// indent
msg = msg.replace(/^/gm, ' ');

fmt = color('error title', ' %s) %s:\n%s')
+ color('error stack', '\n%s\n');
}

// indent stack trace without msg
Expand Down Expand Up @@ -335,6 +322,73 @@ function pad(str, len) {
return Array(len - str.length + 1).join(' ') + str;
}


/**
* Returns an inline diff between 2 strings with coloured ANSI output
*
* @param {Error} Error with actual/expected
* @return {String} Diff
* @api private
*/

function inlineDiff(err, escape) {
var msg = errorDiff(err, 'WordsWithSpace', escape);

// linenos
var lines = msg.split('\n');
if (lines.length > 4) {
var width = String(lines.length).length;
msg = lines.map(function(str, i){
return pad(++i, width) + ' |' + ' ' + str;
}).join('\n');
}

// legend
msg = '\n'
+ color('diff removed', 'actual')
+ ' '
+ color('diff added', 'expected')
+ '\n\n'
+ msg
+ '\n';

// indent
msg = msg.replace(/^/gm, ' ');
return msg;
}

/**
* Returns a unified diff between 2 strings
*
* @param {Error} Error with actual/expected
* @return {String} Diff
* @api private
*/

function unifiedDiff(err, escape) {
var indent = ' ';
function cleanUp(line) {
if (escape) {
line = escapeInvisibles(line);
}
if (line[0] === '+') return indent + colorLines('diff added', line);
if (line[0] === '-') return indent + colorLines('diff removed', line);
if (line.match(/\@\@/)) return null;
if (line.match(/\\ No newline/)) return null;
else return indent + line;
}
function notBlank(line) {
return line != null;
}
msg = diff.createPatch('string', err.actual, err.expected);
var lines = msg.split('\n').splice(4);
return '\n '
+ colorLines('diff added', '+ expected') + ' '
+ colorLines('diff removed', '- actual')
+ '\n\n'
+ lines.map(cleanUp).filter(notBlank).join('\n');
}

/**
* Return a character diff for `err`.
*
Expand All @@ -344,19 +398,28 @@ function pad(str, len) {
*/

function errorDiff(err, type, escape) {
return diff['diff' + type](err.actual, err.expected).map(function(str){
if (escape) {
str.value = str.value
.replace(/\t/g, '<tab>')
.replace(/\r/g, '<CR>')
.replace(/\n/g, '<LF>\n');
}
var actual = escape ? escapeInvisibles(err.actual) : err.actual;
var expected = escape ? escapeInvisibles(err.expected) : err.expected;
return diff['diff' + type](actual, expected).map(function(str){
if (str.added) return colorLines('diff added', str.value);
if (str.removed) return colorLines('diff removed', str.value);
return str.value;
}).join('');
}

/**
* Returns a string with all invisible characters in plain text
*
* @param {String} line
* @return {String}
* @api private
*/
function escapeInvisibles(line) {
return line.replace(/\t/g, '<tab>')
.replace(/\r/g, '<CR>')
.replace(/\n/g, '<LF>\n');
}

/**
* Color lines for `str`, using the color `name`.
*
Expand Down
127 changes: 94 additions & 33 deletions mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -1833,6 +1833,12 @@ exports = module.exports = Base;

exports.useColors = isatty;

/**
* Inline diffs instead of +/-
*/

exports.inlineDiffs = false;

/**
* Default color map.
*/
Expand Down Expand Up @@ -1973,31 +1979,12 @@ exports.list = function(failures){

// actual / expected diff
if ('string' == typeof actual && 'string' == typeof expected) {
msg = errorDiff(err, 'WordsWithSpace', escape);

// linenos
var lines = msg.split('\n');
if (lines.length > 4) {
var width = String(lines.length).length;
msg = lines.map(function(str, i){
return pad(++i, width) + ' |' + ' ' + str;
}).join('\n');
fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
if (exports.inlineDiffs) {
msg = inlineDiff(err, escape);
} else {
msg = unifiedDiff(err, escape);
}

// legend
msg = '\n'
+ color('diff removed', 'actual')
+ ' '
+ color('diff added', 'expected')
+ '\n\n'
+ msg
+ '\n';

// indent
msg = msg.replace(/^/gm, ' ');

fmt = color('error title', ' %s) %s:\n%s')
+ color('error stack', '\n%s\n');
}

// indent stack trace without msg
Expand Down Expand Up @@ -2133,6 +2120,73 @@ function pad(str, len) {
return Array(len - str.length + 1).join(' ') + str;
}


/**
* Returns an inline diff between 2 strings with coloured ANSI output
*
* @param {Error} Error with actual/expected
* @return {String} Diff
* @api private
*/

function inlineDiff(err) {
var msg = errorDiff(err, 'WordsWithSpace', escape);

// linenos
var lines = msg.split('\n');
if (lines.length > 4) {
var width = String(lines.length).length;
msg = lines.map(function(str, i){
return pad(++i, width) + ' |' + ' ' + str;
}).join('\n');
}

// legend
msg = '\n'
+ color('diff removed', 'actual')
+ ' '
+ color('diff added', 'expected')
+ '\n\n'
+ msg
+ '\n';

// indent
msg = msg.replace(/^/gm, ' ');
return msg;
}

/**
* Returns a unified diff between 2 strings
*
* @param {Error} Error with actual/expected
* @return {String} Diff
* @api private
*/

function unifiedDiff(err, escape) {
var indent = ' ';
function cleanUp(line) {
if (escape) {
line = escapeInvisibles(line);
}
if (line[0] === '+') return indent + colorLines('diff added', line);
if (line[0] === '-') return indent + colorLines('diff removed', line);
if (line.match(/\@\@/)) return null;
if (line.match(/\\ No newline/)) return null;
else return indent + line;
}
function notBlank(line) {
return line != null;
}
msg = diff.createPatch('string', err.actual, err.expected);
var lines = msg.split('\n').splice(4);
return '\n '
+ colorLines('diff added', '+ expected') + ' '
+ colorLines('diff removed', '- actual')
+ '\n\n'
+ lines.map(cleanUp).filter(notBlank).join('\n');
}

/**
* Return a character diff for `err`.
*
Expand All @@ -2142,19 +2196,28 @@ function pad(str, len) {
*/

function errorDiff(err, type, escape) {
return diff['diff' + type](err.actual, err.expected).map(function(str){
if (escape) {
str.value = str.value
.replace(/\t/g, '<tab>')
.replace(/\r/g, '<CR>')
.replace(/\n/g, '<LF>\n');
}
var actual = escape ? escapeInvisibles(err.actual) : err.actual;
var expected = escape ? escapeInvisibles(err.expected) : err.expected;
return diff['diff' + type](actual, expected).map(function(str){
if (str.added) return colorLines('diff added', str.value);
if (str.removed) return colorLines('diff removed', str.value);
return str.value;
}).join('');
}

/**
* Returns a string with all invisible characters in plain text
*
* @param {String} line
* @return {String}
* @api private
*/
function escapeInvisibles(line) {
return line.replace(/\t/g, '<tab>')
.replace(/\r/g, '<CR>')
.replace(/\n/g, '<LF>\n');
}

/**
* Color lines for `str`, using the color `name`.
*
Expand Down Expand Up @@ -4402,9 +4465,7 @@ Runner.prototype.checkGlobals = function(test){
if(this.prevGlobalsLength == globals.length) return;
this.prevGlobalsLength = globals.length;

//console.time('filterLeaksTime');
leaks = filterLeaks(ok, globals);
//console.timeEnd('filterLeaksTime');
this._globals = this._globals.concat(leaks);

if (leaks.length > 1) {
Expand Down
Loading

0 comments on commit eaaace7

Please sign in to comment.