diff --git a/lib/reporters/json.js b/lib/reporters/json.js index 97989ccbb3..efc79b4b02 100644 --- a/lib/reporters/json.js +++ b/lib/reporters/json.js @@ -67,17 +67,44 @@ function JSONReporter (runner) { * @return {Object} */ function clean (test) { + var err = test.err || {}; + if (err instanceof Error) { + err = errorJSON(err); + } + return { title: test.title, fullTitle: test.fullTitle(), duration: test.duration, currentRetry: test.currentRetry(), - err: errorJSON(test.err || {}) + err: cleanCycles(err) }; } /** - * Transform `error` into a JSON object. + * Replaces any circular references inside `obj` with '[object Object]' + * + * @api private + * @param {Object} obj + * @return {Object} + */ +function cleanCycles (obj) { + var cache = []; + return JSON.parse(JSON.stringify(obj, function (key, value) { + if (typeof value === 'object' && value !== null) { + if (cache.indexOf(value) !== -1) { + // Instead of going in a circle, we'll print [object Object] + return '' + value; + } + cache.push(value); + } + + return value; + })); +} + +/** + * Transform an Error object into a JSON object. * * @api private * @param {Error} err diff --git a/test/reporters/json.spec.js b/test/reporters/json.spec.js index d34105ff89..a40eead75f 100644 --- a/test/reporters/json.spec.js +++ b/test/reporters/json.spec.js @@ -58,4 +58,31 @@ describe('json reporter', function () { done(); }); }); + + it('should handle circular objects in errors', function (done) { + var testTitle = 'json test 1'; + function CircleError () { + this.message = 'oh shit'; + this.circular = this; + } + var error = new CircleError(); + + suite.addTest(new Test(testTitle, function (done) { + throw error; + })); + + runner.run(function (failureCount) { + expect(failureCount).to.equal(1); + expect(runner).to.have.property('testResults'); + expect(runner.testResults).to.have.property('failures'); + expect(runner.testResults.failures).to.be.an(Array); + expect(runner.testResults.failures).to.have.length(1); + + var failure = runner.testResults.failures[0]; + expect(failure).to.have.property('title', testTitle); + expect(failure).to.have.property('err'); + + done(); + }); + }); });