big-object-diff
Advanced tools
Comparing version 0.5.0 to 0.6.0
@@ -25,2 +25,3 @@ // Copyright (c) 2014 Titanium I.T. LLC. All rights reserved. For license, see "README" or "LICENSE" file. | ||
task("test", [], function() { | ||
process.stdout.write("Testing: "); | ||
var mocha = new Mocha({ui: "bdd"}); | ||
@@ -27,0 +28,0 @@ testFiles().forEach(mocha.addFile.bind(mocha)); |
{ | ||
"name": "big-object-diff", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"engines": { | ||
"node": ">=0.10" | ||
}, | ||
"description": "A tool for visualizing the differences between large JavaScript objects", | ||
"description": "A tool for visualizing the differences between large JavaScript objects.", | ||
"main": "src/index.js", | ||
@@ -9,0 +9,0 @@ "scripts": { |
@@ -25,7 +25,7 @@ # Big Object Diff | ||
Deep compare one object to another and return the differences as a formatted string. | ||
Deep compare one object to another and return the differences as a formatted string, or "" if there are no differences. | ||
### `render(obj)` | ||
Render an object and return the result as a formatted string. | ||
Render an object or other variable and return the result as a formatted string. | ||
@@ -39,2 +39,4 @@ ### `match(a, b)` | ||
* 0.6.0 renderDiff() collapses objects as much as possible | ||
* 0.5.0 Initial release | ||
@@ -41,0 +43,0 @@ |
@@ -33,15 +33,7 @@ // Copyright (c) 2014 Titanium I.T. LLC. All rights reserved. For license, see "README" or "LICENSE" file. | ||
expect(diff.renderDiff(99, { a: 1 })).to.equal( | ||
"// expected 99 but got:\n" + | ||
" {\n" + | ||
" a: 1\n" + | ||
" }" | ||
); | ||
expect(diff.renderDiff({ a: {} }, null)).to.equal("null // expected {...}"); | ||
expect(diff.renderDiff(null, { a: {} })).to.equal("{...} // expected null"); | ||
expect(diff.renderDiff({ a: 1 }, 99)).to.equal( | ||
"99 // expected:\n" + | ||
" {\n" + | ||
" a: 1\n" + | ||
" }" | ||
); | ||
expect(diff.renderDiff(99, { a: {} })).to.equal("{...} // expected 99"); | ||
expect(diff.renderDiff({ a: {} }, 99)).to.equal("99 // expected {...}"); | ||
}); | ||
@@ -97,2 +89,27 @@ | ||
it("collapses extra and missing objects", function() { | ||
expect(diff.renderDiff({ a: { b: 1 }}, { b: { c: 2 }})).to.equal( | ||
"{\n" + | ||
" // missing properties:\n" + | ||
" a: {...}\n" + | ||
" // extra properties:\n" + | ||
" b: {...}\n" + | ||
"}" | ||
); | ||
expect(diff.renderDiff({ a: null }, {})).to.equal( | ||
"{\n" + | ||
" // missing properties:\n" + | ||
" a: null\n" + | ||
"}" | ||
); | ||
expect(diff.renderDiff({ a: {} }, {})).to.equal( | ||
"{\n" + | ||
" // missing properties:\n" + | ||
" a: {}\n" + | ||
"}" | ||
); | ||
}); | ||
it("complex comparison", function() { | ||
@@ -129,15 +146,7 @@ expect(diff.renderDiff({ a: 1, b: { b1: 2 }, c: 3 }, { a: 99, b: { b1: 2, b2: 3 } })).to.equal( | ||
expect(diff.renderDiff(99, [ 1 ])).to.equal( | ||
"// expected 99 but got:\n" + | ||
" [\n" + | ||
" 0: 1\n" + | ||
" ]" | ||
); | ||
expect(diff.renderDiff([ [] ], null)).to.equal("null // expected [...]"); | ||
expect(diff.renderDiff(null, { a: {} })).to.equal("{...} // expected null"); | ||
expect(diff.renderDiff([ 1 ], 99)).to.equal( | ||
"99 // expected:\n" + | ||
" [\n" + | ||
" 0: 1\n" + | ||
" ]" | ||
); | ||
expect(diff.renderDiff(99, { a: {} })).to.equal("{...} // expected 99"); | ||
expect(diff.renderDiff({ a: {} }, 99)).to.equal("99 // expected {...}"); | ||
}); | ||
@@ -194,6 +203,33 @@ | ||
}); | ||
it("collapses extra and missing arrays", function() { | ||
var a = [ [ 0 ] ]; | ||
var b = []; | ||
b[1] = [ 1 ]; | ||
expect(diff.renderDiff(a, b)).to.equal( | ||
"[\n" + | ||
" // missing properties:\n" + | ||
" 0: [...]\n" + | ||
" // extra properties:\n" + | ||
" 1: [...]\n" + | ||
"]" | ||
); | ||
expect(diff.renderDiff([ null ], [])).to.equal( | ||
"[\n" + | ||
" // missing properties:\n" + | ||
" 0: null\n" + | ||
"]" | ||
); | ||
expect(diff.renderDiff([ [] ], [])).to.equal( | ||
"[\n" + | ||
" // missing properties:\n" + | ||
" 0: []\n" + | ||
"]" | ||
); | ||
}); | ||
}); | ||
describe("arrays and objects compared:", function() { | ||
it("both empty", function() { | ||
@@ -205,53 +241,11 @@ expect(diff.renderDiff([], {})).to.equal("{} // expected []"); | ||
it("one empty", function() { | ||
expect(diff.renderDiff([], { a: 1 })).to.equal( | ||
"// expected [] but got:\n" + | ||
" {\n" + | ||
" a: 1\n" + | ||
" }" | ||
); | ||
expect(diff.renderDiff({ a: 1 }, [])).to.equal( | ||
"[] // expected:\n" + | ||
" {\n" + | ||
" a: 1\n" + | ||
" }" | ||
); | ||
expect(diff.renderDiff({}, [ 1 ])).to.equal( | ||
"// expected {} but got:\n" + | ||
" [\n" + | ||
" 0: 1\n" + | ||
" ]" | ||
); | ||
expect(diff.renderDiff([ 1 ], {})).to.equal( | ||
"{} // expected:\n" + | ||
" [\n" + | ||
" 0: 1\n" + | ||
" ]" | ||
); | ||
expect(diff.renderDiff([], { a: 1 })).to.equal("{...} // expected []"); | ||
expect(diff.renderDiff({ a: 1 }, [])).to.equal("[] // expected {...}"); | ||
expect(diff.renderDiff({}, [ 1 ])).to.equal("[...] // expected {}"); | ||
expect(diff.renderDiff([ 1 ], {})).to.equal("{} // expected [...]"); | ||
}); | ||
it("neither empty", function() { | ||
expect(diff.renderDiff({ a: 1 }, [ 2 ])).to.equal( | ||
"// expected object:\n" + | ||
" {\n" + | ||
" a: 1\n" + | ||
" }\n" + | ||
"// but got array:\n" + | ||
" [\n" + | ||
" 0: 2\n" + | ||
" ]" | ||
); | ||
expect(diff.renderDiff([ 1 ], { a: 2 })).to.equal( | ||
"// expected array:\n" + | ||
" [\n" + | ||
" 0: 1\n" + | ||
" ]\n" + | ||
"// but got object:\n" + | ||
" {\n" + | ||
" a: 2\n" + | ||
" }" | ||
); | ||
expect(diff.renderDiff({ a: 1 }, [ 2 ])).to.equal("[...] // expected {...}"); | ||
expect(diff.renderDiff([ 1 ], { a: 2 })).to.equal("{...} // expected [...]"); | ||
}); | ||
@@ -258,0 +252,0 @@ }); |
119
src/index.js
@@ -13,12 +13,4 @@ // Copyright (c) 2014 Titanium I.T. LLC. All rights reserved. For license, see "README" or "LICENSE" file. | ||
var expectedIsArray = Array.isArray(expected); | ||
var actualIsArray = Array.isArray(actual); | ||
var expectedIsObject = (typeof expected === "object") && !expectedIsArray; | ||
var actualIsObject = (typeof actual === "object") && !actualIsArray; | ||
if (expectedIsArray && actualIsObject || expectedIsObject && actualIsArray) { | ||
return arrayObjectRenderDiff(indent, expected, actual); | ||
} | ||
if (expectedIsArray || actualIsArray) return arrayRenderDiff(indent, expected, actual); | ||
if (actualIsObject || expectedIsObject) return objectRenderDiff(indent, expected, actual); | ||
if (isArray(expected) && isArray(actual)) return arrayRenderDiff(indent, expected, actual); | ||
if (isObject(actual) && isObject(expected)) return objectRenderDiff(indent, expected, actual); | ||
else return flatRenderDiff(expected, actual); | ||
@@ -28,7 +20,7 @@ } | ||
function flatRenderDiff(expected, actual) { | ||
var renderedActual = exports.render(actual); | ||
var renderedExpected = exports.render(expected); | ||
var renderedActual = flatRender(actual); | ||
var renderedExpected = flatRender(expected); | ||
if (typeof expected === "function" && typeof actual === "function") { | ||
if (renderedActual === renderedExpected) renderedExpected = "different " + renderedExpected; | ||
if (isFunction(expected) && isFunction(actual) && renderedActual === renderedExpected) { | ||
renderedExpected = "different " + renderedExpected; | ||
} | ||
@@ -39,52 +31,10 @@ | ||
function arrayObjectRenderDiff(oldIndent, expected, actual) { | ||
var indent = oldIndent + INDENT_TEXT; | ||
if (Array.isArray(expected)) { | ||
if (expected.length === 0) return objectRenderDiff(oldIndent, expected, actual); | ||
if (Object.getOwnPropertyNames(actual).length === 0) return arrayRenderDiff(oldIndent, expected, actual); | ||
return "// expected array:\n" + indent + renderWithIndent(indent, expected) + | ||
"\n" + oldIndent + "// but got object:\n" + indent + renderWithIndent(indent, actual); | ||
} | ||
else { | ||
if (actual.length === 0) return objectRenderDiff(oldIndent, expected, actual); | ||
if (Object.getOwnPropertyNames(expected).length === 0) return arrayRenderDiff(oldIndent, expected, actual); | ||
return "// expected object:\n" + indent + renderWithIndent(indent, expected) + | ||
"\n" + oldIndent + "// but got array:\n" + indent + renderWithIndent(indent, actual); | ||
} | ||
function arrayRenderDiff(indent, expected, actual) { | ||
return "[" + renderPropertiesDiff(indent, expected, actual, true) + "\n" + indent + "]"; | ||
} | ||
function arrayRenderDiff(oldIndent, expected, actual) { | ||
var indent = oldIndent + INDENT_TEXT; | ||
if (!Array.isArray(expected)) { | ||
if (actual.length === 0) return flatRenderDiff(expected, actual); | ||
return "// expected " + exports.render(expected) + " but got:\n" + indent + renderWithIndent(indent, actual); | ||
} | ||
if (!Array.isArray(actual)) { | ||
if (expected.length === 0) return flatRenderDiff(expected, actual); | ||
return exports.render(actual) + " // expected:\n" + indent + renderWithIndent(indent, expected); | ||
} | ||
return "[" + renderPropertiesDiff(oldIndent, expected, actual, true) + "\n" + oldIndent + "]"; | ||
function objectRenderDiff(indent, expected, actual) { | ||
return "{" + renderPropertiesDiff(indent, expected, actual, false) + "\n" + indent + "}"; | ||
} | ||
function objectRenderDiff(oldIndent, expected, actual) { | ||
var indent = oldIndent + INDENT_TEXT; | ||
if (expected === null || actual === null) return flatRenderDiff(expected, actual); | ||
if (typeof expected !== "object" || Array.isArray(expected)) { | ||
if (Object.getOwnPropertyNames(actual).length === 0) return flatRenderDiff(expected, actual); | ||
return "// expected " + exports.render(expected) + " but got:\n" + indent + renderWithIndent(indent, actual); | ||
} | ||
if (typeof actual !== "object" || Array.isArray(actual)) { | ||
if (Object.getOwnPropertyNames(expected).length === 0) return flatRenderDiff(expected, actual); | ||
return exports.render(actual) + " // expected:\n" + indent + renderWithIndent(indent, expected); | ||
} | ||
return "{" + renderPropertiesDiff(oldIndent, expected, actual, false) + "\n" + oldIndent + "}"; | ||
} | ||
function renderPropertiesDiff(oldIndent, expected, actual, ignoreLengthProperty) { | ||
@@ -134,3 +84,3 @@ var indent = oldIndent + INDENT_TEXT; | ||
if (keys.length === 0) return ""; | ||
return "\n" + indent + "// " + title + ":" + renderProperties(oldIndent, obj, keys, false); | ||
return "\n" + indent + "// " + title + ":" + renderProperties(oldIndent, obj, keys, false, true); | ||
} | ||
@@ -149,8 +99,9 @@ | ||
exports.render = function(obj) { | ||
return renderWithIndent("", obj); | ||
return renderWithIndent("", obj, false); | ||
}; | ||
function renderWithIndent(indent, obj) { | ||
if (Array.isArray(obj)) return arrayRender(indent, obj); | ||
else if (typeof obj === "object") return objectRender(indent, obj); | ||
function renderWithIndent(indent, obj, collapseObjects) { | ||
if (collapseObjects) return flatRender(obj); | ||
else if (isArray(obj)) return arrayRender(indent, obj); | ||
else if (isObject(obj)) return objectRender(indent, obj); | ||
else return flatRender(obj); | ||
@@ -161,4 +112,13 @@ } | ||
if (obj === undefined) return "undefined"; | ||
if (obj === null) return "null"; | ||
if (typeof obj === "string") return JSON.stringify(obj); | ||
if (typeof obj === "function") { | ||
if (isArray(obj)) { | ||
if (obj.length === 0) return "[]"; | ||
return "[...]"; | ||
} | ||
if (isObject(obj)) { | ||
if (Object.getOwnPropertyNames(obj).length === 0) return "{}"; | ||
else return "{...}"; | ||
} | ||
if (isFunction(obj)) { | ||
if (!obj.name) return "<anon>()"; | ||
@@ -174,3 +134,3 @@ else return obj.name + "()"; | ||
var properties = renderProperties(indent, obj, Object.getOwnPropertyNames(obj), true); | ||
var properties = renderProperties(indent, obj, Object.getOwnPropertyNames(obj), true, false); | ||
return "[" + properties + "\n" + indent + "]"; | ||
@@ -180,14 +140,13 @@ } | ||
function objectRender(indent, obj) { | ||
if (obj === null) return "null"; | ||
if (Object.getOwnPropertyNames(obj).length === 0) return "{}"; | ||
var properties = renderProperties(indent, obj, Object.getOwnPropertyNames(obj), false); | ||
var properties = renderProperties(indent, obj, Object.getOwnPropertyNames(obj), false, false); | ||
return "{" + properties + "\n" + indent + "}"; | ||
} | ||
function renderProperties(indent, obj, keys, ignoreLengthProperty) { | ||
function renderProperties(indent, obj, keys, ignoreLengthProperty, collapseObjects) { | ||
var newIndent = indent + INDENT_TEXT; | ||
var properties = keys.reduce(function(accumulated, key) { | ||
if (ignoreLengthProperty && key === "length") return accumulated; | ||
return accumulated + "\n" + newIndent + key + ": " + renderWithIndent(newIndent, obj[key]); | ||
return accumulated + "\n" + newIndent + key + ": " + renderWithIndent(newIndent, obj[key], collapseObjects); | ||
}, ""); | ||
@@ -198,3 +157,3 @@ return properties; | ||
exports.match = function(a, b) { | ||
if (typeof a === "object" && typeof b === "object") return objectMatch(a, b); | ||
if (typeof a === "object" && typeof b === "object") return objectAndArrayMatch(a, b); | ||
else return flatMatch(a, b); | ||
@@ -209,3 +168,3 @@ }; | ||
function objectMatch(a, b) { | ||
function objectAndArrayMatch(a, b) { | ||
if (a === b) return true; | ||
@@ -224,2 +183,14 @@ if (a === null) return b === null; | ||
}); | ||
} | ||
} | ||
function isArray(obj) { | ||
return Array.isArray(obj); | ||
} | ||
function isObject(obj) { | ||
return typeof obj === "object" && obj !== null && !isArray(obj); | ||
} | ||
function isFunction(obj) { | ||
return typeof obj === "function"; | ||
} |
66
22914
648