blob: 37e0a9a874d962639b9d7c7d6c16c4e23cb84289 [file] [log] [blame]
// Copyright 2011 Software Freedom Conservancy. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Assertions and expectation utilities for use in WebDriver test
* cases.
*/
goog.provide('webdriver.testing.asserts');
goog.provide('webdriver.testing.asserts.Matcher');
goog.require('goog.array');
goog.require('goog.string');
goog.require('webdriver.promise');
/**
* Describes a matcher used in various assertions.
*
* @param {string} description A description that describes this matcher. Should
* complete the following sentence: "Expected ....".
* @param {function(*):boolean} predicate A predicate function that applies this
* matcher to any value. Should return whether the value is a match.
* @constructor
*/
webdriver.testing.asserts.Matcher = function(description, predicate) {
/**
* A description of this matcher.
* @type {string}
*/
this.description = description;
/**
* The predicate for this matcher.
* @type {function(*):boolean}
*/
this.predicate = predicate;
};
/**
* Tests if a value can be used as a matcher.
* @param {*} value The value to test.
* @return {boolean} Whether the value can be considered a matcher object.
*/
webdriver.testing.asserts.Matcher.isMatcher = function(value) {
return value && goog.isString(value.description) &&
goog.isFunction(value.predicate);
};
/**
* Applies a matcher to a given value. The return promise will be rejected if
* the matcher does not match the value. This function has two signatures based
* on the number of arguments:
*
* Two arguments:
* assertThat(actualValue, matcher)
* Three arguments:
* assertThat(failureMessage, actualValue, matcher)
*
* @param {*} failureMessageOrActualValue Either a failure message or the value
* to apply to the given matcher.
* @param {*} actualValueOrMatcher Either the value to apply to the given
* matcher, or the matcher itself.
* @param {webdriver.testing.asserts.Matcher=} opt_matcher The matcher to use;
* ignored unless this function is invoked with three arguments.
* @return {!webdriver.promise.Promise} The result of the matcher test.
*/
webdriver.testing.asserts.applyMatcher = function(
failureMessageOrActualValue, actualValueOrMatcher, opt_matcher) {
var args = goog.array.slice(arguments, 0);
var message = args.length > 2 ? args.shift() : '';
if (message) message += '\n';
var actualValue = args.shift();
var matcher = args.shift();
if (!webdriver.testing.asserts.Matcher.isMatcher(matcher)) {
throw new Error('Invalid matcher: ' + goog.typeOf(matcher));
}
return webdriver.promise.when(actualValue, function(value) {
if (!matcher.predicate(value)) {
var error = [];
if (message) {
error.push(message, '\n');
}
error.push('Expected ', matcher.description,
'\n but was ', value, webdriver.testing.asserts.typeOf_(value));
throw new Error(error.join(''));
}
});
};
/**
* @param {*} value The value to query.
* @return {string} The type of the value.
* @private
*/
webdriver.testing.asserts.typeOf_ = function(value) {
return ' (' + goog.typeOf(value) + ')';
};
/**
* Asserts that a matcher accepts a given value. If the value is rejected by
* the matcher, an unhandled promise will be reported to the global
* {@link webdriver.promise.Application}.
*
* @param {*} failureMessageOrActualValue Either a failure message or the value
* to apply to the given matcher.
* @param {*} actualValueOrMatcher Either the value to apply to the given
* matcher, or the matcher itself.
* @param {webdriver.testing.asserts.Matcher=} opt_matcher The matcher to use;
* ignored unless this function is invoked with three arguments.
*/
webdriver.testing.asserts.assertThat = function(
failureMessageOrActualValue, actualValueOrMatcher, opt_matcher) {
webdriver.testing.asserts.applyMatcher.apply(null, arguments);
};
/**
* Creates a matcher that inverts another matcher.
* @param {!webdriver.testing.asserts.Matcher} matcher The matcher to invert.
* @return {!webdriver.testing.asserts.Matcher} The new matcher.
*/
webdriver.testing.asserts.not = function(matcher) {
return new webdriver.testing.asserts.Matcher('not ' + matcher.description,
function(value) {
return !matcher.predicate(value);
});
};
/**
* Creates a logical union of two matchers.
* @param {!webdriver.testing.asserts.Matcher} a The first matcher in the union.
* @param {!webdriver.testing.asserts.Matcher} b The second matcher in the
* union.
* @return {!webdriver.testing.asserts.Matcher} The new matcher.
*/
webdriver.testing.asserts.or = function(a, b) {
return new webdriver.testing.asserts.Matcher(
a.description + ' or ' + b.description,
function(value) {
return a.predicate(value) || b.predicate(value);
});
};
/**
* Creates a matcher that does a strict equality (===) check.
* @param {*} expected The expected value.
* @return {!webdriver.testing.asserts.Matcher} The new matcher.
*/
webdriver.testing.asserts.equalTo = function(expected) {
return new webdriver.testing.asserts.Matcher(
'to equal ' + expected + webdriver.testing.asserts.typeOf_(expected),
function(actual) {
return expected === actual;
});
};
/**
* Creates a matcher that verifies a string contains a substring.
* @param {string} expected The expected substring.
* @return {!webdriver.testing.asserts.Matcher} The new matcher.
*/
webdriver.testing.asserts.contains = function(expected) {
return new webdriver.testing.asserts.Matcher('to contain ' + expected,
function(actual) {
return goog.string.contains(actual, expected);
});
};
/**
* Creates a matcher that verifies a string matchess a regular expression.
* @param {!RegExp} regex The regex to check against.
* @return {!webdriver.testing.asserts.Matcher} The new matcher.
*/
webdriver.testing.asserts.matchesRegex = function(regex) {
return new webdriver.testing.asserts.Matcher('to match regex ' + regex,
function(value) {
return !!value.match(regex);
});
};
/**
* Creates a matcher that verifies a string starts with another string.
* @param {string} expected The expected string prefix.
* @return {!webdriver.testing.asserts.Matcher} The new matcher.
*/
webdriver.testing.asserts.startsWith = function(expected) {
return new webdriver.testing.asserts.Matcher(
'to start with ' + expected,
function(value) {
return goog.string.startsWith(value, expected);
});
};
goog.exportSymbol('assertThat', webdriver.testing.asserts.assertThat);
goog.exportSymbol('contains', webdriver.testing.asserts.contains);
goog.exportSymbol('equalTo', webdriver.testing.asserts.equalTo);
goog.exportSymbol('equals', webdriver.testing.asserts.equalTo);
goog.exportSymbol('is', webdriver.testing.asserts.equalTo);
goog.exportSymbol('matchesRegex', webdriver.testing.asserts.matchesRegex);
goog.exportSymbol('not', webdriver.testing.asserts.not);
goog.exportSymbol('or', webdriver.testing.asserts.or);
goog.exportSymbol('startsWith', webdriver.testing.asserts.startsWith);