Skip to content

Commit

Permalink
Fix diffs of circular structures (close mochajs#1198, fix mochajs#1179)
Browse files Browse the repository at this point in the history
  • Loading branch information
travisjeffery authored and tandrewnichols committed Dec 15, 2014
1 parent de7fbdf commit 91f7395
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 51 deletions.
52 changes: 2 additions & 50 deletions lib/reporters/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ exports.list = function(failures){
// explicitly show diff
if (err.showDiff && sameType(actual, expected)) {
escape = false;
err.actual = actual = stringify(canonicalize(actual));
err.expected = expected = stringify(canonicalize(expected));
err.actual = actual = utils.stringify(actual);
err.expected = expected = utils.stringify(expected);
}

// actual / expected diff
Expand Down Expand Up @@ -453,53 +453,6 @@ function colorLines(name, str) {
}).join('\n');
}

/**
* Stringify `obj`.
*
* @param {Object} obj
* @return {String}
* @api private
*/

function stringify(obj) {
if (obj instanceof RegExp) return obj.toString();
return JSON.stringify(obj, null, 2);
}

/**
* Return a new object that has the keys in sorted order.
* @param {Object} obj
* @return {Object}
* @api private
*/

function canonicalize(obj, stack) {
stack = stack || [];

if (utils.indexOf(stack, obj) !== -1) return obj;

var canonicalizedObj;

if ('[object Array]' == {}.toString.call(obj)) {
stack.push(obj);
canonicalizedObj = utils.map(obj, function(item) {
return canonicalize(item, stack);
});
stack.pop();
} else if (typeof obj === 'object' && obj !== null) {
stack.push(obj);
canonicalizedObj = {};
utils.forEach(utils.keys(obj).sort(), function(key) {
canonicalizedObj[key] = canonicalize(obj[key], stack);
});
stack.pop();
} else {
canonicalizedObj = obj;
}

return canonicalizedObj;
}

/**
* Check that a / b have the same type.
*
Expand All @@ -514,4 +467,3 @@ function sameType(a, b) {
b = Object.prototype.toString.call(b);
return a == b;
}

48 changes: 48 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,3 +300,51 @@ exports.highlightTags = function(name) {
code[i].innerHTML = highlight(code[i].innerHTML);
}
};


/**
* Stringify `obj`.
*
* @param {Object} obj
* @return {String}
* @api private
*/

exports.stringify = function(obj) {
if (obj instanceof RegExp) return obj.toString();
return JSON.stringify(exports.canonicalize(obj), null, 2);
}

/**
* Return a new object that has the keys in sorted order.
* @param {Object} obj
* @return {Object}
* @api private
*/

exports.canonicalize = function(obj, stack) {
stack = stack || [];

if (exports.indexOf(stack, obj) !== -1) return '[Circular]';

var canonicalizedObj;

if ({}.toString.call(obj) === '[object Array]') {
stack.push(obj);
canonicalizedObj = exports.map(obj, function(item) {
return exports.canonicalize(item, stack);
});
stack.pop();
} else if (typeof obj === 'object' && obj !== null) {
stack.push(obj);
canonicalizedObj = {};
exports.forEach(exports.keys(obj).sort(), function(key) {
canonicalizedObj[key] = exports.canonicalize(obj[key], stack);
});
stack.pop();
} else {
canonicalizedObj = obj;
}

return canonicalizedObj;
}
18 changes: 17 additions & 1 deletion test/acceptance/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,20 @@ describe('lib/utils', function () {
utils.clean(fn).should.equal("foo()");
});
});
});

describe('stringify', function(){
it('should canoncalize the object', function(){
var travis = { name: 'travis', age: 24 };
var travis2 = { age: 24, name: 'travis' };

utils.stringify(travis).should.equal(utils.stringify(travis2));
});

it('should handle circular structures', function(){
var travis = { name: 'travis' };
travis.whoami = travis;

utils.stringify(travis).should.equal('{\n "name": "travis",\n "whoami": "[Circular]"\n}');
});
});
});

0 comments on commit 91f7395

Please sign in to comment.