# lib/goog/testing/mockmatchers.js

    1// Copyright 2008 The Closure Library Authors. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS-IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.1415/**16 * @fileoverview Matchers to be used with the mock utilities.  They allow for17 * flexible matching by type.  Custom matchers can be created by passing a18 * matcher function into an ArgumentMatcher instance.19 *20 * For examples, please see the unit test.21 *22 */232425goog.provide('goog.testing.mockmatchers');26goog.provide('goog.testing.mockmatchers.ArgumentMatcher');27goog.provide('goog.testing.mockmatchers.IgnoreArgument');28goog.provide('goog.testing.mockmatchers.InstanceOf');29goog.provide('goog.testing.mockmatchers.ObjectEquals');30goog.provide('goog.testing.mockmatchers.RegexpMatch');31goog.provide('goog.testing.mockmatchers.SaveArgument');32goog.provide('goog.testing.mockmatchers.TypeOf');3334goog.require('goog.array');35goog.require('goog.dom');36goog.require('goog.testing.asserts');37383940/**41 * A simple interface for executing argument matching.  A match in this case is42 * testing to see if a supplied object fits a given criteria.  True is returned43 * if the given criteria is met.44 * @param {Function=} opt_matchFn A function that evaluates a given argument45 *     and returns true if it meets a given criteria.46 * @param {?string=} opt_matchName The name expressing intent as part of47 *      an error message for when a match fails.48 * @constructor49 */50goog.testing.mockmatchers.ArgumentMatcher =51    function(opt_matchFn, opt_matchName) {52  /**53   * A function that evaluates a given argument and returns true if it meets a54   * given criteria.55   * @type {Function}56   * @private57   */58  this.matchFn_ = opt_matchFn || null;5960  /**61   * A string indicating the match intent (e.g. isBoolean or isString).62   * @type {?string}63   * @private64   */65  this.matchName_ = opt_matchName || null;66};676869/**70 * A function that takes a match argument and an optional MockExpectation71 * which (if provided) will get error information and returns whether or72 * not it matches.73 * @param {*} toVerify The argument that should be verified.74 * @param {goog.testing.MockExpectation?=} opt_expectation The expectation75 *     for this match.76 * @return {boolean} Whether or not a given argument passes verification.77 */78goog.testing.mockmatchers.ArgumentMatcher.prototype.matches =79    function(toVerify, opt_expectation) {80  if (this.matchFn_) {81    var isamatch = this.matchFn_(toVerify);82    if (!isamatch && opt_expectation) {83      if (this.matchName_) {84        opt_expectation.addErrorMessage('Expected: ' +85            this.matchName_ + ' but was: ' + _displayStringForValue(toVerify));86      } else {87        opt_expectation.addErrorMessage('Expected: missing mockmatcher' +88            ' description but was: ' +89            _displayStringForValue(toVerify));90      }91    }92    return isamatch;93  } else {94    throw Error('No match function defined for this mock matcher');95  }96};979899100/**101 * A matcher that verifies that an argument is an instance of a given class.102 * @param {Function} ctor The class that will be used for verification.103 * @constructor104 * @extends {goog.testing.mockmatchers.ArgumentMatcher}105 * @final106 */107goog.testing.mockmatchers.InstanceOf = function(ctor) {108  goog.testing.mockmatchers.ArgumentMatcher.call(this,109      function(obj) {110        return obj instanceof ctor;111        // NOTE: Browser differences on ctor.toString() output112        // make using that here problematic. So for now, just let113        // people know the instanceOf() failed without providing114        // browser specific details...115      }, 'instanceOf()');116};117goog.inherits(goog.testing.mockmatchers.InstanceOf,118    goog.testing.mockmatchers.ArgumentMatcher);119120121122/**123 * A matcher that verifies that an argument is of a given type (e.g. "object").124 * @param {string} type The type that a given argument must have.125 * @constructor126 * @extends {goog.testing.mockmatchers.ArgumentMatcher}127 * @final128 */129goog.testing.mockmatchers.TypeOf = function(type) {130  goog.testing.mockmatchers.ArgumentMatcher.call(this,131      function(obj) {132        return goog.typeOf(obj) == type;133      }, 'typeOf(' + type + ')');134};135goog.inherits(goog.testing.mockmatchers.TypeOf,136    goog.testing.mockmatchers.ArgumentMatcher);137138139140/**141 * A matcher that verifies that an argument matches a given RegExp.142 * @param {RegExp} regexp The regular expression that the argument must match.143 * @constructor144 * @extends {goog.testing.mockmatchers.ArgumentMatcher}145 * @final146 */147goog.testing.mockmatchers.RegexpMatch = function(regexp) {148  goog.testing.mockmatchers.ArgumentMatcher.call(this,149      function(str) {150        return regexp.test(str);151      }, 'match(' + regexp + ')');152};153goog.inherits(goog.testing.mockmatchers.RegexpMatch,154    goog.testing.mockmatchers.ArgumentMatcher);155156157158/**159 * A matcher that always returns true. It is useful when the user does not care160 * for some arguments.161 * For example: mockFunction('username', 'password', IgnoreArgument);162 * @constructor163 * @extends {goog.testing.mockmatchers.ArgumentMatcher}164 * @final165 */166goog.testing.mockmatchers.IgnoreArgument = function() {167  goog.testing.mockmatchers.ArgumentMatcher.call(this,168      function() {169        return true;170      }, 'true');171};172goog.inherits(goog.testing.mockmatchers.IgnoreArgument,173    goog.testing.mockmatchers.ArgumentMatcher);174175176177/**178 * A matcher that verifies that the argument is an object that equals the given179 * expected object, using a deep comparison.180 * @param {Object} expectedObject An object to match against when181 *     verifying the argument.182 * @constructor183 * @extends {goog.testing.mockmatchers.ArgumentMatcher}184 */185goog.testing.mockmatchers.ObjectEquals = function(expectedObject) {186  goog.testing.mockmatchers.ArgumentMatcher.call(this,187      function(matchObject) {188        assertObjectEquals('Expected equal objects', expectedObject,189            matchObject);190        return true;191      }, 'objectEquals(' + expectedObject + ')');192};193goog.inherits(goog.testing.mockmatchers.ObjectEquals,194    goog.testing.mockmatchers.ArgumentMatcher);195196197/** @override */198goog.testing.mockmatchers.ObjectEquals.prototype.matches =199    function(toVerify, opt_expectation) {200  // Override the default matches implementation to capture the exception thrown201  // by assertObjectEquals (if any) and add that message to the expectation.202  try {203    return goog.testing.mockmatchers.ObjectEquals.superClass_.matches.call(204        this, toVerify, opt_expectation);205  } catch (e) {206    if (opt_expectation) {207      opt_expectation.addErrorMessage(e.message);208    }209    return false;210  }211};212213214215/**216 * A matcher that saves the argument that it is verifying so that your unit test217 * can perform extra tests with this argument later.  For example, if the218 * argument is a callback method, the unit test can then later call this219 * callback to test the asynchronous portion of the call.220 * @param {goog.testing.mockmatchers.ArgumentMatcher|Function=} opt_matcher221 *     Argument matcher or matching function that will be used to validate the222 *     argument.  By default, argument will always be valid.223 * @param {?string=} opt_matchName The name expressing intent as part of224 *      an error message for when a match fails.225 * @constructor226 * @extends {goog.testing.mockmatchers.ArgumentMatcher}227 * @final228 */229goog.testing.mockmatchers.SaveArgument = function(opt_matcher, opt_matchName) {230  goog.testing.mockmatchers.ArgumentMatcher.call(231      this, /** @type {Function} */ (opt_matcher), opt_matchName);232233  if (opt_matcher instanceof goog.testing.mockmatchers.ArgumentMatcher) {234    /**235     * Delegate match requests to this matcher.236     * @type {goog.testing.mockmatchers.ArgumentMatcher}237     * @private238     */239    this.delegateMatcher_ = opt_matcher;240  } else if (!opt_matcher) {241    this.delegateMatcher_ = goog.testing.mockmatchers.ignoreArgument;242  }243};244goog.inherits(goog.testing.mockmatchers.SaveArgument,245    goog.testing.mockmatchers.ArgumentMatcher);246247248/** @override */249goog.testing.mockmatchers.SaveArgument.prototype.matches = function(250    toVerify, opt_expectation) {251  this.arg = toVerify;252  if (this.delegateMatcher_) {253    return this.delegateMatcher_.matches(toVerify, opt_expectation);254  }255  return goog.testing.mockmatchers.SaveArgument.superClass_.matches.call(256      this, toVerify, opt_expectation);257};258259260/**261 * Saved argument that was verified.262 * @type {*}263 */264goog.testing.mockmatchers.SaveArgument.prototype.arg;265266267/**268 * An instance of the IgnoreArgument matcher. Returns true for all matches.269 * @type {goog.testing.mockmatchers.IgnoreArgument}270 */271goog.testing.mockmatchers.ignoreArgument =272    new goog.testing.mockmatchers.IgnoreArgument();273274275/**276 * A matcher that verifies that an argument is an array.277 * @type {goog.testing.mockmatchers.ArgumentMatcher}278 */279goog.testing.mockmatchers.isArray =280    new goog.testing.mockmatchers.ArgumentMatcher(goog.isArray,281        'isArray');282283284/**285 * A matcher that verifies that an argument is a array-like.  A NodeList is an286 * example of a collection that is very close to an array.287 * @type {goog.testing.mockmatchers.ArgumentMatcher}288 */289goog.testing.mockmatchers.isArrayLike =290    new goog.testing.mockmatchers.ArgumentMatcher(goog.isArrayLike,291        'isArrayLike');292293294/**295 * A matcher that verifies that an argument is a date-like.296 * @type {goog.testing.mockmatchers.ArgumentMatcher}297 */298goog.testing.mockmatchers.isDateLike =299    new goog.testing.mockmatchers.ArgumentMatcher(goog.isDateLike,300        'isDateLike');301302303/**304 * A matcher that verifies that an argument is a string.305 * @type {goog.testing.mockmatchers.ArgumentMatcher}306 */307goog.testing.mockmatchers.isString =308    new goog.testing.mockmatchers.ArgumentMatcher(goog.isString,309        'isString');310311312/**313 * A matcher that verifies that an argument is a boolean.314 * @type {goog.testing.mockmatchers.ArgumentMatcher}315 */316goog.testing.mockmatchers.isBoolean =317    new goog.testing.mockmatchers.ArgumentMatcher(goog.isBoolean,318        'isBoolean');319320321/**322 * A matcher that verifies that an argument is a number.323 * @type {goog.testing.mockmatchers.ArgumentMatcher}324 */325goog.testing.mockmatchers.isNumber =326    new goog.testing.mockmatchers.ArgumentMatcher(goog.isNumber,327        'isNumber');328329330/**331 * A matcher that verifies that an argument is a function.332 * @type {goog.testing.mockmatchers.ArgumentMatcher}333 */334goog.testing.mockmatchers.isFunction =335    new goog.testing.mockmatchers.ArgumentMatcher(goog.isFunction,336        'isFunction');337338339/**340 * A matcher that verifies that an argument is an object.341 * @type {goog.testing.mockmatchers.ArgumentMatcher}342 */343goog.testing.mockmatchers.isObject =344    new goog.testing.mockmatchers.ArgumentMatcher(goog.isObject,345        'isObject');346347348/**349 * A matcher that verifies that an argument is like a DOM node.350 * @type {goog.testing.mockmatchers.ArgumentMatcher}351 */352goog.testing.mockmatchers.isNodeLike =353    new goog.testing.mockmatchers.ArgumentMatcher(goog.dom.isNodeLike,354        'isNodeLike');355356357/**358 * A function that checks to see if an array matches a given set of359 * expectations.  The expectations array can be a mix of ArgumentMatcher360 * implementations and values.  True will be returned if values are identical or361 * if a matcher returns a positive result.362 * @param {Array} expectedArr An array of expectations which can be either363 *     values to check for equality or ArgumentMatchers.364 * @param {Array} arr The array to match.365 * @param {goog.testing.MockExpectation?=} opt_expectation The expectation366 *     for this match.367 * @return {boolean} Whether or not the given array matches the expectations.368 */369goog.testing.mockmatchers.flexibleArrayMatcher =370    function(expectedArr, arr, opt_expectation) {371  return goog.array.equals(expectedArr, arr, function(a, b) {372    var errCount = 0;373    if (opt_expectation) {374      errCount = opt_expectation.getErrorMessageCount();375    }376    var isamatch = a === b ||377        a instanceof goog.testing.mockmatchers.ArgumentMatcher &&378        a.matches(b, opt_expectation);379    var failureMessage = null;380    if (!isamatch) {381      failureMessage = goog.testing.asserts.findDifferences(a, b);382      isamatch = !failureMessage;383    }384    if (!isamatch && opt_expectation) {385      // If the error count changed, the match sent out an error386      // message. If the error count has not changed, then387      // we need to send out an error message...388      if (errCount == opt_expectation.getErrorMessageCount()) {389        // Use the _displayStringForValue() from assert.js390        // for consistency...391        if (!failureMessage) {392          failureMessage = 'Expected: ' + _displayStringForValue(a) +393              ' but was: ' + _displayStringForValue(b);394        }395        opt_expectation.addErrorMessage(failureMessage);396      }397    }398    return isamatch;399  });400};