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

View File

@@ -54,6 +54,13 @@ describe("matchersUtil", function() {
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() {
expect(jasmineUnderTest.matchersUtil.equals([1, 2], [1, 2, 3])).toBe(false);
});

View File

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