¶matchersUtil.js |
|
getJasmineRequireObj().matchersUtil = function(j$) {
|
|
|
TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter? |
return {
equals: function(a, b, customTesters) {
customTesters = customTesters || [];
return eq(a, b, [], [], customTesters);
},
contains: function(haystack, needle, customTesters) {
customTesters = customTesters || [];
if (Object.prototype.toString.apply(haystack) === '[object Array]') {
for (var i = 0; i < haystack.length; i++) {
if (eq(haystack[i], needle, [], [], customTesters)) {
return true;
}
}
return false;
}
return !!haystack && haystack.indexOf(needle) >= 0;
},
buildFailureMessage: function() {
var args = Array.prototype.slice.call(arguments, 0),
matcherName = args[0],
isNot = args[1],
actual = args[2],
expected = args.slice(3),
englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
var message = 'Expected ' +
j$.pp(actual) +
(isNot ? ' not ' : ' ') +
englishyPredicate;
if (expected.length > 0) {
for (var i = 0; i < expected.length; i++) {
if (i > 0) {
message += ',';
}
message += ' ' + j$.pp(expected[i]);
}
}
return message + '.';
}
};
|
|
Equality function lovingly adapted from isEqual in Underscore |
function eq(a, b, aStack, bStack, customTesters) {
var result = true;
for (var i = 0; i < customTesters.length; i++) {
var customTesterResult = customTesters[i](a, b);
if (!j$.util.isUndefined(customTesterResult)) {
return customTesterResult;
}
}
if (a instanceof j$.Any) {
result = a.jasmineMatches(b);
if (result) {
return true;
}
}
if (b instanceof j$.Any) {
result = b.jasmineMatches(a);
if (result) {
return true;
}
}
if (b instanceof j$.ObjectContaining) {
result = b.jasmineMatches(a);
if (result) {
return true;
}
}
if (a instanceof Error && b instanceof Error) {
return a.message == b.message;
}
|
|
Identical objects are equal. |
if (a === b) { return a !== 0 || 1 / a == 1 / b; }
|
|
A strict comparison is necessary because |
if (a === null || b === null) { return a === b; }
var className = Object.prototype.toString.call(a);
if (className != Object.prototype.toString.call(b)) { return false; }
switch (className) {
|
|
Strings, numbers, dates, and booleans are compared by value. |
case '[object String]':
|
|
Primitives and their corresponding object wrappers are equivalent; thus, |
return a == String(b);
case '[object Number]':
|
|
|
return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
case '[object Boolean]':
|
|
Coerce dates and booleans to numeric primitive values. Dates are compared by their
millisecond representations. Note that invalid dates with millisecond representations
of |
return +a == +b;
|
|
RegExps are compared by their source patterns and flags. |
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') { return false; }
|
|
Assume equality for cyclic structures. The algorithm for detecting cyclic
structures is adapted from ES 5.1 section 15.12.3, abstract operation |
var length = aStack.length;
while (length--) {
|
|
Linear search. Performance is inversely proportional to the number of unique nested structures. |
if (aStack[length] == a) { return bStack[length] == b; }
}
|
|
Add the first object to the stack of traversed objects. |
aStack.push(a);
bStack.push(b);
var size = 0;
|
|
Recursively compare objects and arrays. |
if (className == '[object Array]') {
|
|
Compare array lengths to determine if a deep comparison is necessary. |
size = a.length;
result = size == b.length;
if (result) {
|
|
Deep compare the contents, ignoring non-numeric properties. |
while (size--) {
if (!(result = eq(a[size], b[size], aStack, bStack, customTesters))) { break; }
}
}
} else {
|
|
Objects with different constructors are not equivalent, but |
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) &&
isFunction(bCtor) && (bCtor instanceof bCtor))) {
return false;
}
|
|
Deep compare objects. |
for (var key in a) {
if (has(a, key)) {
|
|
Count the expected number of properties. |
size++;
|
|
Deep compare each member. |
if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters))) { break; }
}
}
|
|
Ensure that both objects contain the same number of properties. |
if (result) {
for (key in b) {
if (has(b, key) && !(size--)) { break; }
}
result = !size;
}
}
|
|
Remove the first object from the stack of traversed objects. |
aStack.pop();
bStack.pop();
return result;
function has(obj, key) {
return obj.hasOwnProperty(key);
}
function isFunction(obj) {
return typeof obj === 'function';
}
}
};
|