Use custom object formatters for any part of a diff, not just leaf nodes

This commit is contained in:
Steve Gravrock
2020-02-01 18:49:06 -08:00
committed by Steve Gravrock
parent 25816a6e77
commit 873d1c2945
15 changed files with 669 additions and 141 deletions

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2008-2019 Pivotal Labs Copyright (c) 2008-2020 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
@@ -106,6 +106,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
j$.DiffBuilder = jRequire.DiffBuilder(j$); j$.DiffBuilder = jRequire.DiffBuilder(j$);
j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$); j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$);
j$.ObjectPath = jRequire.ObjectPath(j$); j$.ObjectPath = jRequire.ObjectPath(j$);
j$.MismatchTree = jRequire.MismatchTree(j$);
j$.GlobalErrors = jRequire.GlobalErrors(j$); j$.GlobalErrors = jRequire.GlobalErrors(j$);
j$.Truthy = jRequire.Truthy(j$); j$.Truthy = jRequire.Truthy(j$);
@@ -4187,20 +4188,54 @@ getJasmineRequireObj().toBeResolvedTo = function(j$) {
}; };
}; };
getJasmineRequireObj().DiffBuilder = function(j$) { getJasmineRequireObj().DiffBuilder = function (j$) {
return function DiffBuilder(config) { return function DiffBuilder(config) {
var path = new j$.ObjectPath(), var prettyPrinter = (config || {}).prettyPrinter || j$.makePrettyPrinter(),
mismatches = [], mismatches = new j$.MismatchTree(),
prettyPrinter = (config || {}).prettyPrinter || j$.makePrettyPrinter(); path = new j$.ObjectPath(),
actualRoot = undefined,
expectedRoot = undefined;
return { return {
record: function (actual, expected, formatter) { setRoots: function (actual, expected) {
formatter = formatter || defaultFormatter; actualRoot = actual;
mismatches.push(formatter(actual, expected, path, prettyPrinter)); expectedRoot = expected;
},
recordMismatch: function (formatter) {
mismatches.add(path, formatter);
}, },
getMessage: function () { getMessage: function () {
return mismatches.join('\n'); var messages = [];
mismatches.traverse(function (path, isLeaf, formatter) {
var actualCustom, expectedCustom, useCustom,
actual = path.dereference(actualRoot),
expected = path.dereference(expectedRoot);
if (formatter) {
messages.push(formatter(actual, expected, path, prettyPrinter));
return true;
}
actualCustom = prettyPrinter.customFormat_(actual);
expectedCustom = prettyPrinter.customFormat_(expected);
useCustom = !(j$.util.isUndefined(actualCustom) && j$.util.isUndefined(expectedCustom));
if (useCustom) {
messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path));
return false; // don't recurse further
}
if (isLeaf) {
messages.push(defaultFormatter(actual, expected, path, prettyPrinter));
}
return true;
});
return messages.join('\n');
}, },
withPath: function (pathComponent, block) { withPath: function (pathComponent, block) {
@@ -4211,12 +4246,16 @@ getJasmineRequireObj().DiffBuilder = function(j$) {
} }
}; };
function defaultFormatter (actual, expected, path, prettyPrinter) { function defaultFormatter(actual, expected, path, prettyPrinter) {
return wrapPrettyPrinted(prettyPrinter(actual), prettyPrinter(expected), path);
}
function wrapPrettyPrinted(actual, expected, path) {
return 'Expected ' + return 'Expected ' +
path + (path.depth() ? ' = ' : '') + path + (path.depth() ? ' = ' : '') +
prettyPrinter(actual) + actual +
' to equal ' + ' to equal ' +
prettyPrinter(expected) + expected +
'.'; '.';
} }
}; };
@@ -4293,7 +4332,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
if (asymmetricA) { if (asymmetricA) {
result = a.asymmetricMatch(b, shim); result = a.asymmetricMatch(b, shim);
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
} }
@@ -4301,7 +4340,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
if (asymmetricB) { if (asymmetricB) {
result = b.asymmetricMatch(a, shim); result = b.asymmetricMatch(a, shim);
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
} }
@@ -4319,6 +4358,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
customTesters = customTesters || this.customTesters_; customTesters = customTesters || this.customTesters_;
diffBuilder = diffBuilder || j$.NullDiffBuilder(); diffBuilder = diffBuilder || j$.NullDiffBuilder();
diffBuilder.setRoots(a, b);
return this.eq_(a, b, [], [], customTesters, diffBuilder); return this.eq_(a, b, [], [], customTesters, diffBuilder);
}; };
@@ -4337,7 +4377,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
var customTesterResult = customTesters[i](a, b); var customTesterResult = customTesters[i](a, b);
if (!j$.util.isUndefined(customTesterResult)) { if (!j$.util.isUndefined(customTesterResult)) {
if (!customTesterResult) { if (!customTesterResult) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return customTesterResult; return customTesterResult;
} }
@@ -4346,7 +4386,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
if (a instanceof Error && b instanceof Error) { if (a instanceof Error && b instanceof Error) {
result = a.message == b.message; result = a.message == b.message;
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
} }
@@ -4356,7 +4396,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
if (a === b) { if (a === b) {
result = a !== 0 || 1 / a == 1 / b; result = a !== 0 || 1 / a == 1 / b;
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
} }
@@ -4364,13 +4404,13 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
if (a === null || b === null) { if (a === null || b === null) {
result = a === b; result = a === b;
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
} }
var className = Object.prototype.toString.call(a); var className = Object.prototype.toString.call(a);
if (className != Object.prototype.toString.call(b)) { if (className != Object.prototype.toString.call(b)) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
switch (className) { switch (className) {
@@ -4380,7 +4420,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
// equivalent to `new String("5")`. // equivalent to `new String("5")`.
result = a == String(b); result = a == String(b);
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
case '[object Number]': case '[object Number]':
@@ -4388,7 +4428,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
// other numeric values. // other numeric values.
result = a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b); result = a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b);
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
case '[object Date]': case '[object Date]':
@@ -4398,7 +4438,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
// of `NaN` are not equivalent. // of `NaN` are not equivalent.
result = +a == +b; result = +a == +b;
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
// RegExps are compared by their source patterns and flags. // RegExps are compared by their source patterns and flags.
@@ -4409,7 +4449,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
a.ignoreCase == b.ignoreCase; a.ignoreCase == b.ignoreCase;
} }
if (typeof a != 'object' || typeof b != 'object') { if (typeof a != 'object' || typeof b != 'object') {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
@@ -4419,12 +4459,12 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
// At first try to use DOM3 method isEqualNode // At first try to use DOM3 method isEqualNode
result = a.isEqualNode(b); result = a.isEqualNode(b);
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
} }
if (aIsDomNode || bIsDomNode) { if (aIsDomNode || bIsDomNode) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
@@ -4454,7 +4494,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
diffBuilder.withPath('length', function() { diffBuilder.withPath('length', function() {
if (aLength !== bLength) { if (aLength !== bLength) {
diffBuilder.record(aLength, bLength); diffBuilder.recordMismatch();
result = false; result = false;
} }
}); });
@@ -4462,7 +4502,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
for (i = 0; i < aLength || i < bLength; i++) { for (i = 0; i < aLength || i < bLength; i++) {
diffBuilder.withPath(i, function() { diffBuilder.withPath(i, function() {
if (i >= bLength) { if (i >= bLength) {
diffBuilder.record(a[i], void 0, actualArrayIsLongerFormatter.bind(null, self.pp)); diffBuilder.recordMismatch(actualArrayIsLongerFormatter.bind(null, self.pp));
result = false; result = false;
} else { } else {
result = self.eq_(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack, customTesters, diffBuilder) && result; result = self.eq_(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack, customTesters, diffBuilder) && result;
@@ -4474,7 +4514,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
} }
} else if (j$.isMap(a) && j$.isMap(b)) { } else if (j$.isMap(a) && j$.isMap(b)) {
if (a.size != b.size) { if (a.size != b.size) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
@@ -4516,12 +4556,12 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
} }
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
} else if (j$.isSet(a) && j$.isSet(b)) { } else if (j$.isSet(a) && j$.isSet(b)) {
if (a.size != b.size) { if (a.size != b.size) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
@@ -4566,7 +4606,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
} }
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
} else { } else {
@@ -4579,7 +4619,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
a instanceof aCtor && b instanceof bCtor && a instanceof aCtor && b instanceof bCtor &&
!(aCtor instanceof aCtor && bCtor instanceof bCtor)) { !(aCtor instanceof aCtor && bCtor instanceof bCtor)) {
diffBuilder.record(a, b, constructorsAreDifferentFormatter.bind(null, this.pp)); diffBuilder.recordMismatch(constructorsAreDifferentFormatter.bind(null, this.pp));
return false; return false;
} }
} }
@@ -4590,7 +4630,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
// Ensure that both objects contain the same number of properties before comparing deep equality. // Ensure that both objects contain the same number of properties before comparing deep equality.
if (keys(b, className == '[object Array]').length !== size) { if (keys(b, className == '[object Array]').length !== size) {
diffBuilder.record(a, b, objectKeysAreDifferentFormatter.bind(null, this.pp)); diffBuilder.recordMismatch(objectKeysAreDifferentFormatter.bind(null, this.pp));
return false; return false;
} }
@@ -4598,7 +4638,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
key = aKeys[i]; key = aKeys[i];
// Deep compare each member // Deep compare each member
if (!j$.util.has(b, key)) { if (!j$.util.has(b, key)) {
diffBuilder.record(a, b, objectKeysAreDifferentFormatter.bind(null, this.pp)); diffBuilder.recordMismatch(objectKeysAreDifferentFormatter.bind(null, this.pp));
result = false; result = false;
continue; continue;
} }
@@ -4704,12 +4744,75 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
} }
function isDiffBuilder(obj) { function isDiffBuilder(obj) {
return obj && typeof obj.record === 'function'; return obj && typeof obj.recordMismatch === 'function';
} }
return MatchersUtil; return MatchersUtil;
}; };
getJasmineRequireObj().MismatchTree = function (j$) {
/*
To be able to apply custom object formatters at all possible levels of an
object graph, DiffBuilder needs to be able to know not just where the
mismatch occurred but also all ancestors of the mismatched value in both
the expected and actual object graphs. MismatchTree maintains that context
and provides it via the traverse method.
*/
function MismatchTree(path) {
this.path = path || new j$.ObjectPath([]);
this.formatter = undefined;
this.children = [];
this.isMismatch = false;
}
MismatchTree.prototype.add = function (path, formatter) {
var key, child;
if (path.depth() === 0) {
this.formatter = formatter;
this.isMismatch = true;
} else {
key = path.components[0];
path = path.shift();
child = this.child(key);
if (!child) {
child = new MismatchTree(this.path.add(key));
this.children.push(child);
}
child.add(path, formatter);
}
};
MismatchTree.prototype.traverse = function (visit) {
var i, hasChildren = this.children.length > 0;
if (this.isMismatch || hasChildren) {
if (visit(this.path, !hasChildren, this.formatter)) {
for (i = 0; i < this.children.length; i++) {
this.children[i].traverse(visit);
}
}
}
};
MismatchTree.prototype.child = function(key) {
var i, pathEls;
for (i = 0; i < this.children.length; i++) {
pathEls = this.children[i].path.components;
if (pathEls[pathEls.length - 1] === key) {
return this.children[i];
}
}
};
return MismatchTree;
};
getJasmineRequireObj().nothing = function() { getJasmineRequireObj().nothing = function() {
/** /**
* {@link expect} nothing explicitly. * {@link expect} nothing explicitly.
@@ -4738,7 +4841,8 @@ getJasmineRequireObj().NullDiffBuilder = function(j$) {
withPath: function(_, block) { withPath: function(_, block) {
block(); block();
}, },
record: function() {} setRoots: function() {},
recordMismatch: function() {}
}; };
}; };
}; };
@@ -4756,10 +4860,24 @@ getJasmineRequireObj().ObjectPath = function(j$) {
} }
}; };
ObjectPath.prototype.dereference = function(obj) {
var i;
for (i = 0; i < this.components.length; i++) {
obj = obj[this.components[i]];
}
return obj;
};
ObjectPath.prototype.add = function(component) { ObjectPath.prototype.add = function(component) {
return new ObjectPath(this.components.concat([component])); return new ObjectPath(this.components.concat([component]));
}; };
ObjectPath.prototype.shift = function() {
return new ObjectPath(this.components.slice(1));
};
ObjectPath.prototype.depth = function() { ObjectPath.prototype.depth = function() {
return this.components.length; return this.components.length;
}; };
@@ -6109,15 +6227,7 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
}; };
SinglePrettyPrintRun.prototype.applyCustomFormatters_ = function(value) { SinglePrettyPrintRun.prototype.applyCustomFormatters_ = function(value) {
var i, result; return customFormat(value, this.customObjectFormatters_);
for (i = 0; i < this.customObjectFormatters_.length; i++) {
result = this.customObjectFormatters_[i](value);
if (result !== undefined) {
return result;
}
}
}; };
SinglePrettyPrintRun.prototype.iterateObject = function(obj, fn) { SinglePrettyPrintRun.prototype.iterateObject = function(obj, fn) {
@@ -6390,16 +6500,31 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
return extraKeys; return extraKeys;
} }
function customFormat(value, customObjectFormatters) {
var i, result;
for (i = 0; i < customObjectFormatters.length; i++) {
result = customObjectFormatters[i](value);
if (result !== undefined) {
return result;
}
}
}
return function(customObjectFormatters) { return function(customObjectFormatters) {
customObjectFormatters = customObjectFormatters || [];
var pp = function(value) { var pp = function(value) {
var prettyPrinter = new SinglePrettyPrintRun( var prettyPrinter = new SinglePrettyPrintRun(customObjectFormatters, pp);
customObjectFormatters || [],
pp
);
prettyPrinter.format(value); prettyPrinter.format(value);
return prettyPrinter.stringParts.join(''); return prettyPrinter.stringParts.join('');
}; };
pp.customFormat_ = function(value) {
return customFormat(value, customObjectFormatters);
};
return pp; return pp;
}; };
}; };

View File

@@ -505,7 +505,7 @@ describe('PrettyPrinter', function() {
pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters), pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters),
obj = { foo: 'bar' }; obj = { foo: 'bar' };
expect(pp(obj, customObjectFormatters)).toEqual('2nd: bar'); expect(pp(obj)).toEqual('2nd: bar');
}); });
it('should fall back to built in logic if all custom object formatters return undefined', function() { it('should fall back to built in logic if all custom object formatters return undefined', function() {
@@ -517,7 +517,39 @@ describe('PrettyPrinter', function() {
pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters), pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters),
obj = { foo: 'bar' }; obj = { foo: 'bar' };
expect(pp(obj, customObjectFormatters)).toEqual("Object({ foo: 'bar' })"); expect(pp(obj)).toEqual("Object({ foo: 'bar' })");
});
});
describe('#customFormat_', function() {
it('should use the first custom object formatter that does not return undefined', function() {
var customObjectFormatters = [
function(obj) {
return undefined;
},
function(obj) {
return '2nd: ' + obj.foo;
},
function(obj) {
return '3rd: ' + obj.foo;
}
],
pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters),
obj = { foo: 'bar' };
expect(pp.customFormat_(obj)).toEqual('2nd: bar');
});
it('should return undefined if all custom object formatters return undefined', function() {
var customObjectFormatters = [
function(obj) {
return undefined;
}
],
pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters),
obj = { foo: 'bar' };
expect(pp.customFormat_(obj)).toBeUndefined();
}); });
}); });
}); });

View File

@@ -1,73 +1,136 @@
describe("DiffBuilder", function() { describe("DiffBuilder", function () {
it("records the actual and expected objects", function() { it("records the actual and expected objects", function () {
var diffBuilder = jasmineUnderTest.DiffBuilder(); var diffBuilder = jasmineUnderTest.DiffBuilder();
diffBuilder.record({x: 'actual'}, {x: 'expected'}); diffBuilder.setRoots({x: 'actual'}, {x: 'expected'});
diffBuilder.recordMismatch();
expect(diffBuilder.getMessage()).toEqual("Expected Object({ x: 'actual' }) to equal Object({ x: 'expected' })."); expect(diffBuilder.getMessage()).toEqual("Expected Object({ x: 'actual' }) to equal Object({ x: 'expected' }).");
}); });
it("prints the path at which the difference was found", function() { it("prints the path at which the difference was found", function () {
var diffBuilder = jasmineUnderTest.DiffBuilder(); var diffBuilder = jasmineUnderTest.DiffBuilder();
diffBuilder.setRoots({foo: {x: 'actual'}}, {foo: {x: 'expected'}});
diffBuilder.withPath('foo', function() { diffBuilder.withPath('foo', function () {
diffBuilder.record({x: 'actual'}, {x: 'expected'}); diffBuilder.recordMismatch();
}); });
expect(diffBuilder.getMessage()).toEqual("Expected $.foo = Object({ x: 'actual' }) to equal Object({ x: 'expected' })."); expect(diffBuilder.getMessage()).toEqual("Expected $.foo = Object({ x: 'actual' }) to equal Object({ x: 'expected' }).");
}); });
it("prints multiple messages, separated by newlines", function() { it("prints multiple messages, separated by newlines", function () {
var diffBuilder = jasmineUnderTest.DiffBuilder(); var diffBuilder = jasmineUnderTest.DiffBuilder();
diffBuilder.setRoots({foo: 1, bar: 3}, {foo: 2, bar: 4});
diffBuilder.withPath('foo', function() { diffBuilder.withPath('foo', function () {
diffBuilder.record(1, 2); diffBuilder.recordMismatch();
});
diffBuilder.withPath('bar', function () {
diffBuilder.recordMismatch();
}); });
var message = var message =
"Expected $.foo = 1 to equal 2.\n" + "Expected $.foo = 1 to equal 2.\n" +
"Expected 3 to equal 4."; "Expected $.bar = 3 to equal 4.";
diffBuilder.record(3, 4);
expect(diffBuilder.getMessage()).toEqual(message); expect(diffBuilder.getMessage()).toEqual(message);
}); });
it("allows customization of the message", function() { it("allows customization of the message", function () {
var diffBuilder = jasmineUnderTest.DiffBuilder(); var diffBuilder = jasmineUnderTest.DiffBuilder();
diffBuilder.setRoots({x: 'bar'}, {x: 'foo'});
function darthVaderFormatter(actual, expected, path) { function darthVaderFormatter(actual, expected, path) {
return "I find your lack of " + expected + " disturbing. (was " + actual + ", at " + path + ")" return "I find your lack of " + expected + " disturbing. (was " + actual + ", at " + path + ")"
} }
diffBuilder.withPath('x', function() { diffBuilder.withPath('x', function () {
diffBuilder.record('bar', 'foo', darthVaderFormatter); diffBuilder.recordMismatch(darthVaderFormatter);
}); });
expect(diffBuilder.getMessage()).toEqual("I find your lack of foo disturbing. (was bar, at $.x)"); expect(diffBuilder.getMessage()).toEqual("I find your lack of foo disturbing. (was bar, at $.x)");
}); });
it("uses the injected pretty-printer", function() { it("uses the injected pretty-printer", function () {
var prettyPrinter = function(val) { var prettyPrinter = function (val) {
return '|' + val + '|'; return '|' + val + '|';
}, },
diffBuilder = jasmineUnderTest.DiffBuilder({prettyPrinter: prettyPrinter}); diffBuilder = jasmineUnderTest.DiffBuilder({prettyPrinter: prettyPrinter});
prettyPrinter.customFormat_ = function () {
};
diffBuilder.withPath('foo', function() { diffBuilder.setRoots({foo: 'actual'}, {foo: 'expected'});
diffBuilder.record('actual', 'expected'); diffBuilder.withPath('foo', function () {
diffBuilder.recordMismatch();
}); });
expect(diffBuilder.getMessage()).toEqual("Expected $.foo = |actual| to equal |expected|."); expect(diffBuilder.getMessage()).toEqual("Expected $.foo = |actual| to equal |expected|.");
}); });
it("passes the injected pretty-printer to the diff formatter", function () {
it("passes the injected pretty-printer to the diff formatter", function() {
var diffFormatter = jasmine.createSpy('diffFormatter'), var diffFormatter = jasmine.createSpy('diffFormatter'),
prettyPrinter = function() {}, prettyPrinter = function () {
},
diffBuilder = jasmineUnderTest.DiffBuilder({prettyPrinter: prettyPrinter}); diffBuilder = jasmineUnderTest.DiffBuilder({prettyPrinter: prettyPrinter});
prettyPrinter.customFormat_ = function () {
};
diffBuilder.withPath('x', function() { diffBuilder.setRoots({x: 'bar'}, {x: 'foo'});
diffBuilder.record('bar', 'foo', diffFormatter); diffBuilder.withPath('x', function () {
diffBuilder.recordMismatch(diffFormatter);
}); });
diffBuilder.getMessage();
expect(diffFormatter).toHaveBeenCalledWith('bar', 'foo', jasmine.anything(), prettyPrinter); expect(diffFormatter).toHaveBeenCalledWith('bar', 'foo', jasmine.anything(), prettyPrinter);
}); });
it("uses custom object formatters on leaf nodes", function() {
var formatter = function(x) {
if (typeof x === 'number') {
return '[number:' + x + ']';
}
};
prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
var diffBuilder = new jasmineUnderTest.DiffBuilder({prettyPrinter: prettyPrinter});
diffBuilder.setRoots(5, 4);
diffBuilder.recordMismatch();
expect(diffBuilder.getMessage()).toEqual('Expected [number:5] to equal [number:4].');
});
it("uses custom object formatters on non leaf nodes", function () {
var formatter = function (x) {
if (x.hasOwnProperty('a')) {
return '[thing with a=' + x.a + ', b=' + JSON.stringify(x.b) + ']';
}
};
prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
var diffBuilder = new jasmineUnderTest.DiffBuilder({prettyPrinter: prettyPrinter});
var expectedMsg = 'Expected $[0].foo = [thing with a=1, b={"x":42}] to equal [thing with a=1, b={"x":43}].\n' +
"Expected $[0].bar = 'yes' to equal 'no'.";
diffBuilder.setRoots(
[{foo: {a: 1, b: {x: 42}}, bar: 'yes'}],
[{foo: {a: 1, b: {x: 43}}, bar: 'no'}]
);
diffBuilder.withPath(0, function () {
diffBuilder.withPath('foo', function () {
diffBuilder.withPath('b', function () {
diffBuilder.withPath('x', function () {
diffBuilder.recordMismatch();
});
});
});
diffBuilder.withPath('bar', function () {
diffBuilder.recordMismatch();
});
});
expect(diffBuilder.getMessage()).toEqual(expectedMsg);
});
}); });

View File

@@ -0,0 +1,136 @@
describe('MismatchTree', function () {
describe('#add', function () {
describe('When the path is empty', function () {
it('flags the root node as mismatched', function () {
var tree = new jasmineUnderTest.MismatchTree();
tree.add(new jasmineUnderTest.ObjectPath([]));
expect(tree.isMismatch).toBe(true);
});
});
describe('When the path is not empty', function () {
it('flags the node as mismatched', function () {
var tree = new jasmineUnderTest.MismatchTree();
tree.add(new jasmineUnderTest.ObjectPath(['a', 'b']));
expect(tree.child('a').child('b').isMismatch).toBe(true);
});
it('does not flag ancestors as mismatched', function () {
var tree = new jasmineUnderTest.MismatchTree();
tree.add(new jasmineUnderTest.ObjectPath(['a', 'b']));
expect(tree.isMismatch).toBe(false);
expect(tree.child('a').isMismatch).toBe(false);
});
});
it('stores the formatter on only the target node', function () {
var tree = new jasmineUnderTest.MismatchTree();
tree.add(new jasmineUnderTest.ObjectPath(['a', 'b']), formatter);
expect(tree.formatter).toBeFalsy();
expect(tree.child('a').formatter).toBeFalsy();
expect(tree.child('a').child('b').formatter).toBe(formatter);
});
it('stores the path to the node', function () {
var tree = new jasmineUnderTest.MismatchTree();
tree.add(new jasmineUnderTest.ObjectPath(['a', 'b']), formatter);
expect(tree.child('a').child('b').path.components).toEqual(['a', 'b']);
});
});
describe('#traverse', function () {
it('calls the callback for all nodes that are or contain mismatches', function () {
var tree = new jasmineUnderTest.MismatchTree();
tree.add(new jasmineUnderTest.ObjectPath(['a', 'b']), formatter);
tree.add(new jasmineUnderTest.ObjectPath(['c']));
var visit = jasmine.createSpy('visit').and.returnValue(true);
tree.traverse(visit);
expect(visit).toHaveBeenCalledWith(
new jasmineUnderTest.ObjectPath([]), false, undefined
);
expect(visit).toHaveBeenCalledWith(
new jasmineUnderTest.ObjectPath(['a']), false, undefined
);
expect(visit).toHaveBeenCalledWith(
new jasmineUnderTest.ObjectPath(['a', 'b']), true, formatter
);
expect(visit).toHaveBeenCalledWith(
new jasmineUnderTest.ObjectPath(['c']), true, undefined
);
});
it('does not call the callback if there are no mismatches', function () {
var tree = new jasmineUnderTest.MismatchTree();
var visit = jasmine.createSpy('visit');
tree.traverse(visit);
expect(visit).not.toHaveBeenCalled();
});
it('visits parents before children', function () {
var tree = new jasmineUnderTest.MismatchTree();
tree.add(new jasmineUnderTest.ObjectPath(['a', 'b']));
var visited = [];
tree.traverse(function (path) {
visited.push(path);
return true;
});
expect(visited).toEqual([
new jasmineUnderTest.ObjectPath([]),
new jasmineUnderTest.ObjectPath(['a']),
new jasmineUnderTest.ObjectPath(['a', 'b'])
]);
});
it('visits children in the order they were recorded', function() {
var tree = new jasmineUnderTest.MismatchTree();
tree.add(new jasmineUnderTest.ObjectPath(['length']));
tree.add(new jasmineUnderTest.ObjectPath([1]));
var visited = [];
tree.traverse(function (path) {
visited.push(path);
return true;
});
expect(visited).toEqual([
new jasmineUnderTest.ObjectPath([]),
new jasmineUnderTest.ObjectPath(['length']),
new jasmineUnderTest.ObjectPath([1])
]);
});
it('does not visit children if the callback returns falsy', function() {
var tree = new jasmineUnderTest.MismatchTree();
tree.add(new jasmineUnderTest.ObjectPath(['a', 'b']));
var visited = [];
tree.traverse(function (path) {
visited.push(path);
return path.depth() === 0;
});
expect(visited).toEqual([
new jasmineUnderTest.ObjectPath([]),
new jasmineUnderTest.ObjectPath(['a'])
]);
});
});
function formatter() {
}
});

View File

@@ -1,13 +1,8 @@
describe('NullDiffBuilder', function() { describe('NullDiffBuilder', function () {
it('responds to withPath() by calling the passed function', function() { it('responds to withPath() by calling the passed function', function () {
var spy = jasmine.createSpy('callback'); var spy = jasmine.createSpy('callback');
jasmineUnderTest.NullDiffBuilder().withPath('does not matter', spy); jasmineUnderTest.NullDiffBuilder().withPath('does not matter', spy);
expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalled();
}); });
it('responds to record()', function() {
expect(function() {
jasmineUnderTest.NullDiffBuilder().record('does not matter');
}).not.toThrow();
})
}); });

View File

@@ -39,5 +39,13 @@ describe('ObjectPath', function() {
expect(path.toString()).toEqual('$.foo'); expect(path.toString()).toEqual('$.foo');
expect(root.toString()).toEqual(''); expect(root.toString()).toEqual('');
}) });
describe('#dereference', function() {
it('returns the value corresponding to the path', function () {
var path = new ObjectPath().add('foo').add(1).add('bar');
var obj = {foo: ['', {bar: 42}]};
expect(path.dereference(obj)).toEqual(42);
});
});
}); });

View File

@@ -730,26 +730,26 @@ describe("matchersUtil", function() {
var diffBuilder = new jasmineUnderTest.DiffBuilder(), var diffBuilder = new jasmineUnderTest.DiffBuilder(),
matchersUtil = new jasmineUnderTest.MatchersUtil(); matchersUtil = new jasmineUnderTest.MatchersUtil();
spyOn(diffBuilder, 'record'); spyOn(diffBuilder, 'recordMismatch');
spyOn(diffBuilder, 'withPath').and.callThrough(); spyOn(diffBuilder, 'withPath').and.callThrough();
matchersUtil.equals([1], [2], [], diffBuilder); matchersUtil.equals([1], [2], [], diffBuilder);
expect(diffBuilder.withPath).toHaveBeenCalledWith('length', jasmine.any(Function)); expect(diffBuilder.withPath).toHaveBeenCalledWith('length', jasmine.any(Function));
expect(diffBuilder.withPath).toHaveBeenCalledWith(0, jasmine.any(Function)); expect(diffBuilder.withPath).toHaveBeenCalledWith(0, jasmine.any(Function));
expect(diffBuilder.record).toHaveBeenCalledWith(1, 2); expect(diffBuilder.recordMismatch).toHaveBeenCalledWith();
}); });
it('uses a diffBuilder if one is provided as the third argument', function() { it('uses a diffBuilder if one is provided as the third argument', function() {
var diffBuilder = new jasmineUnderTest.DiffBuilder(), var diffBuilder = new jasmineUnderTest.DiffBuilder(),
matchersUtil = new jasmineUnderTest.MatchersUtil(); matchersUtil = new jasmineUnderTest.MatchersUtil();
spyOn(diffBuilder, 'record'); spyOn(diffBuilder, 'recordMismatch');
spyOn(diffBuilder, 'withPath').and.callThrough(); spyOn(diffBuilder, 'withPath').and.callThrough();
matchersUtil.equals([1], [2], diffBuilder); matchersUtil.equals([1], [2], diffBuilder);
expect(diffBuilder.withPath).toHaveBeenCalledWith('length', jasmine.any(Function)); expect(diffBuilder.withPath).toHaveBeenCalledWith('length', jasmine.any(Function));
expect(diffBuilder.withPath).toHaveBeenCalledWith(0, jasmine.any(Function)); expect(diffBuilder.withPath).toHaveBeenCalledWith(0, jasmine.any(Function));
expect(diffBuilder.record).toHaveBeenCalledWith(1, 2); expect(diffBuilder.recordMismatch).toHaveBeenCalled();
}); });
}); });

View File

@@ -97,8 +97,12 @@ describe("toEqual", function() {
expect(compareEquals(actual, expected).message).toEqual(message); expect(compareEquals(actual, expected).message).toEqual(message);
}); });
it("uses custom object formatters to pretty-print properties", function() { it("uses custom object formatters to pretty-print simple properties", function() {
function formatter(x) { return '|' + x + '|'; } function formatter(x) {
if (typeof x === 'number') {
return '|' + x + '|';
}
}
var actual = {x: {y: 1, z: 2, f: 4}}, var actual = {x: {y: 1, z: 2, f: 4}},
expected = {x: {y: 1, z: 2, g: 3}}, expected = {x: {y: 1, z: 2, g: 3}},
@@ -114,8 +118,12 @@ describe("toEqual", function() {
expect(matcher.compare(actual, expected).message).toEqual(message); expect(matcher.compare(actual, expected).message).toEqual(message);
}); });
it("uses custom object formatters to build diffs", function() { it("uses custom object formatters to show simple values in diffs", function() {
function formatter(x) { return '|' + x + '|'; } function formatter(x) {
if (typeof x === 'number') {
return '|' + x + '|';
}
}
var actual = [{foo: 4}], var actual = [{foo: 4}],
expected = [{foo: 5}], expected = [{foo: 5}],
@@ -127,6 +135,30 @@ describe("toEqual", function() {
expect(matcher.compare(actual, expected).message).toEqual(message); expect(matcher.compare(actual, expected).message).toEqual(message);
}); });
it("uses custom object formatters to show more complex objects diffs", function() {
function formatter(x) {
if (x.hasOwnProperty('a')) {
return '[thing with a=' + x.a + ', b=' + x.b + ']';
}
}
var actual = [{
foo: {a: 1, b: 2},
bar: 'should not be pretty printed'
}],
expected = [{
foo: {a: 5, b: 2},
bar: "shouldn't be pretty printed"
}],
prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]),
util = new jasmineUnderTest.MatchersUtil({pp: prettyPrinter}),
matcher = jasmineUnderTest.matchers.toEqual(util),
message = "Expected $[0].foo = [thing with a=1, b=2] to equal [thing with a=5, b=2].\n" +
"Expected $[0].bar = 'should not be pretty printed' to equal 'shouldn't be pretty printed'.";
expect(matcher.compare(actual, expected).message).toEqual(message);
});
it("reports extra and missing properties of the root-level object", function() { it("reports extra and missing properties of the root-level object", function() {
var actual = {x: 1}, var actual = {x: 1},
expected = {a: 1}, expected = {a: 1},
@@ -303,10 +335,14 @@ describe("toEqual", function() {
expect(compareEquals(actual, expected).message).toEqual(message); expect(compareEquals(actual, expected).message).toEqual(message);
}); });
it("uses custom object formatters to report objects with different constructors", function () { it("uses custom object formatters for the value but not the type when reporting objects with different constructors", function () {
function Foo() {} function Foo() {}
function Bar() {} function Bar() {}
function formatter(x) { return '|' + x + '|'; } function formatter(x) {
if (x instanceof Foo || x instanceof Bar) {
return '|' + x + '|';
}
}
var actual = {x: new Foo()}, var actual = {x: new Foo()},
expected = {x: new Bar()}, expected = {x: new Bar()},
@@ -329,6 +365,11 @@ describe("toEqual", function() {
expect(compareEquals(actual, expected).message).toEqual(message); expect(compareEquals(actual, expected).message).toEqual(message);
}); });
it("reports value mismatches at the root level", function() {
expect(compareEquals(1, 2).message).toEqual("Expected 1 to equal 2.");
});
it("reports mismatches between objects with their own constructor property", function () { it("reports mismatches between objects with their own constructor property", function () {
function Foo() {} function Foo() {}
function Bar() {} function Bar() {}
@@ -839,7 +880,11 @@ describe("toEqual", function() {
}); });
it("uses custom object formatters when the actual array is longer", function() { it("uses custom object formatters when the actual array is longer", function() {
function formatter(x) { return '|' + x + '|'; } function formatter(x) {
if (typeof x === 'number') {
return '|' + x + '|';
}
}
var actual = [1, 1, 2, 3, 5], var actual = [1, 1, 2, 3, 5],
expected = [1, 1, 2, 3], expected = [1, 1, 2, 3],

View File

@@ -102,15 +102,7 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
}; };
SinglePrettyPrintRun.prototype.applyCustomFormatters_ = function(value) { SinglePrettyPrintRun.prototype.applyCustomFormatters_ = function(value) {
var i, result; return customFormat(value, this.customObjectFormatters_);
for (i = 0; i < this.customObjectFormatters_.length; i++) {
result = this.customObjectFormatters_[i](value);
if (result !== undefined) {
return result;
}
}
}; };
SinglePrettyPrintRun.prototype.iterateObject = function(obj, fn) { SinglePrettyPrintRun.prototype.iterateObject = function(obj, fn) {
@@ -383,16 +375,31 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
return extraKeys; return extraKeys;
} }
function customFormat(value, customObjectFormatters) {
var i, result;
for (i = 0; i < customObjectFormatters.length; i++) {
result = customObjectFormatters[i](value);
if (result !== undefined) {
return result;
}
}
}
return function(customObjectFormatters) { return function(customObjectFormatters) {
customObjectFormatters = customObjectFormatters || [];
var pp = function(value) { var pp = function(value) {
var prettyPrinter = new SinglePrettyPrintRun( var prettyPrinter = new SinglePrettyPrintRun(customObjectFormatters, pp);
customObjectFormatters || [],
pp
);
prettyPrinter.format(value); prettyPrinter.format(value);
return prettyPrinter.stringParts.join(''); return prettyPrinter.stringParts.join('');
}; };
pp.customFormat_ = function(value) {
return customFormat(value, customObjectFormatters);
};
return pp; return pp;
}; };
}; };

View File

@@ -1,17 +1,51 @@
getJasmineRequireObj().DiffBuilder = function(j$) { getJasmineRequireObj().DiffBuilder = function (j$) {
return function DiffBuilder(config) { return function DiffBuilder(config) {
var path = new j$.ObjectPath(), var prettyPrinter = (config || {}).prettyPrinter || j$.makePrettyPrinter(),
mismatches = [], mismatches = new j$.MismatchTree(),
prettyPrinter = (config || {}).prettyPrinter || j$.makePrettyPrinter(); path = new j$.ObjectPath(),
actualRoot = undefined,
expectedRoot = undefined;
return { return {
record: function (actual, expected, formatter) { setRoots: function (actual, expected) {
formatter = formatter || defaultFormatter; actualRoot = actual;
mismatches.push(formatter(actual, expected, path, prettyPrinter)); expectedRoot = expected;
},
recordMismatch: function (formatter) {
mismatches.add(path, formatter);
}, },
getMessage: function () { getMessage: function () {
return mismatches.join('\n'); var messages = [];
mismatches.traverse(function (path, isLeaf, formatter) {
var actualCustom, expectedCustom, useCustom,
actual = path.dereference(actualRoot),
expected = path.dereference(expectedRoot);
if (formatter) {
messages.push(formatter(actual, expected, path, prettyPrinter));
return true;
}
actualCustom = prettyPrinter.customFormat_(actual);
expectedCustom = prettyPrinter.customFormat_(expected);
useCustom = !(j$.util.isUndefined(actualCustom) && j$.util.isUndefined(expectedCustom));
if (useCustom) {
messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path));
return false; // don't recurse further
}
if (isLeaf) {
messages.push(defaultFormatter(actual, expected, path, prettyPrinter));
}
return true;
});
return messages.join('\n');
}, },
withPath: function (pathComponent, block) { withPath: function (pathComponent, block) {
@@ -22,12 +56,16 @@ getJasmineRequireObj().DiffBuilder = function(j$) {
} }
}; };
function defaultFormatter (actual, expected, path, prettyPrinter) { function defaultFormatter(actual, expected, path, prettyPrinter) {
return wrapPrettyPrinted(prettyPrinter(actual), prettyPrinter(expected), path);
}
function wrapPrettyPrinted(actual, expected, path) {
return 'Expected ' + return 'Expected ' +
path + (path.depth() ? ' = ' : '') + path + (path.depth() ? ' = ' : '') +
prettyPrinter(actual) + actual +
' to equal ' + ' to equal ' +
prettyPrinter(expected) + expected +
'.'; '.';
} }
}; };

View File

@@ -0,0 +1,62 @@
getJasmineRequireObj().MismatchTree = function (j$) {
/*
To be able to apply custom object formatters at all possible levels of an
object graph, DiffBuilder needs to be able to know not just where the
mismatch occurred but also all ancestors of the mismatched value in both
the expected and actual object graphs. MismatchTree maintains that context
and provides it via the traverse method.
*/
function MismatchTree(path) {
this.path = path || new j$.ObjectPath([]);
this.formatter = undefined;
this.children = [];
this.isMismatch = false;
}
MismatchTree.prototype.add = function (path, formatter) {
var key, child;
if (path.depth() === 0) {
this.formatter = formatter;
this.isMismatch = true;
} else {
key = path.components[0];
path = path.shift();
child = this.child(key);
if (!child) {
child = new MismatchTree(this.path.add(key));
this.children.push(child);
}
child.add(path, formatter);
}
};
MismatchTree.prototype.traverse = function (visit) {
var i, hasChildren = this.children.length > 0;
if (this.isMismatch || hasChildren) {
if (visit(this.path, !hasChildren, this.formatter)) {
for (i = 0; i < this.children.length; i++) {
this.children[i].traverse(visit);
}
}
}
};
MismatchTree.prototype.child = function(key) {
var i, pathEls;
for (i = 0; i < this.children.length; i++) {
pathEls = this.children[i].path.components;
if (pathEls[pathEls.length - 1] === key) {
return this.children[i];
}
}
};
return MismatchTree;
};

View File

@@ -4,7 +4,8 @@ getJasmineRequireObj().NullDiffBuilder = function(j$) {
withPath: function(_, block) { withPath: function(_, block) {
block(); block();
}, },
record: function() {} setRoots: function() {},
recordMismatch: function() {}
}; };
}; };
}; };

View File

@@ -11,10 +11,24 @@ getJasmineRequireObj().ObjectPath = function(j$) {
} }
}; };
ObjectPath.prototype.dereference = function(obj) {
var i;
for (i = 0; i < this.components.length; i++) {
obj = obj[this.components[i]];
}
return obj;
};
ObjectPath.prototype.add = function(component) { ObjectPath.prototype.add = function(component) {
return new ObjectPath(this.components.concat([component])); return new ObjectPath(this.components.concat([component]));
}; };
ObjectPath.prototype.shift = function() {
return new ObjectPath(this.components.slice(1));
};
ObjectPath.prototype.depth = function() { ObjectPath.prototype.depth = function() {
return this.components.length; return this.components.length;
}; };

View File

@@ -69,7 +69,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
if (asymmetricA) { if (asymmetricA) {
result = a.asymmetricMatch(b, shim); result = a.asymmetricMatch(b, shim);
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
} }
@@ -77,7 +77,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
if (asymmetricB) { if (asymmetricB) {
result = b.asymmetricMatch(a, shim); result = b.asymmetricMatch(a, shim);
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
} }
@@ -95,6 +95,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
customTesters = customTesters || this.customTesters_; customTesters = customTesters || this.customTesters_;
diffBuilder = diffBuilder || j$.NullDiffBuilder(); diffBuilder = diffBuilder || j$.NullDiffBuilder();
diffBuilder.setRoots(a, b);
return this.eq_(a, b, [], [], customTesters, diffBuilder); return this.eq_(a, b, [], [], customTesters, diffBuilder);
}; };
@@ -113,7 +114,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
var customTesterResult = customTesters[i](a, b); var customTesterResult = customTesters[i](a, b);
if (!j$.util.isUndefined(customTesterResult)) { if (!j$.util.isUndefined(customTesterResult)) {
if (!customTesterResult) { if (!customTesterResult) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return customTesterResult; return customTesterResult;
} }
@@ -122,7 +123,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
if (a instanceof Error && b instanceof Error) { if (a instanceof Error && b instanceof Error) {
result = a.message == b.message; result = a.message == b.message;
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
} }
@@ -132,7 +133,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
if (a === b) { if (a === b) {
result = a !== 0 || 1 / a == 1 / b; result = a !== 0 || 1 / a == 1 / b;
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
} }
@@ -140,13 +141,13 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
if (a === null || b === null) { if (a === null || b === null) {
result = a === b; result = a === b;
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
} }
var className = Object.prototype.toString.call(a); var className = Object.prototype.toString.call(a);
if (className != Object.prototype.toString.call(b)) { if (className != Object.prototype.toString.call(b)) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
switch (className) { switch (className) {
@@ -156,7 +157,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
// equivalent to `new String("5")`. // equivalent to `new String("5")`.
result = a == String(b); result = a == String(b);
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
case '[object Number]': case '[object Number]':
@@ -164,7 +165,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
// other numeric values. // other numeric values.
result = a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b); result = a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b);
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
case '[object Date]': case '[object Date]':
@@ -174,7 +175,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
// of `NaN` are not equivalent. // of `NaN` are not equivalent.
result = +a == +b; result = +a == +b;
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
// RegExps are compared by their source patterns and flags. // RegExps are compared by their source patterns and flags.
@@ -185,7 +186,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
a.ignoreCase == b.ignoreCase; a.ignoreCase == b.ignoreCase;
} }
if (typeof a != 'object' || typeof b != 'object') { if (typeof a != 'object' || typeof b != 'object') {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
@@ -195,12 +196,12 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
// At first try to use DOM3 method isEqualNode // At first try to use DOM3 method isEqualNode
result = a.isEqualNode(b); result = a.isEqualNode(b);
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
} }
return result; return result;
} }
if (aIsDomNode || bIsDomNode) { if (aIsDomNode || bIsDomNode) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
@@ -230,7 +231,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
diffBuilder.withPath('length', function() { diffBuilder.withPath('length', function() {
if (aLength !== bLength) { if (aLength !== bLength) {
diffBuilder.record(aLength, bLength); diffBuilder.recordMismatch();
result = false; result = false;
} }
}); });
@@ -238,7 +239,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
for (i = 0; i < aLength || i < bLength; i++) { for (i = 0; i < aLength || i < bLength; i++) {
diffBuilder.withPath(i, function() { diffBuilder.withPath(i, function() {
if (i >= bLength) { if (i >= bLength) {
diffBuilder.record(a[i], void 0, actualArrayIsLongerFormatter.bind(null, self.pp)); diffBuilder.recordMismatch(actualArrayIsLongerFormatter.bind(null, self.pp));
result = false; result = false;
} else { } else {
result = self.eq_(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack, customTesters, diffBuilder) && result; result = self.eq_(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack, customTesters, diffBuilder) && result;
@@ -250,7 +251,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
} }
} else if (j$.isMap(a) && j$.isMap(b)) { } else if (j$.isMap(a) && j$.isMap(b)) {
if (a.size != b.size) { if (a.size != b.size) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
@@ -292,12 +293,12 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
} }
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
} else if (j$.isSet(a) && j$.isSet(b)) { } else if (j$.isSet(a) && j$.isSet(b)) {
if (a.size != b.size) { if (a.size != b.size) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
@@ -342,7 +343,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
} }
if (!result) { if (!result) {
diffBuilder.record(a, b); diffBuilder.recordMismatch();
return false; return false;
} }
} else { } else {
@@ -355,7 +356,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
a instanceof aCtor && b instanceof bCtor && a instanceof aCtor && b instanceof bCtor &&
!(aCtor instanceof aCtor && bCtor instanceof bCtor)) { !(aCtor instanceof aCtor && bCtor instanceof bCtor)) {
diffBuilder.record(a, b, constructorsAreDifferentFormatter.bind(null, this.pp)); diffBuilder.recordMismatch(constructorsAreDifferentFormatter.bind(null, this.pp));
return false; return false;
} }
} }
@@ -366,7 +367,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
// Ensure that both objects contain the same number of properties before comparing deep equality. // Ensure that both objects contain the same number of properties before comparing deep equality.
if (keys(b, className == '[object Array]').length !== size) { if (keys(b, className == '[object Array]').length !== size) {
diffBuilder.record(a, b, objectKeysAreDifferentFormatter.bind(null, this.pp)); diffBuilder.recordMismatch(objectKeysAreDifferentFormatter.bind(null, this.pp));
return false; return false;
} }
@@ -374,7 +375,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
key = aKeys[i]; key = aKeys[i];
// Deep compare each member // Deep compare each member
if (!j$.util.has(b, key)) { if (!j$.util.has(b, key)) {
diffBuilder.record(a, b, objectKeysAreDifferentFormatter.bind(null, this.pp)); diffBuilder.recordMismatch(objectKeysAreDifferentFormatter.bind(null, this.pp));
result = false; result = false;
continue; continue;
} }
@@ -480,7 +481,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
} }
function isDiffBuilder(obj) { function isDiffBuilder(obj) {
return obj && typeof obj.record === 'function'; return obj && typeof obj.recordMismatch === 'function';
} }
return MatchersUtil; return MatchersUtil;

View File

@@ -84,6 +84,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
j$.DiffBuilder = jRequire.DiffBuilder(j$); j$.DiffBuilder = jRequire.DiffBuilder(j$);
j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$); j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$);
j$.ObjectPath = jRequire.ObjectPath(j$); j$.ObjectPath = jRequire.ObjectPath(j$);
j$.MismatchTree = jRequire.MismatchTree(j$);
j$.GlobalErrors = jRequire.GlobalErrors(j$); j$.GlobalErrors = jRequire.GlobalErrors(j$);
j$.Truthy = jRequire.Truthy(j$); j$.Truthy = jRequire.Truthy(j$);