1500 lines
38 KiB
JavaScript
1500 lines
38 KiB
JavaScript
/*
|
|
* JsHamcrest v0.7.0
|
|
* http://danielfm.github.com/jshamcrest/
|
|
*
|
|
* Library of matcher objects for JavaScript.
|
|
*
|
|
* Copyright (c) 2009-2013 Daniel Fernandes Martins
|
|
* Licensed under the BSD license.
|
|
*
|
|
* Revision: 13d2f6ac0272b6b679eca295514fe69c0a84251a
|
|
* Date: Sat Jan 26 12:47:27 2013 -0200
|
|
*/
|
|
|
|
var JsHamcrest = {
|
|
/**
|
|
* Library version.
|
|
*/
|
|
version: '0.7.0',
|
|
|
|
/**
|
|
* Delegate function, useful when used to create a matcher that has a value-equalTo semantic
|
|
*/
|
|
EqualTo: function (func) {
|
|
return function (matcherOrValue) {
|
|
if (!JsHamcrest.isMatcher(matcherOrValue)) {
|
|
return func(JsHamcrest.Matchers.equalTo(matcherOrValue));
|
|
}
|
|
return func(matcherOrValue);
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Returns whether the given object is a matcher.
|
|
*/
|
|
isMatcher: function(obj) {
|
|
return obj instanceof JsHamcrest.SimpleMatcher;
|
|
},
|
|
|
|
/**
|
|
* Returns whether the given arrays are equivalent.
|
|
*/
|
|
areArraysEqual: function(array, anotherArray) {
|
|
if (array instanceof Array || anotherArray instanceof Array) {
|
|
if (array.length != anotherArray.length) {
|
|
return false;
|
|
}
|
|
|
|
for (var i = 0; i < array.length; i++) {
|
|
var a = array[i];
|
|
var b = anotherArray[i];
|
|
|
|
if (a instanceof Array || b instanceof Array) {
|
|
if(!JsHamcrest.areArraysEqual(a, b)) {
|
|
return false;
|
|
}
|
|
} else if (a != b) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
} else {
|
|
return array == anotherArray;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Returns whether the given Arrays are equivalent. This will return true if the objects
|
|
* inside the Arrays are equivalent i.e. they dont have to be the same object reference.
|
|
* Two objects with the same key value pairs will be equivalent eventhough they are not
|
|
* the same object.
|
|
*
|
|
* @param {type} expected A map of expected values.
|
|
* @param {type} actual A map of the actual values.
|
|
* @returns {Boolean} A Boolean signifing if the two Arrays are equivalent, true if they are.
|
|
*/
|
|
areArraysEquivalent: function(expected, actual)
|
|
{
|
|
if (expected.length !== actual.length)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (var i = 0; i < expected.length; i++)
|
|
{
|
|
var a = expected[i];
|
|
var b = actual[i];
|
|
|
|
if(JsHamcrest.areTwoEntitiesEquivalent(a, b) === false)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Returns whether the given maps are equivalent. This will return true if the objects
|
|
* inside the maps are equivalent i.e. they dont have to be the same object reference.
|
|
* Two objects with the same key value pairs will be equivalent eventhough they are not
|
|
* the same object.
|
|
*
|
|
* @param {type} expected A map of expected values.
|
|
* @param {type} actual A map of the actual values.
|
|
* @returns {Boolean} A Boolean signifing if the two maps are equivalent, true if they are.
|
|
*/
|
|
areMapsEquivalent: function(expected, actual)
|
|
{
|
|
// we need to do this both ways in case both maps have undefined values (which makes counting the number
|
|
// of keys a non-acceptable comparison).
|
|
if(JsHamcrest.simpleMapCompare(expected, actual) && JsHamcrest.simpleMapCompare(actual, expected))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
simpleMapCompare: function(firstMap, secondMap)
|
|
{
|
|
for(var item in firstMap)
|
|
{
|
|
if(firstMap.hasOwnProperty(item))
|
|
{
|
|
if(!JsHamcrest.areTwoEntitiesEquivalent(firstMap[item], secondMap[item])) return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
areTwoEntitiesEquivalent: function(expected, actual)
|
|
{
|
|
var expectedsMatcher = JsHamcrest.retreiveEntityMatcherFunction(expected);
|
|
var actualsMatcher = JsHamcrest.retreiveEntityMatcherFunction(actual);
|
|
|
|
if(expectedsMatcher === actualsMatcher && expectedsMatcher(expected, actual))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Returns the function that would be used to compare the entity with an entity of the same type.
|
|
*
|
|
* @param {type} entity A JavaScript entity, this method will try and figure out what type.
|
|
*/
|
|
retreiveEntityMatcherFunction: function(entity) {
|
|
if ( (Array.isArray && Array.isArray(entity)) || entity instanceof Array ) return JsHamcrest.areArraysEquivalent;
|
|
|
|
if(entity instanceof Boolean) return JsHamcrest.areBooleansEqual;
|
|
|
|
if(entity instanceof Date) return JsHamcrest.areDatesEqual;
|
|
|
|
if(entity instanceof Function) return JsHamcrest.areFunctionsEqual;
|
|
|
|
if(entity instanceof Number || typeof entity === "number") return JsHamcrest.areNumbersEqual;
|
|
|
|
if(entity instanceof String || typeof entity === "string") return JsHamcrest.areStringsEqual;
|
|
|
|
if(entity instanceof RegExp) return JsHamcrest.areRegExpEqual;
|
|
|
|
if(entity instanceof Error) return JsHamcrest.areErrorsEqual;
|
|
|
|
if(typeof entity === "undefined") return JsHamcrest.areEntitiesUndefined;
|
|
|
|
if(entity === null) return JsHamcrest.areEntitiesNull;
|
|
|
|
if(entity.constructor === Object) return JsHamcrest.areMapsEquivalent;
|
|
|
|
return JsHamcrest.areEntitiesStrictlyEquals;
|
|
},
|
|
|
|
/**
|
|
* Simple comparator functions.
|
|
*
|
|
* @param {type} expected The Object that is expected to be present.
|
|
* @param {type} actual The Object that is actually present.
|
|
*/
|
|
areBooleansEqual: function(expected, actual) { return expected.toString() === actual.toString(); },
|
|
|
|
areDatesEqual: function(expected, actual) { return expected.toString() === actual.toString(); },
|
|
|
|
areFunctionsEqual: function(expected, actual) { return expected === actual; },
|
|
|
|
areNumbersEqual: function(expected, actual) { return expected.valueOf() === actual.valueOf(); },
|
|
|
|
areStringsEqual: function(expected, actual) { return expected.valueOf() === actual.valueOf(); },
|
|
|
|
areRegExpEqual: function(expected, actual) { return expected.toString() === actual.toString(); },
|
|
|
|
areErrorsEqual: function(expected, actual) { return expected.constructor === actual.constructor && expected.message === actual.message; },
|
|
|
|
areEntitiesUndefined: function(expected, actual) { return expected === actual; },
|
|
|
|
areEntitiesNull: function(expected, actual) { return expected === actual; },
|
|
|
|
areEntitiesStrictlyEquals: function(expected, actual) { return expected === actual; },
|
|
|
|
/**
|
|
* Builds a matcher object that uses external functions provided by the
|
|
* caller in order to define the current matching logic.
|
|
*/
|
|
SimpleMatcher: function(params) {
|
|
params = params || {};
|
|
|
|
this.matches = params.matches;
|
|
this.describeTo = params.describeTo;
|
|
|
|
// Replace the function to describe the actual value
|
|
if (params.describeValueTo) {
|
|
this.describeValueTo = params.describeValueTo;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Matcher that provides an easy way to wrap several matchers into one.
|
|
*/
|
|
CombinableMatcher: function(params) {
|
|
// Call superclass' constructor
|
|
JsHamcrest.SimpleMatcher.apply(this, arguments);
|
|
|
|
params = params || {};
|
|
|
|
this.and = function(anotherMatcher) {
|
|
var all = JsHamcrest.Matchers.allOf(this, anotherMatcher);
|
|
return new JsHamcrest.CombinableMatcher({
|
|
matches: all.matches,
|
|
|
|
describeTo: function(description) {
|
|
description.appendDescriptionOf(all);
|
|
}
|
|
});
|
|
};
|
|
|
|
this.or = function(anotherMatcher) {
|
|
var any = JsHamcrest.Matchers.anyOf(this, anotherMatcher);
|
|
return new JsHamcrest.CombinableMatcher({
|
|
matches: any.matches,
|
|
|
|
describeTo: function(description) {
|
|
description.appendDescriptionOf(any);
|
|
}
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Class that builds assertion error messages.
|
|
*/
|
|
Description: function() {
|
|
var value = '';
|
|
|
|
this.get = function() {
|
|
return value;
|
|
};
|
|
|
|
this.appendDescriptionOf = function(selfDescribingObject) {
|
|
if (selfDescribingObject) {
|
|
selfDescribingObject.describeTo(this);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
this.append = function(text) {
|
|
if (text != null) {
|
|
value += text;
|
|
}
|
|
return this;
|
|
};
|
|
|
|
this.appendLiteral = function(literal) {
|
|
var undefined;
|
|
if (literal === undefined) {
|
|
this.append('undefined');
|
|
} else if (literal === null) {
|
|
this.append('null');
|
|
} else if (literal instanceof Array) {
|
|
this.appendValueList('[', ', ', ']', literal);
|
|
} else if (typeof literal == 'string') {
|
|
this.append('"' + literal + '"');
|
|
} else if (literal instanceof Function) {
|
|
this.append('Function' + (literal.name ? ' ' + literal.name : ''));
|
|
} else {
|
|
this.append(literal);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
this.appendValueList = function(start, separator, end, list) {
|
|
this.append(start);
|
|
for (var i = 0; i < list.length; i++) {
|
|
if (i > 0) {
|
|
this.append(separator);
|
|
}
|
|
this.appendLiteral(list[i]);
|
|
}
|
|
this.append(end);
|
|
return this;
|
|
};
|
|
|
|
this.appendList = function(start, separator, end, list) {
|
|
this.append(start);
|
|
for (var i = 0; i < list.length; i++) {
|
|
if (i > 0) {
|
|
this.append(separator);
|
|
}
|
|
this.appendDescriptionOf(list[i]);
|
|
}
|
|
this.append(end);
|
|
return this;
|
|
};
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Describes the actual value to the given description. This method is optional
|
|
* and, if it's not present, the actual value will be described as a JavaScript
|
|
* literal.
|
|
*/
|
|
JsHamcrest.SimpleMatcher.prototype.describeValueTo = function(actual, description) {
|
|
description.appendLiteral(actual);
|
|
};
|
|
|
|
|
|
// CombinableMatcher is a specialization of SimpleMatcher
|
|
JsHamcrest.CombinableMatcher.prototype = new JsHamcrest.SimpleMatcher();
|
|
JsHamcrest.CombinableMatcher.prototype.constructor = JsHamcrest.CombinableMatcher;
|
|
JsHamcrest.Matchers = {};
|
|
|
|
/**
|
|
* The actual value must be any value considered truth by the JavaScript
|
|
* engine.
|
|
*/
|
|
JsHamcrest.Matchers.truth = function() {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('truth');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Delegate-only matcher frequently used to improve readability.
|
|
*/
|
|
JsHamcrest.Matchers.is = JsHamcrest.EqualTo(function(matcher) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return matcher.matches(actual);
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('is ').appendDescriptionOf(matcher);
|
|
}
|
|
});
|
|
});
|
|
|
|
/**
|
|
* The actual value must not match the given matcher or value.
|
|
*/
|
|
JsHamcrest.Matchers.not = JsHamcrest.EqualTo(function(matcher) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return !matcher.matches(actual);
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('not ').appendDescriptionOf(matcher);
|
|
}
|
|
});
|
|
});
|
|
|
|
/**
|
|
* The actual value must be equal to the given value.
|
|
*/
|
|
JsHamcrest.Matchers.equalTo = function(expected) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
if (expected instanceof Array || actual instanceof Array) {
|
|
return JsHamcrest.areArraysEqual(expected, actual);
|
|
}
|
|
return actual == expected;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('equal to ').appendLiteral(expected);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Useless always-match matcher.
|
|
*/
|
|
JsHamcrest.Matchers.anything = function() {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return true;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('anything');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual value must be null (or undefined).
|
|
*/
|
|
JsHamcrest.Matchers.nil = function() {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual == null;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.appendLiteral(null);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual value must be the same as the given value.
|
|
*/
|
|
JsHamcrest.Matchers.sameAs = function(expected) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual === expected;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('same as ').appendLiteral(expected);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual value is a function and, when invoked, it should thrown an
|
|
* exception with the given name.
|
|
*/
|
|
JsHamcrest.Matchers.raises = function(exceptionName) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actualFunction) {
|
|
try {
|
|
actualFunction();
|
|
} catch (e) {
|
|
if (e.name == exceptionName) {
|
|
return true;
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('raises ').append(exceptionName);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual value is a function and, when invoked, it should raise any
|
|
* exception.
|
|
*/
|
|
JsHamcrest.Matchers.raisesAnything = function() {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actualFunction) {
|
|
try {
|
|
actualFunction();
|
|
} catch (e) {
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('raises anything');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Combinable matcher where the actual value must match both of the given
|
|
* matchers.
|
|
*/
|
|
JsHamcrest.Matchers.both = JsHamcrest.EqualTo(function(matcher) {
|
|
return new JsHamcrest.CombinableMatcher({
|
|
matches: matcher.matches,
|
|
describeTo: function(description) {
|
|
description.append('both ').appendDescriptionOf(matcher);
|
|
}
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Combinable matcher where the actual value must match at least one of the
|
|
* given matchers.
|
|
*/
|
|
JsHamcrest.Matchers.either = JsHamcrest.EqualTo(function(matcher) {
|
|
return new JsHamcrest.CombinableMatcher({
|
|
matches: matcher.matches,
|
|
describeTo: function(description) {
|
|
description.append('either ').appendDescriptionOf(matcher);
|
|
}
|
|
});
|
|
});
|
|
|
|
/**
|
|
* All the given values or matchers should match the actual value to be
|
|
* sucessful. This matcher behaves pretty much like the && operator.
|
|
*/
|
|
JsHamcrest.Matchers.allOf = function() {
|
|
var args = arguments;
|
|
if (args[0] instanceof Array) {
|
|
args = args[0];
|
|
}
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
for (var i = 0; i < args.length; i++) {
|
|
var matcher = args[i];
|
|
if (!JsHamcrest.isMatcher(matcher)) {
|
|
matcher = JsHamcrest.Matchers.equalTo(matcher);
|
|
}
|
|
if (!matcher.matches(actual)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.appendList('(', ' and ', ')', args);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* At least one of the given matchers should match the actual value. This
|
|
* matcher behaves pretty much like the || (or) operator.
|
|
*/
|
|
JsHamcrest.Matchers.anyOf = function() {
|
|
var args = arguments;
|
|
if (args[0] instanceof Array) {
|
|
args = args[0];
|
|
}
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
for (var i = 0; i < args.length; i++) {
|
|
var matcher = args[i];
|
|
if (!JsHamcrest.isMatcher(matcher)) {
|
|
matcher = JsHamcrest.Matchers.equalTo(matcher);
|
|
}
|
|
if (matcher.matches(actual)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.appendList('(', ' or ', ')', args);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual number must be greater than the expected number.
|
|
*/
|
|
JsHamcrest.Matchers.greaterThan = function(expected) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual > expected;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('greater than ').appendLiteral(expected);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual number must be greater than or equal to the expected number
|
|
*/
|
|
JsHamcrest.Matchers.greaterThanOrEqualTo = function(expected) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual >= expected;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('greater than or equal to ').appendLiteral(expected);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual number must be less than the expected number.
|
|
*/
|
|
JsHamcrest.Matchers.lessThan = function(expected) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual < expected;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('less than ').appendLiteral(expected);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual number must be less than or equal to the expected number.
|
|
*/
|
|
JsHamcrest.Matchers.lessThanOrEqualTo = function(expected) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual <= expected;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('less than or equal to ').append(expected);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual value must not be a number.
|
|
*/
|
|
JsHamcrest.Matchers.notANumber = function() {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return isNaN(actual);
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('not a number');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual value must be divisible by the given number.
|
|
*/
|
|
JsHamcrest.Matchers.divisibleBy = function(divisor) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual % divisor === 0;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('divisible by ').appendLiteral(divisor);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual value must be even.
|
|
*/
|
|
JsHamcrest.Matchers.even = function() {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual % 2 === 0;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('even');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual number must be odd.
|
|
*/
|
|
JsHamcrest.Matchers.odd = function() {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual % 2 !== 0;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('odd');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual number must be between the given range (inclusive).
|
|
*/
|
|
JsHamcrest.Matchers.between = function(start) {
|
|
return {
|
|
and: function(end) {
|
|
var greater = end;
|
|
var lesser = start;
|
|
|
|
if (start > end) {
|
|
greater = start;
|
|
lesser = end;
|
|
}
|
|
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual >= lesser && actual <= greater;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('between ').appendLiteral(lesser)
|
|
.append(' and ').appendLiteral(greater);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
};
|
|
|
|
/**
|
|
* The actual number must be close enough to *expected*, that is, the actual
|
|
* number is equal to a value within some range of acceptable error.
|
|
*/
|
|
JsHamcrest.Matchers.closeTo = function(expected, delta) {
|
|
if (!delta) {
|
|
delta = 0;
|
|
}
|
|
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return (Math.abs(actual - expected) - delta) <= 0;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('number within ')
|
|
.appendLiteral(delta).append(' of ').appendLiteral(expected);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual number must be zero.
|
|
*/
|
|
JsHamcrest.Matchers.zero = function() {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual === 0;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('zero');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual string must be equal to the given string, ignoring case.
|
|
*/
|
|
JsHamcrest.Matchers.equalIgnoringCase = function(str) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual.toUpperCase() == str.toUpperCase();
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('equal ignoring case "').append(str).append('"');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual string must have a substring equals to the given string.
|
|
*/
|
|
JsHamcrest.Matchers.containsString = function(str) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual.indexOf(str) >= 0;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('contains string "').append(str).append('"');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual string must start with the given string.
|
|
*/
|
|
JsHamcrest.Matchers.startsWith = function(str) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual.indexOf(str) === 0;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('starts with ').appendLiteral(str);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual string must end with the given string.
|
|
*/
|
|
JsHamcrest.Matchers.endsWith = function(str) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual.lastIndexOf(str) + str.length == actual.length;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('ends with ').appendLiteral(str);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual string must match the given regular expression.
|
|
*/
|
|
JsHamcrest.Matchers.matches = function(regex) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return regex.test(actual);
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('matches ').appendLiteral(regex);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual string must look like an e-mail address.
|
|
*/
|
|
JsHamcrest.Matchers.emailAddress = function() {
|
|
var regex = /^([a-z0-9_\.\-\+])+\@(([a-z0-9\-])+\.)+([a-z0-9]{2,4})+$/i;
|
|
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return regex.test(actual);
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('email address');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual value has a member with the given name.
|
|
*/
|
|
JsHamcrest.Matchers.hasMember = function(memberName, matcherOrValue) {
|
|
var undefined;
|
|
if (matcherOrValue === undefined) {
|
|
matcherOrValue = JsHamcrest.Matchers.anything();
|
|
} else if (!JsHamcrest.isMatcher(matcherOrValue)) {
|
|
matcherOrValue = JsHamcrest.Matchers.equalTo(matcherOrValue);
|
|
}
|
|
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
if (actual && memberName in actual) {
|
|
return matcherOrValue.matches(actual[memberName]);
|
|
}
|
|
return false;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('has member ').appendLiteral(memberName)
|
|
.append(' (').appendDescriptionOf(matcherOrValue).append(')');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual value has a function with the given name.
|
|
*/
|
|
JsHamcrest.Matchers.hasFunction = function(functionName) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
if (actual) {
|
|
return functionName in actual &&
|
|
actual[functionName] instanceof Function;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('has function ').appendLiteral(functionName);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual value must be an instance of the given class.
|
|
*/
|
|
JsHamcrest.Matchers.instanceOf = function(clazz) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return !!(actual instanceof clazz);
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
var className = clazz.name ? clazz.name : 'a class';
|
|
description.append('instance of ').append(className);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual value must be an instance of the given type.
|
|
*/
|
|
JsHamcrest.Matchers.typeOf = function(typeName) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return (typeof actual == typeName);
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('typeof ').append('"').append(typeName).append('"');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The actual value must be an object.
|
|
*/
|
|
JsHamcrest.Matchers.object = function() {
|
|
return new JsHamcrest.Matchers.instanceOf(Object);
|
|
};
|
|
|
|
/**
|
|
* The actual value must be a string.
|
|
*/
|
|
JsHamcrest.Matchers.string = function() {
|
|
return new JsHamcrest.Matchers.typeOf('string');
|
|
};
|
|
|
|
/**
|
|
* The actual value must be a number.
|
|
*/
|
|
JsHamcrest.Matchers.number = function() {
|
|
return new JsHamcrest.Matchers.typeOf('number');
|
|
};
|
|
|
|
/**
|
|
* The actual value must be a boolean.
|
|
*/
|
|
JsHamcrest.Matchers.bool = function() {
|
|
return new JsHamcrest.Matchers.typeOf('boolean');
|
|
};
|
|
|
|
/**
|
|
* The actual value must be a function.
|
|
*/
|
|
JsHamcrest.Matchers.func = function() {
|
|
return new JsHamcrest.Matchers.instanceOf(Function);
|
|
};
|
|
|
|
/**
|
|
* The actual value should be an array and it must contain at least one value
|
|
* that matches the given value or matcher.
|
|
*/
|
|
JsHamcrest.Matchers.hasItem = JsHamcrest.EqualTo(function(matcher) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
// Should be an array
|
|
if (!(actual instanceof Array)) {
|
|
return false;
|
|
}
|
|
|
|
for (var i = 0; i < actual.length; i++) {
|
|
if (matcher.matches(actual[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('array contains item ')
|
|
.appendDescriptionOf(matcher);
|
|
}
|
|
});
|
|
});
|
|
|
|
/**
|
|
* The actual value should be an array and the given values or matchers must
|
|
* match at least one item.
|
|
*/
|
|
JsHamcrest.Matchers.hasItems = function() {
|
|
var items = [];
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
items.push(JsHamcrest.Matchers.hasItem(arguments[i]));
|
|
}
|
|
return JsHamcrest.Matchers.allOf(items);
|
|
};
|
|
|
|
/**
|
|
* The actual value should be an array and the given value or matcher must
|
|
* match all items.
|
|
*/
|
|
JsHamcrest.Matchers.everyItem = JsHamcrest.EqualTo(function(matcher) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
// Should be an array
|
|
if (!(actual instanceof Array)) {
|
|
return false;
|
|
}
|
|
|
|
for (var i = 0; i < actual.length; i++) {
|
|
if (!matcher.matches(actual[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('every item ')
|
|
.appendDescriptionOf(matcher);
|
|
}
|
|
});
|
|
});
|
|
|
|
/**
|
|
* The given array must contain the actual value.
|
|
*/
|
|
JsHamcrest.Matchers.isIn = function() {
|
|
var equalTo = JsHamcrest.Matchers.equalTo;
|
|
|
|
var args = arguments;
|
|
if (args[0] instanceof Array) {
|
|
args = args[0];
|
|
}
|
|
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
for (var i = 0; i < args.length; i++) {
|
|
if (equalTo(args[i]).matches(actual)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('one of ').appendLiteral(args);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Alias to 'isIn' matcher.
|
|
*/
|
|
JsHamcrest.Matchers.oneOf = JsHamcrest.Matchers.isIn;
|
|
|
|
/**
|
|
* The actual value should be an array and it must be empty to be sucessful.
|
|
*/
|
|
JsHamcrest.Matchers.empty = function() {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return actual.length === 0;
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('empty');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* The length of the actual value value must match the given value or matcher.
|
|
*/
|
|
JsHamcrest.Matchers.hasSize = JsHamcrest.EqualTo(function(matcher) {
|
|
var getSize = function(actual) {
|
|
var size = actual.length;
|
|
if (size === undefined && typeof actual === 'object') {
|
|
size = 0;
|
|
for (var key in actual)
|
|
size++;
|
|
}
|
|
return size;
|
|
};
|
|
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual) {
|
|
return matcher.matches(getSize(actual));
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('has size ').appendDescriptionOf(matcher);
|
|
},
|
|
|
|
describeValueTo: function(actual, description) {
|
|
description.append(getSize(actual));
|
|
}
|
|
});
|
|
});
|
|
|
|
JsHamcrest.Matchers.equivalentMap = function(expected) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual)
|
|
{
|
|
if(JsHamcrest.retreiveEntityMatcherFunction(actual) === JsHamcrest.areMapsEquivalent &&
|
|
JsHamcrest.retreiveEntityMatcherFunction(expected) === JsHamcrest.areMapsEquivalent)
|
|
{
|
|
return JsHamcrest.areMapsEquivalent(expected, actual);
|
|
}
|
|
|
|
return false; //The passed in objects aren't maps.
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('map equivalent to ').appendLiteral(expected);
|
|
}
|
|
});
|
|
};
|
|
|
|
JsHamcrest.Matchers.equivalentArray = function(expected) {
|
|
return new JsHamcrest.SimpleMatcher({
|
|
matches: function(actual)
|
|
{
|
|
if (expected instanceof Array && actual instanceof Array)
|
|
{
|
|
return JsHamcrest.areArraysEquivalent(expected, actual);
|
|
}
|
|
|
|
return false; //The passed in objects aren't Arrays.
|
|
},
|
|
|
|
describeTo: function(description) {
|
|
description.append('array equivalent to ').appendLiteral(expected);
|
|
}
|
|
});
|
|
};
|
|
JsHamcrest.Operators = {};
|
|
|
|
/**
|
|
* Returns those items of the array for which matcher matches.
|
|
*/
|
|
JsHamcrest.Operators.filter = function(array, matcherOrValue) {
|
|
if (!(array instanceof Array) || matcherOrValue == null) {
|
|
return array;
|
|
}
|
|
if (!(matcherOrValue instanceof JsHamcrest.SimpleMatcher)) {
|
|
matcherOrValue = JsHamcrest.Matchers.equalTo(matcherOrValue);
|
|
}
|
|
|
|
var result = [];
|
|
for (var i = 0; i < array.length; i++) {
|
|
if (matcherOrValue.matches(array[i])) {
|
|
result.push(array[i]);
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* Generic assert function.
|
|
*/
|
|
JsHamcrest.Operators.assert = function(actualValue, matcherOrValue, options) {
|
|
options = options ? options : {};
|
|
var description = new JsHamcrest.Description();
|
|
|
|
if (matcherOrValue == null) {
|
|
matcherOrValue = JsHamcrest.Matchers.truth();
|
|
} else if (!JsHamcrest.isMatcher(matcherOrValue)) {
|
|
matcherOrValue = JsHamcrest.Matchers.equalTo(matcherOrValue);
|
|
}
|
|
|
|
if (options.message) {
|
|
description.append(options.message).append('. ');
|
|
}
|
|
|
|
description.append('Expected ');
|
|
matcherOrValue.describeTo(description);
|
|
|
|
if (!matcherOrValue.matches(actualValue)) {
|
|
description.passed = false;
|
|
description.append(' but was ');
|
|
matcherOrValue.describeValueTo(actualValue, description);
|
|
if (options.fail) {
|
|
options.fail(description.get());
|
|
}
|
|
} else {
|
|
description.append(': Success');
|
|
description.passed = true;
|
|
if (options.pass) {
|
|
options.pass(description.get());
|
|
}
|
|
}
|
|
return description;
|
|
};
|
|
|
|
/**
|
|
* Delegate function, useful when used along with raises() and raisesAnything().
|
|
*/
|
|
JsHamcrest.Operators.callTo = function() {
|
|
var func = [].shift.call(arguments);
|
|
var args = arguments;
|
|
return function() {
|
|
return func.apply(this, args);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Integration utilities.
|
|
*/
|
|
|
|
JsHamcrest.Integration = (function() {
|
|
|
|
var self = this;
|
|
|
|
return {
|
|
|
|
/**
|
|
* Copies all members of an object to another.
|
|
*/
|
|
copyMembers: function(source, target) {
|
|
if (arguments.length == 1) {
|
|
target = source;
|
|
JsHamcrest.Integration.copyMembers(JsHamcrest.Matchers, target);
|
|
JsHamcrest.Integration.copyMembers(JsHamcrest.Operators, target);
|
|
} else if (source) {
|
|
for (var method in source) {
|
|
if (!(method in target)) {
|
|
target[method] = source[method];
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Adds the members of the given object to JsHamcrest.Matchers
|
|
* namespace.
|
|
*/
|
|
installMatchers: function(matchersNamespace) {
|
|
var target = JsHamcrest.Matchers;
|
|
JsHamcrest.Integration.copyMembers(matchersNamespace, target);
|
|
},
|
|
|
|
/**
|
|
* Adds the members of the given object to JsHamcrest.Operators
|
|
* namespace.
|
|
*/
|
|
installOperators: function(operatorsNamespace) {
|
|
var target = JsHamcrest.Operators;
|
|
JsHamcrest.Integration.copyMembers(operatorsNamespace, target);
|
|
},
|
|
|
|
/**
|
|
* Uses the web browser's alert() function to display the assertion
|
|
* results. Great for quick prototyping.
|
|
*/
|
|
WebBrowser: function() {
|
|
JsHamcrest.Integration.copyMembers(self);
|
|
|
|
self.assertThat = function (actual, matcher, message) {
|
|
return JsHamcrest.Operators.assert(actual, matcher, {
|
|
message: message,
|
|
fail: function(message) {
|
|
alert('[FAIL] ' + message);
|
|
},
|
|
pass: function(message) {
|
|
alert('[SUCCESS] ' + message);
|
|
}
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Uses the Rhino's print() function to display the assertion results.
|
|
* Great for prototyping.
|
|
*/
|
|
Rhino: function() {
|
|
JsHamcrest.Integration.copyMembers(self);
|
|
|
|
self.assertThat = function (actual, matcher, message) {
|
|
return JsHamcrest.Operators.assert(actual, matcher, {
|
|
message: message,
|
|
fail: function(message) {
|
|
print('[FAIL] ' + message + '\n');
|
|
},
|
|
pass: function(message) {
|
|
print('[SUCCESS] ' + message + '\n');
|
|
}
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* JsTestDriver integration.
|
|
*/
|
|
JsTestDriver: function(params) {
|
|
params = params ? params : {};
|
|
var target = params.scope || self;
|
|
|
|
JsHamcrest.Integration.copyMembers(target);
|
|
|
|
// Function called when an assertion fails.
|
|
function fail(message) {
|
|
var exc = new Error(message);
|
|
exc.name = 'AssertError';
|
|
|
|
try {
|
|
// Removes all jshamcrest-related entries from error stack
|
|
var re = new RegExp('jshamcrest.*\.js\:', 'i');
|
|
var stack = exc.stack.split('\n');
|
|
var newStack = '';
|
|
for (var i = 0; i < stack.length; i++) {
|
|
if (!re.test(stack[i])) {
|
|
newStack += stack[i] + '\n';
|
|
}
|
|
}
|
|
exc.stack = newStack;
|
|
} catch (e) {
|
|
// It's okay, do nothing
|
|
}
|
|
throw exc;
|
|
}
|
|
|
|
// Assertion method exposed to JsTestDriver.
|
|
target.assertThat = function (actual, matcher, message) {
|
|
return JsHamcrest.Operators.assert(actual, matcher, {
|
|
message: message,
|
|
fail: fail
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* NodeUnit (Node.js Unit Testing) integration.
|
|
*/
|
|
|
|
Nodeunit: function(params) {
|
|
params = params ? params : {};
|
|
var target = params.scope || global;
|
|
|
|
JsHamcrest.Integration.copyMembers(target);
|
|
|
|
target.assertThat = function(actual, matcher, message, test) {
|
|
return JsHamcrest.Operators.assert(actual, matcher, {
|
|
message: message,
|
|
fail: function(message) {
|
|
test.ok(false, message);
|
|
},
|
|
pass: function(message) {
|
|
test.ok(true, message);
|
|
}
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* JsUnitTest integration.
|
|
*/
|
|
JsUnitTest: function(params) {
|
|
params = params ? params : {};
|
|
var target = params.scope || JsUnitTest.Unit.Testcase.prototype;
|
|
|
|
JsHamcrest.Integration.copyMembers(target);
|
|
|
|
// Assertion method exposed to JsUnitTest.
|
|
target.assertThat = function (actual, matcher, message) {
|
|
var self = this;
|
|
|
|
return JsHamcrest.Operators.assert(actual, matcher, {
|
|
message: message,
|
|
fail: function(message) {
|
|
self.fail(message);
|
|
},
|
|
pass: function() {
|
|
self.pass();
|
|
}
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* YUITest (Yahoo UI) integration.
|
|
*/
|
|
YUITest: function(params) {
|
|
params = params ? params : {};
|
|
var target = params.scope || self;
|
|
|
|
JsHamcrest.Integration.copyMembers(target);
|
|
|
|
target.Assert = YAHOO.util.Assert;
|
|
|
|
// Assertion method exposed to YUITest.
|
|
YAHOO.util.Assert.that = function(actual, matcher, message) {
|
|
return JsHamcrest.Operators.assert(actual, matcher, {
|
|
message: message,
|
|
fail: function(message) {
|
|
YAHOO.util.Assert.fail(message);
|
|
}
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* QUnit (JQuery) integration.
|
|
*/
|
|
QUnit: function(params) {
|
|
params = params ? params : {};
|
|
var target = params.scope || self;
|
|
|
|
JsHamcrest.Integration.copyMembers(target);
|
|
|
|
// Assertion method exposed to QUnit.
|
|
target.assertThat = function(assert, actual, matcher, message) {
|
|
return JsHamcrest.Operators.assert(actual, matcher, {
|
|
message: message,
|
|
fail: function(message) {
|
|
assert.ok(false, message);
|
|
},
|
|
pass: function(message) {
|
|
assert.ok(true, message);
|
|
}
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* jsUnity integration.
|
|
*/
|
|
jsUnity: function(params) {
|
|
params = params ? params : {};
|
|
var target = params.scope || jsUnity.env.defaultScope;
|
|
var assertions = params.attachAssertions || false;
|
|
|
|
JsHamcrest.Integration.copyMembers(target);
|
|
|
|
if (assertions) {
|
|
jsUnity.attachAssertions(target);
|
|
}
|
|
|
|
// Assertion method exposed to jsUnity.
|
|
target.assertThat = function(actual, matcher, message) {
|
|
return JsHamcrest.Operators.assert(actual, matcher, {
|
|
message: message,
|
|
fail: function(message) {
|
|
throw message;
|
|
}
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Screw.Unit integration.
|
|
*/
|
|
screwunit: function(params) {
|
|
params = params ? params : {};
|
|
var target = params.scope || Screw.Matchers;
|
|
|
|
JsHamcrest.Integration.copyMembers(target);
|
|
|
|
// Assertion method exposed to Screw.Unit.
|
|
target.assertThat = function(actual, matcher, message) {
|
|
return JsHamcrest.Operators.assert(actual, matcher, {
|
|
message: message,
|
|
fail: function(message) {
|
|
throw message;
|
|
}
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Jasmine integration.
|
|
*/
|
|
jasmine: function(params) {
|
|
params = params ? params : {};
|
|
var target = params.scope || self;
|
|
|
|
JsHamcrest.Integration.copyMembers(target);
|
|
|
|
// Assertion method exposed to Jasmine.
|
|
target.assertThat = function(actual, matcher, message) {
|
|
return JsHamcrest.Operators.assert(actual, matcher, {
|
|
message: message,
|
|
fail: function(message) {
|
|
jasmine.getEnv().currentSpec.addMatcherResult(
|
|
new jasmine.ExpectationResult({passed:false, message:message})
|
|
);
|
|
},
|
|
pass: function(message) {
|
|
jasmine.getEnv().currentSpec.addMatcherResult(
|
|
new jasmine.ExpectationResult({passed:true, message:message})
|
|
);
|
|
}
|
|
});
|
|
};
|
|
}
|
|
};
|
|
})();
|
|
|
|
if (typeof exports !== "undefined") exports.JsHamcrest = JsHamcrest; |