# lib/webdriver/testing/asserts.js

    1// Copyright 2011 Software Freedom Conservancy. 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 Assertions and expectation utilities for use in WebDriver test17 * cases.18 */1920goog.provide('webdriver.testing.Assertion');21goog.provide('webdriver.testing.ContainsMatcher');22goog.provide('webdriver.testing.NegatedAssertion');23goog.provide('webdriver.testing.assert');24goog.provide('webdriver.testing.asserts');2526goog.require('goog.array');27goog.require('goog.labs.testing.CloseToMatcher');28goog.require('goog.labs.testing.EndsWithMatcher');29goog.require('goog.labs.testing.EqualToMatcher');30goog.require('goog.labs.testing.EqualsMatcher');31goog.require('goog.labs.testing.GreaterThanEqualToMatcher');32goog.require('goog.labs.testing.GreaterThanMatcher');33goog.require('goog.labs.testing.LessThanEqualToMatcher');34goog.require('goog.labs.testing.LessThanMatcher');35goog.require('goog.labs.testing.InstanceOfMatcher');36goog.require('goog.labs.testing.IsNotMatcher');37goog.require('goog.labs.testing.IsNullMatcher');38goog.require('goog.labs.testing.IsNullOrUndefinedMatcher');39goog.require('goog.labs.testing.IsUndefinedMatcher');40goog.require('goog.labs.testing.Matcher');41goog.require('goog.labs.testing.ObjectEqualsMatcher');42goog.require('goog.labs.testing.RegexMatcher');43goog.require('goog.labs.testing.StartsWithMatcher');44goog.require('goog.labs.testing.assertThat');45goog.require('goog.string');46goog.require('webdriver.promise');474849/**50 * Accepts strins or array-like structures that contain {@code value}.51 * @param {*} value The value to check for.52 * @constructor53 * @implements {goog.labs.testing.Matcher}54 */55webdriver.testing.ContainsMatcher = function(value) {56  /** @private {*} */57  this.value_ = value;58};596061/** @override */62webdriver.testing.ContainsMatcher.prototype.matches = function(actualValue) {63  if (goog.isString(actualValue)) {64    return goog.string.contains(65        actualValue, /** @type {string} */(this.value_));66  } else {67    return goog.array.contains(68        /** @type {goog.array.ArrayLike} */(actualValue), this.value_);69  }70};717273/** @override */74webdriver.testing.ContainsMatcher.prototype.describe = function(actualValue) {75  return actualValue + ' does not contain ' + this.value_;76};77787980/**81 * Utility for performing assertions against a given {@code value}. If the82 * value is a {@link webdriver.promise.Promise}, this assertion will wait83 * for it to resolve before applying any matchers.84 * @param {*} value The value to wrap and apply matchers to.85 * @constructor86 */87webdriver.testing.Assertion = function(value) {8889  /** @private {*} */90  this.value_ = value;9192  if (!(this instanceof webdriver.testing.NegatedAssertion)) {93    /**94     * A self reference provided for writing fluent assertions:95     *     webdriver.testing.assert(x).is.equalTo(y);96     * @type {!webdriver.testing.Assertion}97     */98    this.is = this;99100    /**101     * Negates any matchers applied to this instance's value:102     *     webdriver.testing.assert(x).not.equalTo(y);103     * @type {!webdriver.testing.NegatedAssertion}104     */105    this.not = new webdriver.testing.NegatedAssertion(value);106  }107};108109110/**111 * Wraps an object literal implementing the Matcher interface. This is used112 * to appease the Closure compiler, which will not treat an object literal as113 * implementing an interface.114 * @param {{matches: function(*): boolean, describe: function(): string}} obj115 *     The object literal to delegate to.116 * @constructor117 * @implements {goog.labs.testing.Matcher}118 * @private119 */120webdriver.testing.Assertion.DelegatingMatcher_ = function(obj) {121122  /** @override */123  this.matches = function(value) {124    return obj.matches(value);125  };126127  /** @override */128  this.describe = function() {129    return obj.describe();130  };131};132133134/**135 * Asserts that the given {@code matcher} accepts the value wrapped by this136 * instance. If the wrapped value is a promise, this function will defer137 * applying the assertion until the value has been resolved. Otherwise, it138 * will be applied immediately.139 * @param {!goog.labs.testing.Matcher} matcher The matcher to apply140 * @param {string=} opt_message A message to include if the matcher does not141 *     accept the value wrapped by this assertion.142 * @return {webdriver.promise.Promise} The deferred assertion result, or143 *     {@code null} if the assertion was immediately applied.144 * @protected145 */146webdriver.testing.Assertion.prototype.apply = function(matcher, opt_message) {147  var result = null;148  if (webdriver.promise.isPromise(this.value_)) {149    result = webdriver.promise.when(this.value_, function(value) {150      goog.labs.testing.assertThat(value, matcher, opt_message);151    });152  } else {153    goog.labs.testing.assertThat(this.value_, matcher, opt_message);154  }155  return result;156};157158159/**160 * Asserts that the value managed by this assertion is a number strictly161 * greater than {@code value}.162 * @param {number} value The minimum value.163 * @param {string=} opt_message A message to include if the matcher does not164 *     accept the value wrapped by this assertion.165 * @return {webdriver.promise.Promise} The assertion result.166 */167webdriver.testing.Assertion.prototype.greaterThan = function(168    value, opt_message) {169  return this.apply(170      new goog.labs.testing.GreaterThanMatcher(value), opt_message);171};172173174/**175 * Asserts that the value managed by this assertion is a number >= the given176 * value.177 * @param {number} value The minimum value.178 * @param {string=} opt_message A message to include if the matcher does not179 *     accept the value wrapped by this assertion.180 * @return {webdriver.promise.Promise} The assertion result.181 */182webdriver.testing.Assertion.prototype.greaterThanEqualTo = function(183    value, opt_message) {184  return this.apply(185      new goog.labs.testing.GreaterThanEqualToMatcher(value), opt_message);186};187188189/**190 * Asserts that the value managed by this assertion is a number strictly less191 * than the given value.192 * @param {number} value The maximum value.193 * @param {string=} opt_message A message to include if the matcher does not194 *     accept the value wrapped by this assertion.195 * @return {webdriver.promise.Promise} The assertion result.196 */197webdriver.testing.Assertion.prototype.lessThan = function(value, opt_message) {198  return this.apply(199      new goog.labs.testing.LessThanMatcher(value), opt_message);200};201202203/**204 * Asserts that the value managed by this assertion is a number <= the given205 * value.206 * @param {number} value The maximum value.207 * @param {string=} opt_message A message to include if the matcher does not208 *     accept the value wrapped by this assertion.209 * @return {webdriver.promise.Promise} The assertion result.210 */211webdriver.testing.Assertion.prototype.lessThanEqualTo = function(212    value, opt_message) {213  return this.apply(214      new goog.labs.testing.LessThanEqualToMatcher(value), opt_message);215};216217218/**219 * Asserts that the wrapped value is a number within a given distance of an220 * expected value.221 * @param {number} value The expected value.222 * @param {number} range The maximum amount the actual value is permitted to223 *     differ from the expected value.224 * @param {string=} opt_message A message to include if the matcher does not225 *     accept the value wrapped by this assertion.226 * @return {webdriver.promise.Promise} The assertion result.227 */228webdriver.testing.Assertion.prototype.closeTo = function(229    value, range, opt_message) {230  return this.apply(231      new goog.labs.testing.CloseToMatcher(value, range), opt_message);232};233234235/**236 * Asserts that the wrapped value is an instance of the given class.237 * @param {!Function} ctor The expected class constructor.238 * @param {string=} opt_message A message to include if the matcher does not239 *     accept the value wrapped by this assertion.240 * @return {webdriver.promise.Promise} The assertion result.241 */242webdriver.testing.Assertion.prototype.instanceOf = function(ctor, opt_message) {243  return this.apply(244      new goog.labs.testing.InstanceOfMatcher(ctor), opt_message);245};246247248/**249 * Asserts that the wrapped value is null.250 * @param {string=} opt_message A message to include if the matcher does not251 *     accept the value wrapped by this assertion.252 * @return {webdriver.promise.Promise} The assertion result.253 */254webdriver.testing.Assertion.prototype.isNull = function(opt_message) {255  return this.apply(new goog.labs.testing.IsNullMatcher(), opt_message);256};257258259/**260 * Asserts that the wrapped value is undefined.261 * @param {string=} opt_message A message to include if the matcher does not262 *     accept the value wrapped by this assertion.263 * @return {webdriver.promise.Promise} The assertion result.264 */265webdriver.testing.Assertion.prototype.isUndefined = function(opt_message) {266  return this.apply(new goog.labs.testing.IsUndefinedMatcher(), opt_message);267};268269270/**271 * Asserts that the wrapped value is null or undefined.272 * @param {string=} opt_message A message to include if the matcher does not273 *     accept the value wrapped by this assertion.274 * @return {webdriver.promise.Promise} The assertion result.275 */276webdriver.testing.Assertion.prototype.isNullOrUndefined = function(277    opt_message) {278  return this.apply(279      new goog.labs.testing.IsNullOrUndefinedMatcher(), opt_message);280};281282283/**284 * Asserts that the wrapped value is a string or array-like structure285 * containing the given value.286 * @param {*} value The expected value.287 * @param {string=} opt_message A message to include if the matcher does not288 *     accept the value wrapped by this assertion.289 * @return {webdriver.promise.Promise} The assertion result.290 */291webdriver.testing.Assertion.prototype.contains = function(value, opt_message) {292  return this.apply(293      new webdriver.testing.ContainsMatcher(value), opt_message);294};295296297/**298 * Asserts that the wrapped value is a string ending with the given suffix.299 * @param {string} suffix The expected suffix.300 * @param {string=} opt_message A message to include if the matcher does not301 *     accept the value wrapped by this assertion.302 * @return {webdriver.promise.Promise} The assertion result.303 */304webdriver.testing.Assertion.prototype.endsWith = function(305    suffix, opt_message) {306  return this.apply(307      new goog.labs.testing.EndsWithMatcher(suffix), opt_message);308};309310311/**312 * Asserts that the wrapped value is a string starting with the given prefix.313 * @param {string} prefix The expected prefix.314 * @param {string=} opt_message A message to include if the matcher does not315 *     accept the value wrapped by this assertion.316 * @return {webdriver.promise.Promise} The assertion result.317 */318webdriver.testing.Assertion.prototype.startsWith = function(319    prefix, opt_message) {320  return this.apply(321      new goog.labs.testing.StartsWithMatcher(prefix), opt_message);322};323324325/**326 * Asserts that the wrapped value is a string that matches the given RegExp.327 * @param {!RegExp} regex The regex to test.328 * @param {string=} opt_message A message to include if the matcher does not329 *     accept the value wrapped by this assertion.330 * @return {webdriver.promise.Promise} The assertion result.331 */332webdriver.testing.Assertion.prototype.matches = function(regex, opt_message) {333  return this.apply(new goog.labs.testing.RegexMatcher(regex), opt_message);334};335336337/**338 * Asserts that the value managed by this assertion is strictly equal to the339 * given {@code value}.340 * @param {*} value The expected value.341 * @param {string=} opt_message A message to include if the matcher does not342 *     accept the value wrapped by this assertion.343 * @return {webdriver.promise.Promise} The assertion result.344 */345webdriver.testing.Assertion.prototype.equalTo = function(value, opt_message) {346  return this.apply(webdriver.testing.asserts.equalTo(value), opt_message);347};348349350/**351 * Asserts that the value managed by this assertion is strictly true.352 * @return {webdriver.promise.Promise} The assertion result.353 */354webdriver.testing.Assertion.prototype.isTrue = function() {355  return this.equalTo(true);356};357358359/**360 * Asserts that the value managed by this assertion is strictly false.361 * @return {webdriver.promise.Promise} The assertion result.362 */363webdriver.testing.Assertion.prototype.isFalse = function() {364  return this.equalTo(false);365};366367368369/**370 * An assertion that negates any applied matchers.371 * @param {*} value The value to perform assertions on.372 * @constructor373 * @extends {webdriver.testing.Assertion}374 */375webdriver.testing.NegatedAssertion = function(value) {376  goog.base(this, value);377  this.value = value;378};379goog.inherits(380    webdriver.testing.NegatedAssertion, webdriver.testing.Assertion);381382383/** @override */384webdriver.testing.NegatedAssertion.prototype.apply = function(385    matcher, opt_message) {386  matcher = new goog.labs.testing.IsNotMatcher(matcher);387  return goog.base(this, 'apply', matcher, opt_message);388};389390391392/**393 * Creates a new assertion.394 * @param {*} value The value to perform an assertion on.395 * @return {!webdriver.testing.Assertion} The new assertion.396 */397webdriver.testing.assert = function(value) {398  return new webdriver.testing.Assertion(value);399};400401402/**403 * Registers a new assertion to expose from the404 * {@link webdriver.testing.Assertion} prototype.405 * @param {string} name The assertion name.406 * @param {(function(new: goog.labs.testing.Matcher, *)|407 *          {matches: function(*): boolean,408 *           describe: function(): string})} matcherTemplate Either the409 *     matcher constructor to use, or an object literal defining a matcher.410 */411webdriver.testing.assert.register = function(name, matcherTemplate) {412  webdriver.testing.Assertion.prototype[name] = function(value, opt_message) {413    var matcher;414    if (goog.isFunction(matcherTemplate)) {415      var ctor = /** @type {function(new: goog.labs.testing.Matcher, *)} */ (416          matcherTemplate);417      matcher = new ctor(value);418    } else {419      matcher = new webdriver.testing.Assertion.DelegatingMatcher_(value);420    }421    return this.apply(matcher, opt_message);422  };423};424425426/**427 * Asserts that a matcher accepts a given value.  This function has two428 * signatures based on the number of arguments:429 *430 * Two arguments:431 *   assertThat(actualValue, matcher)432 * Three arguments:433 *   assertThat(failureMessage, actualValue, matcher)434 *435 * @param {*} failureMessageOrActualValue Either a failure message or the value436 *     to apply to the given matcher.437 * @param {*} actualValueOrMatcher Either the value to apply to the given438 *     matcher, or the matcher itself.439 * @param {goog.labs.testing.Matcher=} opt_matcher The matcher to use;440 *     ignored unless this function is invoked with three arguments.441 * @return {!webdriver.promise.Promise} The assertion result.442 * @deprecated Use webdriver.testing.asserts.assert instead.443 */444webdriver.testing.asserts.assertThat = function(445    failureMessageOrActualValue, actualValueOrMatcher, opt_matcher) {446  var args = goog.array.slice(arguments, 0);447448  var message = args.length > 2 ? args.shift() : '';449  if (message) message += '\n';450451  var actualValue = args.shift();452  var matcher = args.shift();453454  return webdriver.promise.when(actualValue, function(value) {455    goog.labs.testing.assertThat(value, matcher, message);456  });457};458459460/**461 * Creates an equality matcher.462 * @param {*} expected The expected value.463 * @return {!goog.labs.testing.Matcher} The new matcher.464 */465webdriver.testing.asserts.equalTo = function(expected) {466  if (goog.isString(expected)) {467    return new goog.labs.testing.EqualsMatcher(expected);468  } else if (goog.isNumber(expected)) {469    return new goog.labs.testing.EqualToMatcher(expected);470  } else {471    return new goog.labs.testing.ObjectEqualsMatcher(472        /** @type {!Object} */ (expected));473  }474};475476477goog.exportSymbol('assertThat', webdriver.testing.asserts.assertThat);478// Mappings for goog.labs.testing matcher functions to the legacy479// webdriver.testing.asserts matchers.480goog.exportSymbol('contains', containsString);481goog.exportSymbol('equalTo', webdriver.testing.asserts.equalTo);482goog.exportSymbol('equals', webdriver.testing.asserts.equalTo);483goog.exportSymbol('is', webdriver.testing.asserts.equalTo);484goog.exportSymbol('not', isNot);485goog.exportSymbol('or', anyOf);