Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/asserts/asserts.js
4010 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Utilities to check the preconditions, postconditions and
9
* invariants runtime.
10
*
11
* Methods in this package are given special treatment by the compiler
12
* for type-inference. For example, <code>goog.asserts.assert(foo)</code>
13
* will make the compiler treat <code>foo</code> as non-nullable. Similarly,
14
* <code>goog.asserts.assertNumber(foo)</code> informs the compiler about the
15
* type of <code>foo</code>. Where applicable, such assertions are preferable to
16
* casts by jsdoc with <code>@type</code>.
17
*
18
* The compiler has an option to disable asserts. So code like:
19
* <code>
20
* var x = goog.asserts.assert(foo());
21
* goog.asserts.assert(bar());
22
* </code>
23
* will be transformed into:
24
* <code>
25
* var x = foo();
26
* </code>
27
* The compiler will leave in foo() (because its return value is used),
28
* but it will remove bar() because it assumes it does not have side-effects.
29
*
30
* Additionally, note the compiler will consider the type to be "tightened" for
31
* all statements <em>after</em> the assertion. For example:
32
* <code>
33
* const /** ?Object &#ast;/ value = foo();
34
* goog.asserts.assert(value);
35
* // "value" is of type {!Object} at this point.
36
* </code>
37
*/
38
39
goog.module('goog.asserts');
40
goog.module.declareLegacyNamespace();
41
42
const DebugError = goog.require('goog.debug.Error');
43
const NodeType = goog.require('goog.dom.NodeType');
44
const utils = goog.require('goog.utils');
45
46
47
// NOTE: this needs to be exported directly and referenced via the exports
48
// object because unit tests stub it out.
49
/**
50
* @define {boolean} Whether to strip out asserts or to leave them in.
51
*/
52
exports.ENABLE_ASSERTS = goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG);
53
54
55
56
/**
57
* Error object for failed assertions.
58
* @param {string} messagePattern The pattern that was used to form message.
59
* @param {!Array<*>} messageArgs The items to substitute into the pattern.
60
* @constructor
61
* @extends {DebugError}
62
* @final
63
*/
64
function AssertionError(messagePattern, messageArgs) {
65
DebugError.call(this, subs(messagePattern, messageArgs));
66
67
/**
68
* The message pattern used to format the error message. Error handlers can
69
* use this to uniquely identify the assertion.
70
* @type {string}
71
*/
72
this.messagePattern = messagePattern;
73
}
74
utils.inherits(AssertionError, DebugError);
75
exports.AssertionError = AssertionError;
76
77
/** @override @type {string} */
78
AssertionError.prototype.name = 'AssertionError';
79
80
81
/**
82
* The default error handler.
83
* @param {!AssertionError} e The exception to be handled.
84
* @return {void}
85
*/
86
exports.DEFAULT_ERROR_HANDLER = function(e) {
87
throw e;
88
};
89
90
91
/**
92
* The handler responsible for throwing or logging assertion errors.
93
* @type {function(!AssertionError)}
94
*/
95
let errorHandler_ = exports.DEFAULT_ERROR_HANDLER;
96
97
98
/**
99
* Does simple python-style string substitution.
100
* subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog".
101
* @param {string} pattern The string containing the pattern.
102
* @param {!Array<*>} subs The items to substitute into the pattern.
103
* @return {string} A copy of `str` in which each occurrence of
104
* `%s` has been replaced an argument from `var_args`.
105
*/
106
function subs(pattern, subs) {
107
const splitParts = pattern.split('%s');
108
let returnString = '';
109
110
// Replace up to the last split part. We are inserting in the
111
// positions between split parts.
112
const subLast = splitParts.length - 1;
113
for (let i = 0; i < subLast; i++) {
114
// keep unsupplied as '%s'
115
const sub = (i < subs.length) ? subs[i] : '%s';
116
returnString += splitParts[i] + sub;
117
}
118
return returnString + splitParts[subLast];
119
}
120
121
122
/**
123
* Throws an exception with the given message and "Assertion failed" prefixed
124
* onto it.
125
* @param {string} defaultMessage The message to use if givenMessage is empty.
126
* @param {?Array<*>} defaultArgs The substitution arguments for defaultMessage.
127
* @param {string|undefined} givenMessage Message supplied by the caller.
128
* @param {!Array<*>} givenArgs The substitution arguments for givenMessage.
129
* @throws {AssertionError} When the value is not a number.
130
*/
131
function doAssertFailure(defaultMessage, defaultArgs, givenMessage, givenArgs) {
132
let message = 'Assertion failed';
133
let args;
134
if (givenMessage) {
135
message += ': ' + givenMessage;
136
args = givenArgs;
137
} else if (defaultMessage) {
138
message += ': ' + defaultMessage;
139
args = defaultArgs;
140
}
141
// The '' + works around an Opera 10 bug in the unit tests. Without it,
142
// a stack trace is added to var message above. With this, a stack trace is
143
// not added until this line (it causes the extra garbage to be added after
144
// the assertion message instead of in the middle of it).
145
const e = new AssertionError('' + message, args || []);
146
errorHandler_(e);
147
}
148
149
150
/**
151
* Sets a custom error handler that can be used to customize the behavior of
152
* assertion failures, for example by turning all assertion failures into log
153
* messages.
154
* @param {function(!AssertionError)} errorHandler
155
* @return {void}
156
*/
157
exports.setErrorHandler = function(errorHandler) {
158
if (exports.ENABLE_ASSERTS) {
159
errorHandler_ = errorHandler;
160
}
161
};
162
163
164
/**
165
* Checks if the condition evaluates to true if ENABLE_ASSERTS is
166
* true.
167
* @template T
168
* @param {T} condition The condition to check.
169
* @param {string=} opt_message Error message in case of failure.
170
* @param {...*} var_args The items to substitute into the failure message.
171
* @return {T} The value of the condition.
172
* @throws {AssertionError} When the condition evaluates to false.
173
* @closurePrimitive {asserts.truthy}
174
*/
175
exports.assert = function(condition, opt_message, var_args) {
176
if (exports.ENABLE_ASSERTS && !condition) {
177
doAssertFailure(
178
'', null, opt_message, Array.prototype.slice.call(arguments, 2));
179
}
180
return condition;
181
};
182
183
184
/**
185
* Checks if `value` is `null` or `undefined` if goog.asserts.ENABLE_ASSERTS is
186
* true.
187
*
188
* @param {T} value The value to check.
189
* @param {string=} opt_message Error message in case of failure.
190
* @param {...*} var_args The items to substitute into the failure message.
191
* @return {R} `value` with its type narrowed to exclude `null` and `undefined`.
192
*
193
* @template T
194
* @template R :=
195
* mapunion(T, (V) =>
196
* cond(eq(V, 'null'),
197
* none(),
198
* cond(eq(V, 'undefined'),
199
* none(),
200
* V)))
201
* =:
202
*
203
* @throws {!AssertionError} When `value` is `null` or `undefined`.
204
* @closurePrimitive {asserts.matchesReturn}
205
*/
206
exports.assertExists = function(value, opt_message, var_args) {
207
if (exports.ENABLE_ASSERTS && value == null) {
208
doAssertFailure(
209
'Expected to exist: %s.', [value], opt_message,
210
Array.prototype.slice.call(arguments, 2));
211
}
212
return value;
213
};
214
215
216
/**
217
* Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case
218
* when we want to add a check in the unreachable area like switch-case
219
* statement:
220
*
221
* <pre>
222
* switch(type) {
223
* case FOO: doSomething(); break;
224
* case BAR: doSomethingElse(); break;
225
* default: goog.asserts.fail('Unrecognized type: ' + type);
226
* // We have only 2 types - "default:" section is unreachable code.
227
* }
228
* </pre>
229
*
230
* @param {string=} opt_message Error message in case of failure.
231
* @param {...*} var_args The items to substitute into the failure message.
232
* @return {void}
233
* @throws {AssertionError} Failure.
234
* @closurePrimitive {asserts.fail}
235
*/
236
exports.fail = function(opt_message, var_args) {
237
if (exports.ENABLE_ASSERTS) {
238
errorHandler_(new AssertionError(
239
'Failure' + (opt_message ? ': ' + opt_message : ''),
240
Array.prototype.slice.call(arguments, 1)));
241
}
242
};
243
244
245
/**
246
* Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true.
247
* @param {*} value The value to check.
248
* @param {string=} opt_message Error message in case of failure.
249
* @param {...*} var_args The items to substitute into the failure message.
250
* @return {number} The value, guaranteed to be a number when asserts enabled.
251
* @throws {AssertionError} When the value is not a number.
252
* @closurePrimitive {asserts.matchesReturn}
253
*/
254
exports.assertNumber = function(value, opt_message, var_args) {
255
if (exports.ENABLE_ASSERTS && typeof value !== 'number') {
256
doAssertFailure(
257
'Expected number but got %s: %s.', [utils.typeOf(value), value],
258
opt_message, Array.prototype.slice.call(arguments, 2));
259
}
260
return /** @type {number} */ (value);
261
};
262
263
264
/**
265
* Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true.
266
* @param {*} value The value to check.
267
* @param {string=} opt_message Error message in case of failure.
268
* @param {...*} var_args The items to substitute into the failure message.
269
* @return {string} The value, guaranteed to be a string when asserts enabled.
270
* @throws {AssertionError} When the value is not a string.
271
* @closurePrimitive {asserts.matchesReturn}
272
*/
273
exports.assertString = function(value, opt_message, var_args) {
274
if (exports.ENABLE_ASSERTS && typeof value !== 'string') {
275
doAssertFailure(
276
'Expected string but got %s: %s.', [utils.typeOf(value), value],
277
opt_message, Array.prototype.slice.call(arguments, 2));
278
}
279
return /** @type {string} */ (value);
280
};
281
282
283
/**
284
* Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true.
285
* @param {*} value The value to check.
286
* @param {string=} opt_message Error message in case of failure.
287
* @param {...*} var_args The items to substitute into the failure message.
288
* @return {!Function} The value, guaranteed to be a function when asserts
289
* enabled.
290
* @throws {AssertionError} When the value is not a function.
291
* @closurePrimitive {asserts.matchesReturn}
292
*/
293
exports.assertFunction = function(value, opt_message, var_args) {
294
if (exports.ENABLE_ASSERTS && typeof value !== 'function') {
295
doAssertFailure(
296
'Expected function but got %s: %s.', [utils.typeOf(value), value],
297
opt_message, Array.prototype.slice.call(arguments, 2));
298
}
299
return /** @type {!Function} */ (value);
300
};
301
302
303
/**
304
* Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true.
305
* @param {*} value The value to check.
306
* @param {string=} opt_message Error message in case of failure.
307
* @param {...*} var_args The items to substitute into the failure message.
308
* @return {!Object} The value, guaranteed to be a non-null object.
309
* @throws {AssertionError} When the value is not an object.
310
* @closurePrimitive {asserts.matchesReturn}
311
*/
312
exports.assertObject = function(value, opt_message, var_args) {
313
if (exports.ENABLE_ASSERTS && !utils.isObject(value)) {
314
doAssertFailure(
315
'Expected object but got %s: %s.', [utils.typeOf(value), value],
316
opt_message, Array.prototype.slice.call(arguments, 2));
317
}
318
return /** @type {!Object} */ (value);
319
};
320
321
322
/**
323
* Checks if the value is an Array if ENABLE_ASSERTS is true.
324
* @param {*} value The value to check.
325
* @param {string=} opt_message Error message in case of failure.
326
* @param {...*} var_args The items to substitute into the failure message.
327
* @return {!Array<?>} The value, guaranteed to be a non-null array.
328
* @throws {AssertionError} When the value is not an array.
329
* @closurePrimitive {asserts.matchesReturn}
330
*/
331
exports.assertArray = function(value, opt_message, var_args) {
332
if (exports.ENABLE_ASSERTS && !Array.isArray(value)) {
333
doAssertFailure(
334
'Expected array but got %s: %s.', [utils.typeOf(value), value],
335
opt_message, Array.prototype.slice.call(arguments, 2));
336
}
337
return /** @type {!Array<?>} */ (value);
338
};
339
340
341
/**
342
* Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true.
343
* @param {*} value The value to check.
344
* @param {string=} opt_message Error message in case of failure.
345
* @param {...*} var_args The items to substitute into the failure message.
346
* @return {boolean} The value, guaranteed to be a boolean when asserts are
347
* enabled.
348
* @throws {AssertionError} When the value is not a boolean.
349
* @closurePrimitive {asserts.matchesReturn}
350
*/
351
exports.assertBoolean = function(value, opt_message, var_args) {
352
if (exports.ENABLE_ASSERTS && typeof value !== 'boolean') {
353
doAssertFailure(
354
'Expected boolean but got %s: %s.', [utils.typeOf(value), value],
355
opt_message, Array.prototype.slice.call(arguments, 2));
356
}
357
return /** @type {boolean} */ (value);
358
};
359
360
361
/**
362
* Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true.
363
* @param {*} value The value to check.
364
* @param {string=} opt_message Error message in case of failure.
365
* @param {...*} var_args The items to substitute into the failure message.
366
* @return {!Element} The value, likely to be a DOM Element when asserts are
367
* enabled.
368
* @throws {AssertionError} When the value is not an Element.
369
* @closurePrimitive {asserts.matchesReturn}
370
* @deprecated Use goog.asserts.dom.assertIsElement instead.
371
*/
372
exports.assertElement = function(value, opt_message, var_args) {
373
if (exports.ENABLE_ASSERTS &&
374
(!utils.isObject(value) ||
375
/** @type {!Node} */ (value).nodeType != NodeType.ELEMENT)) {
376
doAssertFailure(
377
'Expected Element but got %s: %s.', [utils.typeOf(value), value],
378
opt_message, Array.prototype.slice.call(arguments, 2));
379
}
380
return /** @type {!Element} */ (value);
381
};
382
383
384
/**
385
* Checks if the value is an instance of the user-defined type if
386
* goog.asserts.ENABLE_ASSERTS is true.
387
*
388
* The compiler may tighten the type returned by this function.
389
*
390
* Do not use this to ensure a value is an HTMLElement or a subclass! Cross-
391
* document DOM inherits from separate - though identical - browser classes, and
392
* such a check will unexpectedly fail. Please use the methods in
393
* goog.asserts.dom for these purposes.
394
*
395
* @param {?} value The value to check.
396
* @param {function(new: T, ...)} type A user-defined constructor.
397
* @param {string=} opt_message Error message in case of failure.
398
* @param {...*} var_args The items to substitute into the failure message.
399
* @throws {AssertionError} When the value is not an instance of
400
* type.
401
* @return {T}
402
* @template T
403
* @closurePrimitive {asserts.matchesReturn}
404
*/
405
exports.assertInstanceof = function(value, type, opt_message, var_args) {
406
if (exports.ENABLE_ASSERTS && !(value instanceof type)) {
407
doAssertFailure(
408
'Expected instanceof %s but got %s.', [getType(type), getType(value)],
409
opt_message, Array.prototype.slice.call(arguments, 3));
410
}
411
return value;
412
};
413
414
415
/**
416
* Checks whether the value is a finite number, if ENABLE_ASSERTS
417
* is true.
418
*
419
* @param {*} value The value to check.
420
* @param {string=} opt_message Error message in case of failure.
421
* @param {...*} var_args The items to substitute into the failure message.
422
* @throws {AssertionError} When the value is not a number, or is
423
* a non-finite number such as NaN, Infinity or -Infinity.
424
* @return {number} The value initially passed in.
425
*/
426
exports.assertFinite = function(value, opt_message, var_args) {
427
if (exports.ENABLE_ASSERTS &&
428
(typeof value != 'number' || !isFinite(value))) {
429
doAssertFailure(
430
'Expected %s to be a finite number but it is not.', [value],
431
opt_message, Array.prototype.slice.call(arguments, 2));
432
}
433
return /** @type {number} */ (value);
434
};
435
436
/**
437
* Returns the type of a value. If a constructor is passed, and a suitable
438
* string cannot be found, 'unknown type name' will be returned.
439
* @param {*} value A constructor, object, or primitive.
440
* @return {string} The best display name for the value, or 'unknown type name'.
441
*/
442
function getType(value) {
443
if (value instanceof Function) {
444
return value.displayName || value.name || 'unknown type name';
445
} else if (value instanceof Object) {
446
return /** @type {string} */ (value.constructor.displayName) ||
447
value.constructor.name || Object.prototype.toString.call(value);
448
} else {
449
return value === null ? 'null' : typeof value;
450
}
451
}
452
453