lib/webdriver/testing/asserts.js

1// Licensed to the Software Freedom Conservancy (SFC) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The SFC licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18/**
19 * @fileoverview Assertions and expectation utilities for use in WebDriver test
20 * cases.
21 */
22
23goog.provide('webdriver.testing.Assertion');
24goog.provide('webdriver.testing.ContainsMatcher');
25goog.provide('webdriver.testing.NegatedAssertion');
26goog.provide('webdriver.testing.assert');
27goog.provide('webdriver.testing.asserts');
28
29goog.require('goog.array');
30goog.require('goog.labs.testing.CloseToMatcher');
31goog.require('goog.labs.testing.EndsWithMatcher');
32goog.require('goog.labs.testing.EqualToMatcher');
33goog.require('goog.labs.testing.EqualsMatcher');
34goog.require('goog.labs.testing.GreaterThanEqualToMatcher');
35goog.require('goog.labs.testing.GreaterThanMatcher');
36goog.require('goog.labs.testing.LessThanEqualToMatcher');
37goog.require('goog.labs.testing.LessThanMatcher');
38goog.require('goog.labs.testing.InstanceOfMatcher');
39goog.require('goog.labs.testing.IsNotMatcher');
40goog.require('goog.labs.testing.IsNullMatcher');
41goog.require('goog.labs.testing.IsNullOrUndefinedMatcher');
42goog.require('goog.labs.testing.IsUndefinedMatcher');
43goog.require('goog.labs.testing.Matcher');
44goog.require('goog.labs.testing.ObjectEqualsMatcher');
45goog.require('goog.labs.testing.RegexMatcher');
46goog.require('goog.labs.testing.StartsWithMatcher');
47goog.require('goog.labs.testing.assertThat');
48goog.require('goog.string');
49goog.require('webdriver.promise');
50
51
52/**
53 * Accepts strins or array-like structures that contain {@code value}.
54 * @param {*} value The value to check for.
55 * @constructor
56 * @implements {goog.labs.testing.Matcher}
57 */
58webdriver.testing.ContainsMatcher = function(value) {
59 /** @private {*} */
60 this.value_ = value;
61};
62
63
64/** @override */
65webdriver.testing.ContainsMatcher.prototype.matches = function(actualValue) {
66 if (goog.isString(actualValue)) {
67 return goog.string.contains(
68 actualValue, /** @type {string} */(this.value_));
69 } else {
70 return goog.array.contains(
71 /** @type {goog.array.ArrayLike} */(actualValue), this.value_);
72 }
73};
74
75
76/** @override */
77webdriver.testing.ContainsMatcher.prototype.describe = function(actualValue) {
78 return actualValue + ' does not contain ' + this.value_;
79};
80
81
82
83/**
84 * Utility for performing assertions against a given {@code value}. If the
85 * value is a {@link webdriver.promise.Promise}, this assertion will wait
86 * for it to resolve before applying any matchers.
87 * @param {*} value The value to wrap and apply matchers to.
88 * @constructor
89 */
90webdriver.testing.Assertion = function(value) {
91
92 /** @private {*} */
93 this.value_ = value;
94
95 if (!(this instanceof webdriver.testing.NegatedAssertion)) {
96 /**
97 * A self reference provided for writing fluent assertions:
98 * webdriver.testing.assert(x).is.equalTo(y);
99 * @type {!webdriver.testing.Assertion}
100 */
101 this.is = this;
102
103 /**
104 * Negates any matchers applied to this instance's value:
105 * webdriver.testing.assert(x).not.equalTo(y);
106 * @type {!webdriver.testing.NegatedAssertion}
107 */
108 this.not = new webdriver.testing.NegatedAssertion(value);
109 }
110};
111
112
113/**
114 * Wraps an object literal implementing the Matcher interface. This is used
115 * to appease the Closure compiler, which will not treat an object literal as
116 * implementing an interface.
117 * @param {{matches: function(*): boolean, describe: function(): string}} obj
118 * The object literal to delegate to.
119 * @constructor
120 * @implements {goog.labs.testing.Matcher}
121 * @private
122 */
123webdriver.testing.Assertion.DelegatingMatcher_ = function(obj) {
124
125 /** @override */
126 this.matches = function(value) {
127 return obj.matches(value);
128 };
129
130 /** @override */
131 this.describe = function() {
132 return obj.describe();
133 };
134};
135
136
137/**
138 * Asserts that the given {@code matcher} accepts the value wrapped by this
139 * instance. If the wrapped value is a promise, this function will defer
140 * applying the assertion until the value has been resolved. Otherwise, it
141 * will be applied immediately.
142 * @param {!goog.labs.testing.Matcher} matcher The matcher to apply
143 * @param {string=} opt_message A message to include if the matcher does not
144 * accept the value wrapped by this assertion.
145 * @return {webdriver.promise.Promise} The deferred assertion result, or
146 * {@code null} if the assertion was immediately applied.
147 * @protected
148 */
149webdriver.testing.Assertion.prototype.apply = function(matcher, opt_message) {
150 var result = null;
151 if (webdriver.promise.isPromise(this.value_)) {
152 result = webdriver.promise.when(this.value_, function(value) {
153 goog.labs.testing.assertThat(value, matcher, opt_message);
154 });
155 } else {
156 goog.labs.testing.assertThat(this.value_, matcher, opt_message);
157 }
158 return result;
159};
160
161
162/**
163 * Asserts that the value managed by this assertion is a number strictly
164 * greater than {@code value}.
165 * @param {number} value The minimum value.
166 * @param {string=} opt_message A message to include if the matcher does not
167 * accept the value wrapped by this assertion.
168 * @return {webdriver.promise.Promise} The assertion result.
169 */
170webdriver.testing.Assertion.prototype.greaterThan = function(
171 value, opt_message) {
172 return this.apply(
173 new goog.labs.testing.GreaterThanMatcher(value), opt_message);
174};
175
176
177/**
178 * Asserts that the value managed by this assertion is a number >= the given
179 * value.
180 * @param {number} value The minimum value.
181 * @param {string=} opt_message A message to include if the matcher does not
182 * accept the value wrapped by this assertion.
183 * @return {webdriver.promise.Promise} The assertion result.
184 */
185webdriver.testing.Assertion.prototype.greaterThanEqualTo = function(
186 value, opt_message) {
187 return this.apply(
188 new goog.labs.testing.GreaterThanEqualToMatcher(value), opt_message);
189};
190
191
192/**
193 * Asserts that the value managed by this assertion is a number strictly less
194 * than the given value.
195 * @param {number} value The maximum value.
196 * @param {string=} opt_message A message to include if the matcher does not
197 * accept the value wrapped by this assertion.
198 * @return {webdriver.promise.Promise} The assertion result.
199 */
200webdriver.testing.Assertion.prototype.lessThan = function(value, opt_message) {
201 return this.apply(
202 new goog.labs.testing.LessThanMatcher(value), opt_message);
203};
204
205
206/**
207 * Asserts that the value managed by this assertion is a number <= the given
208 * value.
209 * @param {number} value The maximum value.
210 * @param {string=} opt_message A message to include if the matcher does not
211 * accept the value wrapped by this assertion.
212 * @return {webdriver.promise.Promise} The assertion result.
213 */
214webdriver.testing.Assertion.prototype.lessThanEqualTo = function(
215 value, opt_message) {
216 return this.apply(
217 new goog.labs.testing.LessThanEqualToMatcher(value), opt_message);
218};
219
220
221/**
222 * Asserts that the wrapped value is a number within a given distance of an
223 * expected value.
224 * @param {number} value The expected value.
225 * @param {number} range The maximum amount the actual value is permitted to
226 * differ from the expected value.
227 * @param {string=} opt_message A message to include if the matcher does not
228 * accept the value wrapped by this assertion.
229 * @return {webdriver.promise.Promise} The assertion result.
230 */
231webdriver.testing.Assertion.prototype.closeTo = function(
232 value, range, opt_message) {
233 return this.apply(
234 new goog.labs.testing.CloseToMatcher(value, range), opt_message);
235};
236
237
238/**
239 * Asserts that the wrapped value is an instance of the given class.
240 * @param {!Function} ctor The expected class constructor.
241 * @param {string=} opt_message A message to include if the matcher does not
242 * accept the value wrapped by this assertion.
243 * @return {webdriver.promise.Promise} The assertion result.
244 */
245webdriver.testing.Assertion.prototype.instanceOf = function(ctor, opt_message) {
246 return this.apply(
247 new goog.labs.testing.InstanceOfMatcher(ctor), opt_message);
248};
249
250
251/**
252 * Asserts that the wrapped value is null.
253 * @param {string=} opt_message A message to include if the matcher does not
254 * accept the value wrapped by this assertion.
255 * @return {webdriver.promise.Promise} The assertion result.
256 */
257webdriver.testing.Assertion.prototype.isNull = function(opt_message) {
258 return this.apply(new goog.labs.testing.IsNullMatcher(), opt_message);
259};
260
261
262/**
263 * Asserts that the wrapped value is undefined.
264 * @param {string=} opt_message A message to include if the matcher does not
265 * accept the value wrapped by this assertion.
266 * @return {webdriver.promise.Promise} The assertion result.
267 */
268webdriver.testing.Assertion.prototype.isUndefined = function(opt_message) {
269 return this.apply(new goog.labs.testing.IsUndefinedMatcher(), opt_message);
270};
271
272
273/**
274 * Asserts that the wrapped value is null or undefined.
275 * @param {string=} opt_message A message to include if the matcher does not
276 * accept the value wrapped by this assertion.
277 * @return {webdriver.promise.Promise} The assertion result.
278 */
279webdriver.testing.Assertion.prototype.isNullOrUndefined = function(
280 opt_message) {
281 return this.apply(
282 new goog.labs.testing.IsNullOrUndefinedMatcher(), opt_message);
283};
284
285
286/**
287 * Asserts that the wrapped value is a string or array-like structure
288 * containing the given value.
289 * @param {*} value The expected value.
290 * @param {string=} opt_message A message to include if the matcher does not
291 * accept the value wrapped by this assertion.
292 * @return {webdriver.promise.Promise} The assertion result.
293 */
294webdriver.testing.Assertion.prototype.contains = function(value, opt_message) {
295 return this.apply(
296 new webdriver.testing.ContainsMatcher(value), opt_message);
297};
298
299
300/**
301 * Asserts that the wrapped value is a string ending with the given suffix.
302 * @param {string} suffix The expected suffix.
303 * @param {string=} opt_message A message to include if the matcher does not
304 * accept the value wrapped by this assertion.
305 * @return {webdriver.promise.Promise} The assertion result.
306 */
307webdriver.testing.Assertion.prototype.endsWith = function(
308 suffix, opt_message) {
309 return this.apply(
310 new goog.labs.testing.EndsWithMatcher(suffix), opt_message);
311};
312
313
314/**
315 * Asserts that the wrapped value is a string starting with the given prefix.
316 * @param {string} prefix The expected prefix.
317 * @param {string=} opt_message A message to include if the matcher does not
318 * accept the value wrapped by this assertion.
319 * @return {webdriver.promise.Promise} The assertion result.
320 */
321webdriver.testing.Assertion.prototype.startsWith = function(
322 prefix, opt_message) {
323 return this.apply(
324 new goog.labs.testing.StartsWithMatcher(prefix), opt_message);
325};
326
327
328/**
329 * Asserts that the wrapped value is a string that matches the given RegExp.
330 * @param {!RegExp} regex The regex to test.
331 * @param {string=} opt_message A message to include if the matcher does not
332 * accept the value wrapped by this assertion.
333 * @return {webdriver.promise.Promise} The assertion result.
334 */
335webdriver.testing.Assertion.prototype.matches = function(regex, opt_message) {
336 return this.apply(new goog.labs.testing.RegexMatcher(regex), opt_message);
337};
338
339
340/**
341 * Asserts that the value managed by this assertion is strictly equal to the
342 * given {@code value}.
343 * @param {*} value The expected value.
344 * @param {string=} opt_message A message to include if the matcher does not
345 * accept the value wrapped by this assertion.
346 * @return {webdriver.promise.Promise} The assertion result.
347 */
348webdriver.testing.Assertion.prototype.equalTo = function(value, opt_message) {
349 return this.apply(webdriver.testing.asserts.equalTo(value), opt_message);
350};
351
352
353/**
354 * Asserts that the value managed by this assertion is strictly true.
355 * @return {webdriver.promise.Promise} The assertion result.
356 */
357webdriver.testing.Assertion.prototype.isTrue = function() {
358 return this.equalTo(true);
359};
360
361
362/**
363 * Asserts that the value managed by this assertion is strictly false.
364 * @return {webdriver.promise.Promise} The assertion result.
365 */
366webdriver.testing.Assertion.prototype.isFalse = function() {
367 return this.equalTo(false);
368};
369
370
371
372/**
373 * An assertion that negates any applied matchers.
374 * @param {*} value The value to perform assertions on.
375 * @constructor
376 * @extends {webdriver.testing.Assertion}
377 */
378webdriver.testing.NegatedAssertion = function(value) {
379 webdriver.testing.NegatedAssertion.base(this, 'constructor', value);
380 this.value = value;
381};
382goog.inherits(
383 webdriver.testing.NegatedAssertion, webdriver.testing.Assertion);
384
385
386/** @override */
387webdriver.testing.NegatedAssertion.prototype.apply = function(
388 matcher, opt_message) {
389 matcher = new goog.labs.testing.IsNotMatcher(matcher);
390 return webdriver.testing.NegatedAssertion.base(this, 'apply', matcher,
391 opt_message);
392};
393
394/**
395 * Creates a new assertion.
396 * @param {*} value The value to perform an assertion on.
397 * @return {!webdriver.testing.Assertion} The new assertion.
398 */
399webdriver.testing.assert = function(value) {
400 return new webdriver.testing.Assertion(value);
401};
402
403
404/**
405 * Registers a new assertion to expose from the
406 * {@link webdriver.testing.Assertion} prototype.
407 * @param {string} name The assertion name.
408 * @param {(function(new: goog.labs.testing.Matcher, *)|
409 * {matches: function(*): boolean,
410 * describe: function(): string})} matcherTemplate Either the
411 * matcher constructor to use, or an object literal defining a matcher.
412 */
413webdriver.testing.assert.register = function(name, matcherTemplate) {
414 webdriver.testing.Assertion.prototype[name] = function(value, opt_message) {
415 var matcher;
416 if (goog.isFunction(matcherTemplate)) {
417 var ctor = /** @type {function(new: goog.labs.testing.Matcher, *)} */ (
418 matcherTemplate);
419 matcher = new ctor(value);
420 } else {
421 matcher = new webdriver.testing.Assertion.DelegatingMatcher_(value);
422 }
423 return this.apply(matcher, opt_message);
424 };
425};
426
427
428/**
429 * Asserts that a matcher accepts a given value. This function has two
430 * signatures based on the number of arguments:
431 *
432 * Two arguments:
433 * assertThat(actualValue, matcher)
434 * Three arguments:
435 * assertThat(failureMessage, actualValue, matcher)
436 *
437 * @param {*} failureMessageOrActualValue Either a failure message or the value
438 * to apply to the given matcher.
439 * @param {*} actualValueOrMatcher Either the value to apply to the given
440 * matcher, or the matcher itself.
441 * @param {goog.labs.testing.Matcher=} opt_matcher The matcher to use;
442 * ignored unless this function is invoked with three arguments.
443 * @return {!webdriver.promise.Promise} The assertion result.
444 * @deprecated Use webdriver.testing.asserts.assert instead.
445 */
446webdriver.testing.asserts.assertThat = function(
447 failureMessageOrActualValue, actualValueOrMatcher, opt_matcher) {
448 var args = goog.array.slice(arguments, 0);
449
450 var message = args.length > 2 ? args.shift() : '';
451 if (message) message += '\n';
452
453 var actualValue = args.shift();
454 var matcher = args.shift();
455
456 return webdriver.promise.when(actualValue, function(value) {
457 goog.labs.testing.assertThat(value, matcher, message);
458 });
459};
460
461
462/**
463 * Creates an equality matcher.
464 * @param {*} expected The expected value.
465 * @return {!goog.labs.testing.Matcher} The new matcher.
466 */
467webdriver.testing.asserts.equalTo = function(expected) {
468 if (goog.isString(expected)) {
469 return new goog.labs.testing.EqualsMatcher(expected);
470 } else if (goog.isNumber(expected)) {
471 return new goog.labs.testing.EqualToMatcher(expected);
472 } else {
473 return new goog.labs.testing.ObjectEqualsMatcher(
474 /** @type {!Object} */ (expected));
475 }
476};
477
478
479goog.exportSymbol('assertThat', webdriver.testing.asserts.assertThat);
480// Mappings for goog.labs.testing matcher functions to the legacy
481// webdriver.testing.asserts matchers.
482goog.exportSymbol('contains', containsString);
483goog.exportSymbol('equalTo', webdriver.testing.asserts.equalTo);
484goog.exportSymbol('equals', webdriver.testing.asserts.equalTo);
485goog.exportSymbol('is', webdriver.testing.asserts.equalTo);
486goog.exportSymbol('not', isNot);
487goog.exportSymbol('or', anyOf);