Array equality treats undefined elements as equal however they got in there

- Fixes #786
This commit is contained in:
Gregg Van Hove
2016-02-22 11:06:59 -08:00
parent 602f5bc08a
commit 5458f2f18d
3 changed files with 95 additions and 48 deletions

View File

@@ -2879,36 +2879,43 @@ getJasmineRequireObj().matchersUtil = function(j$) {
var size = 0; var size = 0;
// Recursively compare objects and arrays. // Recursively compare objects and arrays.
// Compare array lengths to determine if a deep comparison is necessary. // Compare array lengths to determine if a deep comparison is necessary.
if (className == '[object Array]' && a.length !== b.length) { if (className == '[object Array]') {
result = false; size = a.length;
} if (size !== b.length) {
return false;
}
if (result) { while (size--) {
// Objects with different constructors are not equivalent, but `Object`s result = eq(a[size], b[size], aStack, bStack, customTesters);
// or `Array`s from different frames are. if (!result) {
if (className !== '[object Array]') {
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(isFunction(aCtor) && aCtor instanceof aCtor &&
isFunction(bCtor) && bCtor instanceof bCtor)) {
return false; return false;
} }
} }
} else {
// Deep compare objects. // Objects with different constructors are not equivalent, but `Object`s
var aKeys = keys(a), key; // or `Array`s from different frames are.
size = aKeys.length; var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(isFunction(aCtor) && aCtor instanceof aCtor &&
isFunction(bCtor) && bCtor instanceof bCtor)) {
return false;
}
}
// Ensure that both objects contain the same number of properties before comparing deep equality. // Deep compare objects.
if (keys(b).length !== size) { return false; } var aKeys = keys(a, className == '[object Array]'), key;
size = aKeys.length;
while (size--) { // Ensure that both objects contain the same number of properties before comparing deep equality.
key = aKeys[size]; if (keys(b, className == '[object Array]').length !== size) { return false; }
// Deep compare each member
result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters);
if (!result) { while (size--) {
return false; key = aKeys[size];
} // Deep compare each member
result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters);
if (!result) {
return false;
} }
} }
// Remove the first object from the stack of traversed objects. // Remove the first object from the stack of traversed objects.
@@ -2917,8 +2924,8 @@ getJasmineRequireObj().matchersUtil = function(j$) {
return result; return result;
function keys(obj) { function keys(obj, isArray) {
return Object.keys ? Object.keys(obj) : var allKeys = Object.keys ? Object.keys(obj) :
(function(o) { (function(o) {
var keys = []; var keys = [];
for (var key in o) { for (var key in o) {
@@ -2928,6 +2935,19 @@ getJasmineRequireObj().matchersUtil = function(j$) {
} }
return keys; return keys;
})(obj); })(obj);
if (!isArray) {
return allKeys;
}
var extraKeys = [];
for (var i in allKeys) {
if (!allKeys[i].match(/^[0-9]+$/)) {
extraKeys.push(key);
}
}
return extraKeys;
} }
function has(obj, key) { function has(obj, key) {

View File

@@ -54,6 +54,13 @@ describe("matchersUtil", function() {
expect(jasmineUnderTest.matchersUtil.equals([1, 2], [1, 2])).toBe(true); expect(jasmineUnderTest.matchersUtil.equals([1, 2], [1, 2])).toBe(true);
}); });
it("passes for Arrays that are equivalent, with elements added by changing length", function() {
var foo = [];
foo.length = 1;
expect(jasmineUnderTest.matchersUtil.equals(foo, [undefined])).toBe(true);
});
it("fails for Arrays that are not equivalent", function() { it("fails for Arrays that are not equivalent", function() {
expect(jasmineUnderTest.matchersUtil.equals([1, 2], [1, 2, 3])).toBe(false); expect(jasmineUnderTest.matchersUtil.equals([1, 2], [1, 2, 3])).toBe(false);
}); });

View File

@@ -161,36 +161,43 @@ getJasmineRequireObj().matchersUtil = function(j$) {
var size = 0; var size = 0;
// Recursively compare objects and arrays. // Recursively compare objects and arrays.
// Compare array lengths to determine if a deep comparison is necessary. // Compare array lengths to determine if a deep comparison is necessary.
if (className == '[object Array]' && a.length !== b.length) { if (className == '[object Array]') {
result = false; size = a.length;
} if (size !== b.length) {
return false;
}
if (result) { while (size--) {
// Objects with different constructors are not equivalent, but `Object`s result = eq(a[size], b[size], aStack, bStack, customTesters);
// or `Array`s from different frames are. if (!result) {
if (className !== '[object Array]') {
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(isFunction(aCtor) && aCtor instanceof aCtor &&
isFunction(bCtor) && bCtor instanceof bCtor)) {
return false; return false;
} }
} }
} else {
// Deep compare objects. // Objects with different constructors are not equivalent, but `Object`s
var aKeys = keys(a), key; // or `Array`s from different frames are.
size = aKeys.length; var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(isFunction(aCtor) && aCtor instanceof aCtor &&
isFunction(bCtor) && bCtor instanceof bCtor)) {
return false;
}
}
// Ensure that both objects contain the same number of properties before comparing deep equality. // Deep compare objects.
if (keys(b).length !== size) { return false; } var aKeys = keys(a, className == '[object Array]'), key;
size = aKeys.length;
while (size--) { // Ensure that both objects contain the same number of properties before comparing deep equality.
key = aKeys[size]; if (keys(b, className == '[object Array]').length !== size) { return false; }
// Deep compare each member
result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters);
if (!result) { while (size--) {
return false; key = aKeys[size];
} // Deep compare each member
result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters);
if (!result) {
return false;
} }
} }
// Remove the first object from the stack of traversed objects. // Remove the first object from the stack of traversed objects.
@@ -199,8 +206,8 @@ getJasmineRequireObj().matchersUtil = function(j$) {
return result; return result;
function keys(obj) { function keys(obj, isArray) {
return Object.keys ? Object.keys(obj) : var allKeys = Object.keys ? Object.keys(obj) :
(function(o) { (function(o) {
var keys = []; var keys = [];
for (var key in o) { for (var key in o) {
@@ -210,6 +217,19 @@ getJasmineRequireObj().matchersUtil = function(j$) {
} }
return keys; return keys;
})(obj); })(obj);
if (!isArray) {
return allKeys;
}
var extraKeys = [];
for (var i in allKeys) {
if (!allKeys[i].match(/^[0-9]+$/)) {
extraKeys.push(key);
}
}
return extraKeys;
} }
function has(obj, key) { function has(obj, key) {