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 at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// 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 and
13// limitations under the License.
14
15/**
16 * @fileoverview Assertions and expectation utilities for use in WebDriver test
17 * cases.
18 */
19
20goog.provide('webdriver.testing.Assertion');
21goog.provide('webdriver.testing.ContainsMatcher');
22goog.provide('webdriver.testing.NegatedAssertion');
23goog.provide('webdriver.testing.assert');
24goog.provide('webdriver.testing.asserts');
25
26goog.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');
47
48
49/**
50 * Accepts strins or array-like structures that contain {@code value}.
51 * @param {*} value The value to check for.
52 * @constructor
53 * @implements {goog.labs.testing.Matcher}
54 */
55webdriver.testing.ContainsMatcher = function(value) {
56 /** @private {*} */
57 this.value_ = value;
58};
59
60
61/** @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};
71
72
73/** @override */
74webdriver.testing.ContainsMatcher.prototype.describe = function(actualValue) {
75 return actualValue + ' does not contain ' + this.value_;
76};
77
78
79
80/**
81 * Utility for performing assertions against a given {@code value}. If the
82 * value is a {@link webdriver.promise.Promise}, this assertion will wait
83 * for it to resolve before applying any matchers.
84 * @param {*} value The value to wrap and apply matchers to.
85 * @constructor
86 */
87webdriver.testing.Assertion = function(value) {
88
89 /** @private {*} */
90 this.value_ = value;
91
92 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;
99
100 /**
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};
108
109
110/**
111 * Wraps an object literal implementing the Matcher interface. This is used
112 * to appease the Closure compiler, which will not treat an object literal as
113 * implementing an interface.
114 * @param {{matches: function(*): boolean, describe: function(): string}} obj
115 * The object literal to delegate to.
116 * @constructor
117 * @implements {goog.labs.testing.Matcher}
118 * @private
119 */
120webdriver.testing.Assertion.DelegatingMatcher_ = function(obj) {
121
122 /** @override */
123 this.matches = function(value) {
124 return obj.matches(value);
125 };
126
127 /** @override */
128 this.describe = function() {
129 return obj.describe();
130 };
131};
132
133
134/**
135 * Asserts that the given {@code matcher} accepts the value wrapped by this
136 * instance. If the wrapped value is a promise, this function will defer
137 * applying the assertion until the value has been resolved. Otherwise, it
138 * will be applied immediately.
139 * @param {!goog.labs.testing.Matcher} matcher The matcher to apply
140 * @param {string=} opt_message A message to include if the matcher does not
141 * accept the value wrapped by this assertion.
142 * @return {webdriver.promise.Promise} The deferred assertion result, or
143 * {@code null} if the assertion was immediately applied.
144 * @protected
145 */
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};
157
158
159/**
160 * Asserts that the value managed by this assertion is a number strictly
161 * greater than {@code value}.
162 * @param {number} value The minimum value.
163 * @param {string=} opt_message A message to include if the matcher does not
164 * 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};
172
173
174/**
175 * Asserts that the value managed by this assertion is a number >= the given
176 * value.
177 * @param {number} value The minimum value.
178 * @param {string=} opt_message A message to include if the matcher does not
179 * 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};
187
188
189/**
190 * Asserts that the value managed by this assertion is a number strictly less
191 * than the given value.
192 * @param {number} value The maximum value.
193 * @param {string=} opt_message A message to include if the matcher does not
194 * 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};
201
202
203/**
204 * Asserts that the value managed by this assertion is a number <= the given
205 * value.
206 * @param {number} value The maximum value.
207 * @param {string=} opt_message A message to include if the matcher does not
208 * 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};
216
217
218/**
219 * Asserts that the wrapped value is a number within a given distance of an
220 * expected value.
221 * @param {number} value The expected value.
222 * @param {number} range The maximum amount the actual value is permitted to
223 * differ from the expected value.
224 * @param {string=} opt_message A message to include if the matcher does not
225 * 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};
233
234
235/**
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 not
239 * 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};
246
247
248/**
249 * Asserts that the wrapped value is null.
250 * @param {string=} opt_message A message to include if the matcher does not
251 * 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};
257
258
259/**
260 * Asserts that the wrapped value is undefined.
261 * @param {string=} opt_message A message to include if the matcher does not
262 * 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};
268
269
270/**
271 * Asserts that the wrapped value is null or undefined.
272 * @param {string=} opt_message A message to include if the matcher does not
273 * 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};
281
282
283/**
284 * Asserts that the wrapped value is a string or array-like structure
285 * containing the given value.
286 * @param {*} value The expected value.
287 * @param {string=} opt_message A message to include if the matcher does not
288 * 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};
295
296
297/**
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 not
301 * 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};
309
310
311/**
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 not
315 * 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};
323
324
325/**
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 not
329 * 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};
335
336
337/**
338 * Asserts that the value managed by this assertion is strictly equal to the
339 * given {@code value}.
340 * @param {*} value The expected value.
341 * @param {string=} opt_message A message to include if the matcher does not
342 * 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};
348
349
350/**
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};
357
358
359/**
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};
366
367
368
369/**
370 * An assertion that negates any applied matchers.
371 * @param {*} value The value to perform assertions on.
372 * @constructor
373 * @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);
381
382
383/** @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};
389
390
391
392/**
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};
400
401
402/**
403 * Registers a new assertion to expose from the
404 * {@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 the
409 * 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};
424
425
426/**
427 * Asserts that a matcher accepts a given value. This function has two
428 * 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 value
436 * to apply to the given matcher.
437 * @param {*} actualValueOrMatcher Either the value to apply to the given
438 * 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);
447
448 var message = args.length > 2 ? args.shift() : '';
449 if (message) message += '\n';
450
451 var actualValue = args.shift();
452 var matcher = args.shift();
453
454 return webdriver.promise.when(actualValue, function(value) {
455 goog.labs.testing.assertThat(value, matcher, message);
456 });
457};
458
459
460/**
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};
475
476
477goog.exportSymbol('assertThat', webdriver.testing.asserts.assertThat);
478// Mappings for goog.labs.testing matcher functions to the legacy
479// 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);