/* * 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;