Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/testing/testcase.js
4501 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview A class representing a set of test functions to be run.
9
*
10
* Testing code should not have dependencies outside of goog.testing so as to
11
* reduce the chance of masking missing dependencies.
12
*
13
* This file does not compile correctly with --collapse_properties. Use
14
* --property_renaming=ALL_UNQUOTED instead.
15
*/
16
17
goog.setTestOnly('goog.testing.TestCase');
18
goog.provide('goog.testing.TestCase');
19
goog.provide('goog.testing.TestCase.Error');
20
goog.provide('goog.testing.TestCase.Order');
21
goog.provide('goog.testing.TestCase.Result');
22
goog.provide('goog.testing.TestCase.Test');
23
24
25
goog.require('goog.Promise');
26
goog.require('goog.Thenable');
27
goog.require('goog.array');
28
goog.require('goog.asserts');
29
goog.require('goog.debug');
30
goog.require('goog.dom');
31
goog.require('goog.dom.TagName');
32
goog.require('goog.object');
33
goog.require('goog.testing.CspViolationObserver');
34
goog.require('goog.testing.JsUnitException');
35
goog.require('goog.url');
36
37
38
39
/**
40
* A class representing a JsUnit test case. A TestCase is made up of a number
41
* of test functions which can be run. Individual test cases can override the
42
* following functions to set up their test environment:
43
* - runTests - completely override the test's runner
44
* - setUpPage - called before any of the test functions are run
45
* - tearDownPage - called after all tests are finished
46
* - setUp - called before each of the test functions
47
* - tearDown - called after each of the test functions
48
* - shouldRunTests - called before a test run, all tests are skipped if it
49
* returns false. Can be used to disable tests on browsers
50
* where they aren't expected to pass.
51
* <p>
52
* TestCase objects are usually constructed by inspecting the global environment
53
* to discover functions that begin with the prefix <code>test</code>.
54
* (See {@link #autoDiscoverLifecycle} and {@link #autoDiscoverTests}.)
55
* </p>
56
*
57
* <h2>Testing asychronous code with promises</h2>
58
*
59
* <p>
60
* In the simplest cases, the behavior that the developer wants to test
61
* is synchronous, and the test functions exercising the behavior execute
62
* synchronously. But TestCase can also be used to exercise asynchronous code
63
* through the use of <a
64
* href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">
65
* promises</a>. If a test function returns an object that has a
66
* <code>then</code> method defined on it, the test framework switches to an
67
* asynchronous execution strategy: the next test function will not begin
68
* execution until the returned promise is resolved or rejected. Instead of
69
* writing test assertions at the top level inside a test function, the test
70
* author chains them on the end of the returned promise. For example:
71
* </p>
72
* <pre>
73
* function testPromiseBasedAPI() {
74
* return promiseBasedAPI().then(function(value) {
75
* // Will run when the promise resolves, and before the next
76
* // test function begins execution.
77
* assertEquals('foo', value.bar);
78
* });
79
* }
80
* </pre>
81
* <p>
82
* Synchronous and asynchronous tests can be mixed in the same TestCase.
83
* Test functions that return an object with a <code>then</code> method are
84
* executed asynchronously, and all other test functions are executed
85
* synchronously. While this is convenient for test authors (since it doesn't
86
* require any explicit configuration for asynchronous tests), it can lead to
87
* confusion if the test author forgets to return the promise from the test
88
* function. For example:
89
* </p>
90
* <pre>
91
* function testPromiseBasedAPI() {
92
* // This test should never succeed.
93
* promiseBasedAPI().then(fail, fail);
94
* // Oops! The promise isn't returned to the framework,
95
* // so this test actually does succeed.
96
* }
97
* </pre>
98
* <p>
99
* Since the test framework knows nothing about the promise created
100
* in the test function, it will run the function synchronously, record
101
* a success, and proceed immediately to the next test function.
102
* </p>
103
* <p>
104
* Promises returned from test functions can time out. If a returned promise
105
* is not resolved or rejected within {@link promiseTimeout} milliseconds,
106
* the test framework rejects the promise without a timeout error message.
107
* Test cases can configure the value of `promiseTimeout` by setting
108
* <pre>
109
* goog.testing.TestCase.getActiveTestCase().promiseTimeout = ...
110
* </pre>
111
* in their `setUpPage` methods.
112
* </p>
113
*
114
* @param {string=} opt_name The name of the test case, defaults to
115
* 'Untitled Test Case'.
116
* @constructor
117
*/
118
goog.testing.TestCase = function(opt_name) {
119
'use strict';
120
/**
121
* A name for the test case.
122
* @type {string}
123
* @private
124
*/
125
this.name_ = opt_name || 'Untitled Test Case';
126
127
/**
128
* If the test should be auto discovered via {@link #autoDiscoverTests} when
129
* test case is initialized.
130
* @type {boolean}
131
* @private
132
*/
133
this.shouldAutoDiscoverTests_ = true;
134
135
/**
136
* Array of test functions that can be executed.
137
* @type {!Array<!goog.testing.TestCase.Test>}
138
* @private
139
*/
140
this.tests_ = [];
141
142
/**
143
* Set of test names and/or indices to execute, or null if all tests should
144
* be executed.
145
*
146
* Indices are included to allow automation tools to run a subset of the
147
* tests without knowing the exact contents of the test file.
148
*
149
* Indices should only be used with SORTED ordering.
150
*
151
* Example valid values:
152
* <ul>
153
* <li>[testName]
154
* <li>[testName1, testName2]
155
* <li>[2] - will run the 3rd test in the order specified
156
* <li>[1,3,5]
157
* <li>[testName1, testName2, 3, 5] - will work
158
* <ul>
159
* @type {?Object}
160
* @private
161
*/
162
this.testsToRun_ = null;
163
164
/**
165
* A call back for each test.
166
* @private {?function(?goog.testing.TestCase.Test, !Array<string>)}
167
*/
168
this.testDone_ = null;
169
170
/**
171
* The order to run the auto-discovered tests in.
172
* @type {string}
173
*/
174
this.order = goog.testing.TestCase.Order.SORTED;
175
176
/** @private {function(!goog.testing.TestCase.Result)} */
177
this.runNextTestCallback_ = () => {};
178
179
/**
180
* The currently executing test case or null.
181
* @private {?goog.testing.TestCase.Test}
182
*/
183
this.curTest_ = null;
184
185
/**
186
* Object used to encapsulate the test results.
187
* @type {!goog.testing.TestCase.Result}
188
* @protected
189
* @suppress {underscore|visibility}
190
*/
191
this.result_ = new goog.testing.TestCase.Result(this);
192
193
/**
194
* An array of exceptions generated by `assert` statements.
195
* @private {!Array<!goog.testing.JsUnitException>}
196
*/
197
this.thrownAssertionExceptions_ = [];
198
199
/**
200
* The maximum time in milliseconds a promise returned from a test function
201
* may remain pending before the test fails due to timeout.
202
* @type {number}
203
*/
204
this.promiseTimeout = 1000; // 1s
205
206
/**
207
* Callbacks that will be executed when the test has finalized.
208
* @private {!Array<function()>}
209
*/
210
this.onCompletedCallbacks_ = [];
211
212
/** @type {number|undefined} */
213
this.endTime_;
214
215
/** @private {number} */
216
this.testsRanSoFar_ = 0;
217
218
/** @private {!goog.testing.CspViolationObserver} */
219
this.cspViolationObserver_ = new goog.testing.CspViolationObserver();
220
221
/** @private {boolean} */
222
this.ignoreStartupCspViolations_ = false;
223
};
224
225
226
/**
227
* The order to run the auto-discovered tests.
228
* @enum {string}
229
*/
230
goog.testing.TestCase.Order = {
231
/**
232
* This is browser dependent and known to be different in FF and Safari
233
* compared to others.
234
*/
235
NATURAL: 'natural',
236
237
/** Random order. */
238
RANDOM: 'random',
239
240
/** Sorted based on the name. */
241
SORTED: 'sorted'
242
};
243
244
245
/**
246
* @return {string} The name of the test.
247
*/
248
goog.testing.TestCase.prototype.getName = function() {
249
'use strict';
250
return this.name_;
251
};
252
253
/**
254
* Returns the current test or null.
255
* @return {?goog.testing.TestCase.Test}
256
* @protected
257
*/
258
goog.testing.TestCase.prototype.getCurrentTest = function() {
259
'use strict';
260
return this.curTest_;
261
};
262
263
/**
264
* The maximum amount of time in milliseconds that the test case can take
265
* before it is forced to yield and reschedule. This prevents the test runner
266
* from blocking the browser and potentially hurting the test harness.
267
* @type {number}
268
*/
269
goog.testing.TestCase.maxRunTime = 200;
270
271
272
/**
273
* Save a reference to `window.setTimeout`, so any code that overrides the
274
* default behavior (the MockClock, for example) doesn't affect our runner.
275
* @type {function((Function|string), number=, *=): number}
276
* @private
277
*/
278
goog.testing.TestCase.protectedSetTimeout_ = goog.global.setTimeout;
279
280
281
/**
282
* Save a reference to `window.clearTimeout`, so any code that overrides
283
* the default behavior (e.g. MockClock) doesn't affect our runner.
284
* @type {function((null|number|undefined)): void}
285
* @private
286
*/
287
goog.testing.TestCase.protectedClearTimeout_ = goog.global.clearTimeout;
288
289
290
/**
291
* Save a reference to `window.Date`, so any code that overrides
292
* the default behavior doesn't affect our runner.
293
* @type {function(new: Date)}
294
* @private
295
*/
296
goog.testing.TestCase.protectedDate_ = Date;
297
298
/**
299
* Save a reference to `window.performance`, so any code that overrides
300
* the default behavior doesn't affect our runner.
301
* @type {?Performance}
302
* @private
303
*/
304
goog.testing.TestCase.protectedPerformance_ = typeof window !== 'undefined' &&
305
window.performance && window.performance.now ?
306
performance :
307
null;
308
309
310
/**
311
* Name of the current test that is running, or null if none is running.
312
* @type {?string}
313
*/
314
goog.testing.TestCase.currentTestName = null;
315
316
317
/**
318
* Avoid a dependency on goog.userAgent and keep our own reference of whether
319
* the browser is IE.
320
* @type {boolean}
321
*/
322
goog.testing.TestCase.IS_IE = typeof opera == 'undefined' &&
323
!!goog.global.navigator &&
324
goog.global.navigator.userAgent.indexOf('MSIE') != -1;
325
326
327
/**
328
* Exception object that was detected before a test runs.
329
* @type {*}
330
* @protected
331
*/
332
goog.testing.TestCase.prototype.exceptionBeforeTest;
333
334
335
/**
336
* Whether the test case has ever tried to execute.
337
* @type {boolean}
338
*/
339
goog.testing.TestCase.prototype.started = false;
340
341
342
/**
343
* Whether the test case is running.
344
* @type {boolean}
345
*/
346
goog.testing.TestCase.prototype.running = false;
347
348
349
/**
350
* Timestamp for when the test was started.
351
* @type {number}
352
* @private
353
*/
354
goog.testing.TestCase.prototype.startTime_ = 0;
355
356
357
/**
358
* Time since the last batch of tests was started, if batchTime exceeds
359
* {@link #maxRunTime} a timeout will be used to stop the tests blocking the
360
* browser and a new batch will be started.
361
* @type {number}
362
* @private
363
*/
364
goog.testing.TestCase.prototype.batchTime_ = 0;
365
366
367
/**
368
* Pointer to the current test.
369
* @type {number}
370
* @private
371
*/
372
goog.testing.TestCase.prototype.currentTestPointer_ = 0;
373
374
375
/**
376
* Whether to use Native Promises or goog.Promise.
377
* @type {boolean}
378
* @private
379
*/
380
goog.testing.TestCase.prototype.useNativePromise_ = false;
381
382
383
/**
384
* Adds a new test to the test case.
385
* @param {!goog.testing.TestCase.Test} test The test to add.
386
*/
387
goog.testing.TestCase.prototype.add = function(test) {
388
'use strict';
389
goog.asserts.assert(test);
390
if (this.started) {
391
throw new Error(
392
'Tests cannot be added after execute() has been called. ' +
393
'Test: ' + test.name);
394
}
395
396
this.tests_.push(test);
397
};
398
399
400
/**
401
* Creates and adds a new test.
402
*
403
* Convenience function to make syntax less awkward when not using automatic
404
* test discovery.
405
*
406
* @param {string} name The test name.
407
* @param {function()} ref Reference to the test function.
408
* @param {!Object=} scope Optional scope that the test function should be
409
* called in.
410
* @param {!Array<!Object>=} objChain An array of Objects that may have
411
* additional set up/tear down logic for a particular test.
412
*/
413
goog.testing.TestCase.prototype.addNewTest = function(
414
name, ref, scope, objChain) {
415
'use strict';
416
this.add(this.createTest(name, ref, scope || this, objChain));
417
};
418
419
420
/**
421
* Sets the tests.
422
* @param {!Array<goog.testing.TestCase.Test>} tests A new test array.
423
* @protected
424
*/
425
goog.testing.TestCase.prototype.setTests = function(tests) {
426
'use strict';
427
this.tests_ = tests;
428
};
429
430
431
/**
432
* Gets the tests.
433
* @return {!Array<goog.testing.TestCase.Test>} The test array.
434
*/
435
goog.testing.TestCase.prototype.getTests = function() {
436
'use strict';
437
return this.tests_;
438
};
439
440
441
/**
442
* Returns the number of tests contained in the test case.
443
* @return {number} The number of tests.
444
*/
445
goog.testing.TestCase.prototype.getCount = function() {
446
'use strict';
447
return this.tests_.length;
448
};
449
450
451
/**
452
* Returns the number of tests actually run in the test case, i.e. subtracting
453
* any which are skipped.
454
* @return {number} The number of un-ignored tests.
455
*/
456
goog.testing.TestCase.prototype.getActuallyRunCount = function() {
457
'use strict';
458
return this.testsToRun_ ? goog.object.getCount(this.testsToRun_) : 0;
459
};
460
461
462
/**
463
* Returns the current test and increments the pointer.
464
* @return {goog.testing.TestCase.Test} The current test case.
465
*/
466
goog.testing.TestCase.prototype.next = function() {
467
'use strict';
468
var test;
469
while ((test = this.tests_[this.currentTestPointer_++])) {
470
if (!this.testsToRun_ || this.testsToRun_[test.name] ||
471
this.testsToRun_[this.currentTestPointer_ - 1]) {
472
return test;
473
}
474
}
475
return null;
476
};
477
478
479
/**
480
* Resets the test case pointer, so that next returns the first test.
481
*/
482
goog.testing.TestCase.prototype.reset = function() {
483
'use strict';
484
this.currentTestPointer_ = 0;
485
this.result_ = new goog.testing.TestCase.Result(this);
486
};
487
488
489
/**
490
* Adds a callback function that should be executed when the tests have
491
* completed.
492
* @param {function()} fn The callback function.
493
*/
494
goog.testing.TestCase.prototype.addCompletedCallback = function(fn) {
495
'use strict';
496
this.onCompletedCallbacks_.push(fn);
497
};
498
499
500
/**
501
* @param {goog.testing.TestCase.Order} order The sort order for running tests.
502
*/
503
goog.testing.TestCase.prototype.setOrder = function(order) {
504
'use strict';
505
this.order = order;
506
};
507
508
509
/**
510
* @param {Object<string, boolean>} testsToRun Set of tests to run. Entries in
511
* the set may be test names, like "testFoo", or numeric indices. Only
512
* tests identified by name or by index will be executed.
513
*/
514
goog.testing.TestCase.prototype.setTestsToRun = function(testsToRun) {
515
'use strict';
516
this.testsToRun_ = testsToRun;
517
};
518
519
520
/**
521
* Can be overridden in test classes to indicate whether the tests in a case
522
* should be run in that particular situation. For example, this could be used
523
* to stop tests running in a particular browser, where browser support for
524
* the class under test was absent.
525
* @return {boolean} Whether any of the tests in the case should be run.
526
*/
527
goog.testing.TestCase.prototype.shouldRunTests = function() {
528
'use strict';
529
return true;
530
};
531
532
533
/**
534
* Executes the tests, yielding asynchronously if execution time exceeds
535
* {@link maxRunTime}. There is no guarantee that the test case has finished
536
* once this method has returned. To be notified when the test case
537
* has finished, use {@link #addCompletedCallback} or
538
* {@link #runTestsReturningPromise}.
539
*/
540
goog.testing.TestCase.prototype.execute = function() {
541
'use strict';
542
if (!this.prepareForRun_()) {
543
return;
544
}
545
this.groupLogsStart();
546
this.log('Starting tests: ' + this.name_);
547
this.cycleTests();
548
};
549
550
551
/**
552
* Sets up the internal state of the test case for a run.
553
* @return {boolean} If false, preparation failed because the test case
554
* is not supposed to run in the present environment.
555
* @private
556
*/
557
goog.testing.TestCase.prototype.prepareForRun_ = function() {
558
'use strict';
559
this.started = true;
560
this.reset();
561
this.startTime_ = this.now();
562
this.running = true;
563
this.result_.totalCount = this.getCount();
564
this.cspViolationObserver_.start();
565
if (!this.shouldRunTests()) {
566
this.log('shouldRunTests() returned false, skipping these tests.');
567
this.result_.testSuppressed = true;
568
this.finalize();
569
return false;
570
}
571
this.checkCspViolations_('shouldRunTests');
572
return true;
573
};
574
575
576
/**
577
* Finalizes the test case, called when the tests have finished executing.
578
*/
579
goog.testing.TestCase.prototype.finalize = function() {
580
'use strict';
581
this.saveMessage('Done');
582
583
try {
584
this.tearDownPage();
585
} catch (e) {
586
// Report the error and continue with tests.
587
window['onerror'](e.toString(), document.location.href, 0, 0, e);
588
}
589
590
this.endTime_ = this.now();
591
this.running = false;
592
this.result_.runTime = this.endTime_ - this.startTime_;
593
this.result_.numFilesLoaded = this.countNumFilesLoaded_();
594
this.result_.complete = true;
595
this.testsRanSoFar_++;
596
597
this.log(this.result_.getSummary());
598
if (this.result_.isSuccess()) {
599
this.log('Tests complete');
600
} else {
601
this.log('Tests Failed');
602
}
603
this.onCompletedCallbacks_.forEach(function(cb) {
604
'use strict';
605
cb();
606
});
607
this.onCompletedCallbacks_ = [];
608
this.groupLogsEnd();
609
this.cspViolationObserver_.stop();
610
};
611
612
613
/**
614
* Saves a message to the result set.
615
* @param {string} message The message to save.
616
*/
617
goog.testing.TestCase.prototype.saveMessage = function(message) {
618
'use strict';
619
this.result_.messages.push(this.getTimeStamp_() + ' ' + message);
620
};
621
622
623
/**
624
* @return {boolean} Whether the test case is running inside the multi test
625
* runner.
626
*/
627
goog.testing.TestCase.prototype.isInsideMultiTestRunner = function() {
628
'use strict';
629
var top = goog.global['top'];
630
return top && typeof top['_allTests'] != 'undefined';
631
};
632
633
/**
634
* @return {boolean} Whether the test-progress should be logged to the console.
635
*/
636
goog.testing.TestCase.prototype.shouldLogTestProgress = function() {
637
'use strict';
638
return !goog.global['skipClosureTestProgress'] &&
639
!this.isInsideMultiTestRunner();
640
};
641
642
/**
643
* Logs an object to the console, if available.
644
* @param {*} val The value to log. Will be ToString'd.
645
*/
646
goog.testing.TestCase.prototype.log = function(val) {
647
'use strict';
648
if (this.shouldLogTestProgress() && goog.global.console) {
649
if (typeof val == 'string') {
650
val = this.getTimeStamp_() + ' : ' + val;
651
}
652
if (val instanceof Error && val.stack) {
653
goog.global.console.log(val.stack);
654
} else {
655
goog.global.console.log(val);
656
}
657
}
658
};
659
660
661
/**
662
* Groups the upcoming logs in the same log group
663
*/
664
goog.testing.TestCase.prototype.groupLogsStart = function() {
665
'use strict';
666
if (!this.isInsideMultiTestRunner() && goog.global.console &&
667
goog.global.console.group) {
668
goog.global.console.group(
669
'Test #' + (this.testsRanSoFar_ + 1) + ': ' + this.name_);
670
}
671
};
672
673
674
/**
675
* Closes the group of the upcoming logs
676
*/
677
goog.testing.TestCase.prototype.groupLogsEnd = function() {
678
'use strict';
679
if (!this.isInsideMultiTestRunner() && goog.global.console &&
680
goog.global.console.groupEnd) {
681
goog.global.console.groupEnd();
682
}
683
};
684
685
686
/**
687
* @return {boolean} Whether the test was a success.
688
*/
689
goog.testing.TestCase.prototype.isSuccess = function() {
690
'use strict';
691
return !!this.result_ && this.result_.isSuccess();
692
};
693
694
695
/**
696
* Returns a string detailing the results from the test.
697
* @param {boolean=} opt_verbose If true results will include data about all
698
* tests, not just what failed.
699
* @return {string} The results from the test.
700
*/
701
goog.testing.TestCase.prototype.getReport = function(opt_verbose) {
702
'use strict';
703
var rv = [];
704
705
if (this.running) {
706
rv.push(this.name_ + ' [RUNNING]');
707
} else if (this.result_.runCount == 0) {
708
rv.push(this.name_ + ' [NO TESTS RUN]');
709
} else {
710
var label = this.result_.isSuccess() ? 'PASSED' : 'FAILED';
711
rv.push(this.name_ + ' [' + label + ']');
712
}
713
714
if (goog.global.location) {
715
rv.push(this.trimPath_(goog.global.location.href));
716
}
717
718
rv.push(this.result_.getSummary());
719
720
if (opt_verbose) {
721
rv.push('.', this.result_.messages.join('\n'));
722
} else if (!this.result_.isSuccess()) {
723
rv.push(this.result_.errors.join('\n'));
724
}
725
726
rv.push(' ');
727
728
return rv.join('\n');
729
};
730
731
732
/**
733
* Returns the test results.
734
* @return {!goog.testing.TestCase.Result}
735
* @package
736
*/
737
goog.testing.TestCase.prototype.getResult = function() {
738
'use strict';
739
return this.result_;
740
};
741
742
743
/**
744
* Returns the amount of time it took for the test to run.
745
* @return {number} The run time, in milliseconds.
746
*/
747
goog.testing.TestCase.prototype.getRunTime = function() {
748
'use strict';
749
return this.result_.runTime;
750
};
751
752
753
/**
754
* Returns the number of script files that were loaded in order to run the test.
755
* @return {number} The number of script files.
756
*/
757
goog.testing.TestCase.prototype.getNumFilesLoaded = function() {
758
'use strict';
759
return this.result_.numFilesLoaded;
760
};
761
762
763
/**
764
* Represents a test result.
765
* @typedef {{
766
* 'source': string,
767
* 'message': string,
768
* 'stacktrace': string
769
* }}
770
*/
771
goog.testing.TestCase.IResult;
772
773
/**
774
* Returns the test results object: a map from test names to a list of test
775
* failures (if any exist).
776
* @return {!Object<string, !Array<goog.testing.TestCase.IResult>>} Test
777
* results object.
778
*/
779
goog.testing.TestCase.prototype.getTestResults = function() {
780
'use strict';
781
var map = {};
782
goog.object.forEach(this.result_.resultsByName, function(resultArray, key) {
783
'use strict';
784
// Make sure we only use properties on the actual map
785
if (!Object.prototype.hasOwnProperty.call(
786
this.result_.resultsByName, key)) {
787
return;
788
}
789
map[key] = [];
790
for (var j = 0; j < resultArray.length; j++) {
791
map[key].push(resultArray[j].toObject_());
792
}
793
}, this);
794
return map;
795
};
796
797
/**
798
* Executes each of the tests, yielding asynchronously if execution time
799
* exceeds {@link #maxRunTime}. There is no guarantee that the test case
800
* has finished execution once this method has returned.
801
* To be notified when the test case has finished execution, use
802
* {@link #addCompletedCallback} or {@link #runTestsReturningPromise}.
803
*
804
* Overridable by the individual test case. This allows test cases to defer
805
* when the test is actually started. If overridden, finalize must be
806
* called by the test to indicate it has finished.
807
*/
808
goog.testing.TestCase.prototype.runTests = function() {
809
'use strict';
810
goog.testing.TestCase.Continuation_.run(this.runSetUpPage_(this.execute));
811
};
812
813
/**
814
* Configures the TestCase to use native Promises when waiting for methods that
815
* return Thenables.
816
*/
817
goog.testing.TestCase.prototype.useNativePromise = function() {
818
this.useNativePromise_ = true;
819
};
820
821
/**
822
* Configures the TestCase to use goog.Promise when waiting for methods that
823
* return Thenables.
824
*/
825
goog.testing.TestCase.prototype.useGoogPromise = function() {
826
this.useNativePromise_ = false;
827
};
828
829
830
/**
831
* Executes each of the tests, returning a promise that resolves with the
832
* test results once they are done running.
833
* @return {!IThenable<!goog.testing.TestCase.Result>}
834
* @final
835
* @package
836
*/
837
goog.testing.TestCase.prototype.runTestsReturningPromise = function() {
838
'use strict';
839
/**
840
* @param {function(!goog.testing.TestCase.Result)} resolve
841
*/
842
const resolver = (resolve) => {
843
'use strict';
844
goog.testing.TestCase.Continuation_.run(this.runSetUpPage_(() => {
845
'use strict';
846
if (!this.prepareForRun_()) {
847
resolve(this.result_);
848
return;
849
}
850
this.groupLogsStart();
851
this.log('Starting tests: ' + this.name_);
852
this.saveMessage('Start');
853
this.batchTime_ = this.now();
854
this.runNextTestCallback_ = resolve;
855
goog.testing.TestCase.Continuation_.run(this.runNextTest_());
856
}));
857
};
858
if (this.useNativePromise_) {
859
return new Promise(resolver);
860
}
861
return new goog.Promise(resolver);
862
};
863
864
865
/**
866
* Runs the setUpPage methods.
867
* @param {function(this:goog.testing.TestCase)} runTestsFn Callback to invoke
868
* after setUpPage has completed.
869
* @return {?goog.testing.TestCase.Continuation_}
870
* @private
871
*/
872
goog.testing.TestCase.prototype.runSetUpPage_ = function(runTestsFn) {
873
'use strict';
874
const reports = goog.testing.CspViolationObserver.getBufferedReports();
875
876
const ret = this.invokeFunction_(this.setUpPage, runTestsFn, function(e) {
877
'use strict';
878
this.exceptionBeforeTest = e;
879
runTestsFn.call(this);
880
}, 'setUpPage');
881
882
if (!this.ignoreStartupCspViolations_ && reports.length > 0) {
883
const msg =
884
'One or more Content Security Policy violations occurred on the page ' +
885
'before the first test was run: ' +
886
goog.testing.CspViolationObserver.formatReports(reports);
887
// This CSP violation takes precedence over any pre-existing exception.
888
this.exceptionBeforeTest = msg;
889
}
890
891
return ret;
892
};
893
894
895
/**
896
* Executes the next test method synchronously or with promises, depending on
897
* the test method's return value.
898
*
899
* If the test method returns a promise, the next test method will run once
900
* the promise is resolved or rejected. If the test method does not
901
* return a promise, it is assumed to be synchronous, and execution proceeds
902
* immediately to the next test method. This means that test cases can run
903
* partially synchronously and partially asynchronously, depending on
904
* the return values of their test methods. In particular, a test case
905
* executes synchronously until the first promise is returned from a
906
* test method (or until a resource limit is reached; see
907
* {@link finishTestInvocation_}).
908
* @return {?goog.testing.TestCase.Continuation_}
909
* @private
910
*/
911
goog.testing.TestCase.prototype.runNextTest_ = function() {
912
'use strict';
913
this.curTest_ = this.next();
914
if (!this.curTest_ || !this.running) {
915
this.finalize();
916
return new goog.testing.TestCase.Continuation_(
917
goog.bind(this.runNextTestCallback_, this, this.result_));
918
}
919
920
var shouldRunTest = true;
921
try {
922
shouldRunTest = this.shouldRunTestsHelper_();
923
} catch (error) {
924
this.curTest_.name = 'shouldRunTests for ' + this.curTest_.name;
925
return new goog.testing.TestCase.Continuation_(
926
goog.bind(this.finishTestInvocation_, this, error));
927
}
928
929
if (!shouldRunTest) {
930
return new goog.testing.TestCase.Continuation_(
931
goog.bind(this.finishTestInvocation_, this));
932
}
933
934
this.cspViolationObserver_.setEnabled(true);
935
this.curTest_.started();
936
this.result_.runCount++;
937
this.log('Running test: ' + this.curTest_.name);
938
if (this.maybeFailTestEarly(this.curTest_)) {
939
return new goog.testing.TestCase.Continuation_(
940
goog.bind(this.finishTestInvocation_, this));
941
}
942
goog.testing.TestCase.currentTestName = this.curTest_.name;
943
return this.safeSetUp_();
944
};
945
946
947
/**
948
* @return {boolean}
949
* @private
950
*/
951
goog.testing.TestCase.prototype.shouldRunTestsHelper_ = function() {
952
'use strict';
953
var objChain =
954
this.curTest_.objChain.length ? this.curTest_.objChain : [this];
955
956
for (var i = 0; i < objChain.length; i++) {
957
var obj = objChain[i];
958
959
if (typeof obj.shouldRunTests !== 'function') {
960
continue;
961
}
962
963
if (typeof obj.shouldRunTests['$cachedResult'] === 'function') {
964
if (!obj.shouldRunTests['$cachedResult']()) {
965
this.result_.suppressedTests.push(this.curTest_.name);
966
return false;
967
} else {
968
continue;
969
}
970
}
971
972
var result;
973
(function() {
974
'use strict';
975
// Cache the result by storing a function. This way we only call
976
// shouldRunTests once per object in the chain. This enforces that people
977
// do not attempt to suppress some tests and not others with the same
978
// shouldRunTests function.
979
try {
980
var cached = result = obj.shouldRunTests.call(obj);
981
obj.shouldRunTests['$cachedResult'] = function() {
982
'use strict';
983
return cached;
984
};
985
} catch (error) {
986
obj.shouldRunTests['$cachedResult'] = function() {
987
'use strict';
988
throw error;
989
};
990
throw error;
991
}
992
})();
993
994
if (!result) {
995
this.result_.suppressedTests.push(this.curTest_.name);
996
return false;
997
}
998
}
999
1000
return true;
1001
};
1002
1003
/**
1004
* Runs all the setups associated with a test.
1005
* @return {?goog.testing.TestCase.Continuation_}
1006
* @private
1007
*/
1008
goog.testing.TestCase.prototype.safeSetUp_ = function() {
1009
'use strict';
1010
var setUps =
1011
this.curTest_.setUps.length ? this.curTest_.setUps.slice() : [this.setUp];
1012
return this.safeSetUpHelper_(setUps).call(this);
1013
};
1014
1015
/**
1016
* Recursively invokes setUp functions.
1017
* @param {!Array<function()>} setUps
1018
* @return {function(): ?goog.testing.TestCase.Continuation_}
1019
* @private
1020
*/
1021
goog.testing.TestCase.prototype.safeSetUpHelper_ = function(setUps) {
1022
'use strict';
1023
if (!setUps.length) {
1024
return this.safeRunTest_;
1025
}
1026
return goog.bind(
1027
this.invokeFunction_, this, setUps.shift(), this.safeSetUpHelper_(setUps),
1028
this.safeTearDown_, 'setUp');
1029
};
1030
1031
/**
1032
* Calls the given test function, handling errors appropriately.
1033
* @return {?goog.testing.TestCase.Continuation_}
1034
* @private
1035
*/
1036
goog.testing.TestCase.prototype.safeRunTest_ = function() {
1037
'use strict';
1038
return this.invokeFunction_(
1039
goog.bind(this.curTest_.ref, this.curTest_.scope), this.safeTearDown_,
1040
this.safeTearDown_, this.curTest_.name);
1041
};
1042
1043
1044
/**
1045
* Calls {@link tearDown}, handling errors appropriately.
1046
* @param {*=} opt_error Error associated with the test, if any.
1047
* @return {?goog.testing.TestCase.Continuation_}
1048
* @private
1049
*/
1050
goog.testing.TestCase.prototype.safeTearDown_ = function(opt_error) {
1051
'use strict';
1052
// If the test itself failed, report that before running any tearDown()s.
1053
if (arguments.length == 1) {
1054
this.recordError(this.curTest_.name, opt_error);
1055
}
1056
var tearDowns = this.curTest_.tearDowns.length ?
1057
this.curTest_.tearDowns.slice() :
1058
[this.tearDown];
1059
return this.safeTearDownHelper_(tearDowns).call(this);
1060
};
1061
1062
/**
1063
* Recursively invokes tearDown functions.
1064
* @param {!Array<function()>} tearDowns
1065
* @return {function(): ?goog.testing.TestCase.Continuation_}
1066
* @private
1067
*/
1068
goog.testing.TestCase.prototype.safeTearDownHelper_ = function(tearDowns) {
1069
'use strict';
1070
if (!tearDowns.length) {
1071
return this.finishTestInvocation_;
1072
}
1073
return goog.bind(
1074
this.invokeFunction_, this, tearDowns.shift(),
1075
this.safeTearDownHelper_(tearDowns), this.finishTestInvocation_,
1076
'tearDown');
1077
};
1078
1079
1080
/**
1081
* Calls the given `fn`, then calls either `onSuccess` or
1082
* `onFailure`, either synchronously or using promises, depending on
1083
* `fn`'s return value.
1084
*
1085
* If `fn` throws an exception, `onFailure` is called immediately
1086
* with the exception.
1087
*
1088
* If `fn` returns a promise, and the promise is eventually resolved,
1089
* `onSuccess` is called with no arguments. If the promise is eventually
1090
* rejected, `onFailure` is called with the rejection reason.
1091
*
1092
* Otherwise, if `fn` neither returns a promise nor throws an exception,
1093
* `onSuccess` is called immediately with no arguments.
1094
*
1095
* `fn`, `onSuccess`, and `onFailure` are all called with
1096
* the TestCase instance as the method receiver.
1097
*
1098
* @param {function()} fn The function to call.
1099
* @param {function(this:goog.testing.TestCase):
1100
* (?goog.testing.TestCase.Continuation_|undefined)} onSuccess
1101
* @param {function(this:goog.testing.TestCase, *):
1102
* (?goog.testing.TestCase.Continuation_|undefined)} onFailure
1103
* @param {string} fnName Name of the function being invoked e.g. 'setUp'.
1104
* @return {?goog.testing.TestCase.Continuation_}
1105
* @private
1106
*/
1107
goog.testing.TestCase.prototype.invokeFunction_ = function(
1108
fn, onSuccess, onFailure, fnName) {
1109
'use strict';
1110
var self = this;
1111
this.thrownAssertionExceptions_ = [];
1112
try {
1113
this.cspViolationObserver_.start();
1114
var retval = fn.call(this);
1115
if (goog.Thenable.isImplementedBy(retval) ||
1116
(retval && typeof retval['then'] === 'function')) {
1117
// Resolve Thenable into a proper Promise to avoid hard to debug
1118
// problems.
1119
let promise;
1120
if (this.useNativePromise_) {
1121
promise = Promise.resolve(retval);
1122
} else {
1123
promise = goog.Promise.resolve(retval);
1124
}
1125
promise = this.rejectIfPromiseTimesOut_(
1126
promise, self.promiseTimeout,
1127
'Timed out while waiting for a promise returned from ' + fnName +
1128
' to resolve. Set goog.testing.TestCase.getActiveTestCase()' +
1129
'.promiseTimeout to adjust the timeout.');
1130
promise.then(
1131
function() {
1132
'use strict';
1133
self.resetBatchTimeAfterPromise_();
1134
self.checkCspViolations_(fnName);
1135
if (self.thrownAssertionExceptions_.length == 0) {
1136
goog.testing.TestCase.Continuation_.run(onSuccess.call(self));
1137
} else {
1138
goog.testing.TestCase.Continuation_.run(onFailure.call(
1139
self, self.reportUnpropagatedAssertionExceptions_(fnName)));
1140
}
1141
},
1142
function(e) {
1143
'use strict';
1144
self.reportUnpropagatedAssertionExceptions_(fnName, e);
1145
self.resetBatchTimeAfterPromise_();
1146
self.checkCspViolations_(fnName);
1147
goog.testing.TestCase.Continuation_.run(onFailure.call(self, e));
1148
});
1149
return null;
1150
} else {
1151
this.checkCspViolations_(fnName);
1152
if (this.thrownAssertionExceptions_.length == 0) {
1153
return new goog.testing.TestCase.Continuation_(
1154
goog.bind(onSuccess, this));
1155
} else {
1156
return new goog.testing.TestCase.Continuation_(goog.bind(
1157
onFailure, this,
1158
this.reportUnpropagatedAssertionExceptions_(fnName)));
1159
}
1160
}
1161
} catch (e) {
1162
this.checkCspViolations_(fnName);
1163
this.reportUnpropagatedAssertionExceptions_(fnName, e);
1164
return new goog.testing.TestCase.Continuation_(
1165
goog.bind(onFailure, this, e));
1166
}
1167
};
1168
1169
1170
/**
1171
* Logs all of the exceptions generated from failing assertions, and returns a
1172
* generic exception informing the user that one or more exceptions were not
1173
* propagated, causing the test to erroneously pass.
1174
*
1175
* This is also called when a test fails so that the user sees swallowed errors.
1176
* (This can make it much easier to debug failures in callbacks in catch blocks)
1177
* If the actually-thrown error (that made the test fail) is also a JSUnit error
1178
* (which will therefore be in this array), it will be silently deduped when the
1179
* regular failure handler tries to record it again.
1180
* @param {string} testName The test function's name.
1181
* @param {*=} actualError The thrown error the made the test fail, if any
1182
* @return {!goog.testing.JsUnitException}
1183
* @private
1184
*/
1185
goog.testing.TestCase.prototype.reportUnpropagatedAssertionExceptions_ =
1186
function(testName, actualError) {
1187
'use strict';
1188
var extraExceptions = this.thrownAssertionExceptions_.slice();
1189
// If the actual error isn't a JSUnit exception, it won't be in this array.
1190
goog.array.remove(extraExceptions, actualError);
1191
var numExceptions = extraExceptions.length;
1192
if (numExceptions && actualError) {
1193
// Don't log this message if the only exception is the actual failure.
1194
var message =
1195
numExceptions + ' additional exceptions were swallowed by the test:';
1196
this.log(message);
1197
this.saveMessage(message);
1198
}
1199
1200
1201
for (var i = 0; i < numExceptions; i++) {
1202
this.recordError(testName, extraExceptions[i]);
1203
}
1204
1205
// Mark the test as failed.
1206
return new goog.testing.JsUnitException(
1207
'One or more assertions were raised but not caught by the testing ' +
1208
'framework. These assertions may have been unintentionally captured ' +
1209
'by a catch block or a thenCatch resolution of a Promise.');
1210
};
1211
1212
1213
/**
1214
* Resets the batch run timer. This should only be called after resolving a
1215
* promise since Promise.then() has an implicit yield.
1216
* @private
1217
*/
1218
goog.testing.TestCase.prototype.resetBatchTimeAfterPromise_ = function() {
1219
'use strict';
1220
this.batchTime_ = this.now();
1221
};
1222
1223
1224
/**
1225
* Finishes up bookkeeping for the current test function, and schedules
1226
* the next test function to run, either immediately or asychronously.
1227
* @param {*=} opt_error Optional error resulting from the test invocation.
1228
* @return {?goog.testing.TestCase.Continuation_}
1229
* @private
1230
*/
1231
goog.testing.TestCase.prototype.finishTestInvocation_ = function(opt_error) {
1232
'use strict';
1233
if (arguments.length == 1) {
1234
this.recordError(this.curTest_.name, opt_error);
1235
}
1236
1237
// If no errors have been recorded for the test, it is a success.
1238
if (!(this.curTest_.name in this.result_.resultsByName) ||
1239
!this.result_.resultsByName[this.curTest_.name].length) {
1240
if (this.result_.suppressedTests.indexOf(this.curTest_.name) >= 0) {
1241
this.doSkipped(this.curTest_);
1242
} else {
1243
this.doSuccess(this.curTest_);
1244
}
1245
} else {
1246
this.doError(this.curTest_);
1247
}
1248
1249
goog.testing.TestCase.currentTestName = null;
1250
1251
// If the test case has consumed too much time or stack space,
1252
// yield to avoid blocking the browser. Otherwise, proceed to the next test.
1253
if (this.now() - this.batchTime_ > goog.testing.TestCase.maxRunTime) {
1254
this.saveMessage('Breaking async');
1255
this.timeout(goog.bind(this.startNextBatch_, this), 0);
1256
return null;
1257
} else {
1258
return new goog.testing.TestCase.Continuation_(
1259
goog.bind(this.runNextTest_, this));
1260
}
1261
};
1262
1263
1264
/**
1265
* Checks if any CSP violations have been logged since
1266
* this.cspViolationObserver_.start() was called and reports them as errors.
1267
*
1268
* @param {string} name
1269
* @private
1270
*/
1271
goog.testing.TestCase.prototype.checkCspViolations_ = function(name) {
1272
const reports = this.cspViolationObserver_.stop();
1273
if (reports.length == 0) {
1274
return;
1275
}
1276
1277
const formattedReports =
1278
goog.testing.CspViolationObserver.formatReports(reports);
1279
const msg =
1280
'One or more Content Security Policy violations occurred during ' +
1281
'execution of this test: ' + formattedReports;
1282
if (this.started) {
1283
this.recordError(name, msg);
1284
} else {
1285
this.exceptionBeforeTest = msg;
1286
}
1287
};
1288
1289
1290
/**
1291
* Start a new batch to tests after yielding, resetting batchTime and depth.
1292
* @private
1293
*/
1294
goog.testing.TestCase.prototype.startNextBatch_ = function() {
1295
'use strict';
1296
this.batchTime_ = this.now();
1297
goog.testing.TestCase.Continuation_.run(this.runNextTest_());
1298
};
1299
1300
1301
/**
1302
* Reorders the tests depending on the `order` field.
1303
* @private
1304
*/
1305
goog.testing.TestCase.prototype.orderTests_ = function() {
1306
'use strict';
1307
switch (this.order) {
1308
case goog.testing.TestCase.Order.RANDOM:
1309
// Fisher-Yates shuffle
1310
var i = this.tests_.length;
1311
while (i > 1) {
1312
// goog.math.randomInt is inlined to reduce dependencies.
1313
var j = Math.floor(Math.random() * i); // exclusive
1314
i--;
1315
var tmp = this.tests_[i];
1316
this.tests_[i] = this.tests_[j];
1317
this.tests_[j] = tmp;
1318
}
1319
break;
1320
1321
case goog.testing.TestCase.Order.SORTED:
1322
this.tests_.sort(function(t1, t2) {
1323
'use strict';
1324
if (t1.name == t2.name) {
1325
return 0;
1326
}
1327
return t1.name < t2.name ? -1 : 1;
1328
});
1329
break;
1330
1331
// Do nothing for NATURAL.
1332
}
1333
};
1334
1335
1336
/**
1337
* Gets list of objects that potentially contain test cases. For IE 8 and
1338
* below, this is the global "this" (for properties set directly on the global
1339
* this or window) and the RuntimeObject (for global variables and functions).
1340
* For all other browsers, the array simply contains the global this.
1341
*
1342
* @param {string=} opt_prefix An optional prefix. If specified, only get things
1343
* under this prefix. Note that the prefix is only honored in IE, since it
1344
* supports the RuntimeObject:
1345
* http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
1346
* TODO: Remove this option.
1347
* @return {!Array<!Object>} A list of objects that should be inspected.
1348
*/
1349
goog.testing.TestCase.prototype.getGlobals = function(opt_prefix) {
1350
'use strict';
1351
return goog.testing.TestCase.getGlobals(opt_prefix);
1352
};
1353
1354
1355
/**
1356
* Gets list of objects that potentially contain test cases. For IE 8 and
1357
* below, this is the global "this" (for properties set directly on the global
1358
* this or window) and the RuntimeObject (for global variables and functions).
1359
* For all other browsers, the array simply contains the global this.
1360
*
1361
* @param {string=} opt_prefix An optional prefix. If specified, only get things
1362
* under this prefix. Note that the prefix is only honored in IE, since it
1363
* supports the RuntimeObject:
1364
* http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
1365
* TODO: Remove this option.
1366
* @return {!Array<!Object>} A list of objects that should be inspected.
1367
*/
1368
goog.testing.TestCase.getGlobals = function(opt_prefix) {
1369
'use strict';
1370
// Look in the global scope for most browsers, on IE we use the little known
1371
// RuntimeObject which holds references to all globals. We reference this
1372
// via goog.global so that there isn't an aliasing that throws an exception
1373
// in Firefox.
1374
return typeof goog.global['RuntimeObject'] != 'undefined' ?
1375
[goog.global['RuntimeObject']((opt_prefix || '') + '*'), goog.global] :
1376
[goog.global];
1377
};
1378
1379
1380
/**
1381
* @private {?goog.testing.TestCase}
1382
*/
1383
goog.testing.TestCase.activeTestCase_ = null;
1384
1385
1386
/**
1387
* @return {?goog.testing.TestCase} currently active test case or null if not
1388
* test is currently running. Tries the G_testRunner first then the stored
1389
* value (when run outside of G_testRunner.
1390
*/
1391
goog.testing.TestCase.getActiveTestCase = function() {
1392
'use strict';
1393
var gTestRunner = goog.global['G_testRunner'];
1394
if (gTestRunner && gTestRunner.testCase) {
1395
return gTestRunner.testCase;
1396
} else {
1397
return goog.testing.TestCase.activeTestCase_;
1398
}
1399
};
1400
1401
1402
/**
1403
* Calls {@link goog.testing.TestCase.prototype.invalidateAssertionException}
1404
* on the active test case if it is installed, and logs an error otherwise.
1405
* @param {!goog.testing.JsUnitException} e The exception object to invalidate.
1406
* @package
1407
*/
1408
goog.testing.TestCase.invalidateAssertionException = function(e) {
1409
'use strict';
1410
var testCase = goog.testing.TestCase.getActiveTestCase();
1411
if (testCase) {
1412
testCase.invalidateAssertionException(e);
1413
} else {
1414
goog.global.console.error(
1415
'Failed to remove expected exception: no test case is installed.');
1416
}
1417
};
1418
1419
1420
/**
1421
* Gets called before any tests are executed. Can be overridden to set up the
1422
* environment for the whole test case.
1423
* @return {!Thenable|undefined}
1424
*/
1425
goog.testing.TestCase.prototype.setUpPage = function() {};
1426
1427
1428
/**
1429
* Gets called after all tests have been executed. Can be overridden to tear
1430
* down the entire test case.
1431
*/
1432
goog.testing.TestCase.prototype.tearDownPage = function() {};
1433
1434
1435
/**
1436
* Gets called before every goog.testing.TestCase.Test is been executed. Can
1437
* be overridden to add set up functionality to each test.
1438
* @return {!Thenable|undefined}
1439
*/
1440
goog.testing.TestCase.prototype.setUp = function() {};
1441
1442
1443
/**
1444
* Gets called after every goog.testing.TestCase.Test has been executed. Can
1445
* be overridden to add tear down functionality to each test.
1446
* @return {!Thenable|undefined}
1447
*/
1448
goog.testing.TestCase.prototype.tearDown = function() {};
1449
1450
1451
/**
1452
* @return {string} The function name prefix used to auto-discover tests.
1453
*/
1454
goog.testing.TestCase.prototype.getAutoDiscoveryPrefix = function() {
1455
'use strict';
1456
return 'test';
1457
};
1458
1459
1460
/**
1461
* @return {number} Time since the last batch of tests was started.
1462
* @protected
1463
*/
1464
goog.testing.TestCase.prototype.getBatchTime = function() {
1465
'use strict';
1466
return this.batchTime_;
1467
};
1468
1469
1470
/**
1471
* @param {number} batchTime Time since the last batch of tests was started.
1472
* @protected
1473
*/
1474
goog.testing.TestCase.prototype.setBatchTime = function(batchTime) {
1475
'use strict';
1476
this.batchTime_ = batchTime;
1477
};
1478
1479
1480
/**
1481
* Creates a `goog.testing.TestCase.Test` from an auto-discovered
1482
* function.
1483
* @param {string} name The name of the function.
1484
* @param {function()} ref The auto-discovered function.
1485
* @param {!Object=} scope The scope to attach to the test.
1486
* @param {!Array<!Object>=} objChain
1487
* @return {!goog.testing.TestCase.Test} The newly created test.
1488
* @protected
1489
*/
1490
goog.testing.TestCase.prototype.createTest = function(
1491
name, ref, scope, objChain) {
1492
'use strict';
1493
return new goog.testing.TestCase.Test(name, ref, scope, objChain);
1494
};
1495
1496
1497
/**
1498
* Adds any functions defined on the global object
1499
* that correspond to lifecycle events for the test case. Overrides
1500
* setUp, tearDown, setUpPage, tearDownPage, runTests, and shouldRunTests
1501
* if they are defined on global object.
1502
*/
1503
goog.testing.TestCase.prototype.autoDiscoverLifecycle = function() {
1504
'use strict';
1505
this.setLifecycleObj(goog.global);
1506
};
1507
1508
1509
// TODO(johnlenz): make this package private
1510
/**
1511
* Extracts any functions defined on 'obj' that correspond to page lifecycle
1512
* events (setUpPage, tearDownPage, runTests, shouldRunTests) and add them to
1513
* on this test case.
1514
* @param {!Object} obj
1515
*/
1516
goog.testing.TestCase.prototype.setLifecycleObj = function(obj) {
1517
'use strict';
1518
if (obj['setUp']) {
1519
this.setUp = goog.bind(obj['setUp'], obj);
1520
}
1521
if (obj['tearDown']) {
1522
this.tearDown = goog.bind(obj['tearDown'], obj);
1523
}
1524
if (obj['setUpPage']) {
1525
this.setUpPage = goog.bind(obj['setUpPage'], obj);
1526
}
1527
if (obj['tearDownPage']) {
1528
this.tearDownPage = goog.bind(obj['tearDownPage'], obj);
1529
}
1530
if (obj['runTests']) {
1531
this.runTests = goog.bind(obj['runTests'], obj);
1532
}
1533
if (obj['shouldRunTests']) {
1534
this.shouldRunTests = goog.bind(obj['shouldRunTests'], obj);
1535
}
1536
};
1537
1538
1539
// TODO(johnlenz): make this package private
1540
/**
1541
* @param {!Object} obj An object from which to extract test and lifecycle
1542
* methods.
1543
*/
1544
goog.testing.TestCase.prototype.setTestObj = function(obj) {
1545
'use strict';
1546
// Check any previously added (likely auto-discovered) tests, only one source
1547
// of discovered test and life-cycle methods is allowed.
1548
if (this.tests_.length > 0) {
1549
throw new Error(
1550
'Test methods have already been configured.\n' +
1551
'Tests previously found:\n' +
1552
this.tests_
1553
.map(function(test) {
1554
'use strict';
1555
return test.name;
1556
})
1557
.join('\n') +
1558
'\nNew tests found:\n' +
1559
Object.keys(obj)
1560
.filter(function(name) {
1561
'use strict';
1562
return name.startsWith('test');
1563
})
1564
.join('\n'));
1565
}
1566
this.shouldAutoDiscoverTests_ = false;
1567
if (obj['getTestName']) {
1568
this.name_ = obj['getTestName']();
1569
}
1570
this.setLifecycleObj(obj);
1571
this.addTestObj_(obj, '', [this]);
1572
};
1573
1574
/**
1575
* @param {!Object} obj An object from which to extract test and lifecycle
1576
* methods.
1577
* @param {string} name
1578
* @param {!Array<!Object>} objChain List of objects that have methods used
1579
* to create tests such as setUp, tearDown.
1580
* @private
1581
*/
1582
goog.testing.TestCase.prototype.addTestObj_ = function(obj, name, objChain) {
1583
'use strict';
1584
var regex = new RegExp('^' + this.getAutoDiscoveryPrefix());
1585
var properties = goog.object.getAllPropertyNames(obj);
1586
for (var i = 0; i < properties.length; i++) {
1587
var testName = properties[i];
1588
if (regex.test(testName)) {
1589
var testProperty;
1590
try {
1591
testProperty = obj[testName];
1592
} catch (ex) {
1593
// NOTE(brenneman): When running tests from a file:// URL on Firefox
1594
// 3.5 for Windows, any reference to goog.global.sessionStorage raises
1595
// an "Operation is not supported" exception. Ignore any exceptions
1596
// raised by simply accessing global properties.
1597
testProperty = null;
1598
}
1599
if (name) {
1600
testName = testName.slice(this.getAutoDiscoveryPrefix().length);
1601
}
1602
var fullTestName = name + (testName && name ? '_' : '') + testName;
1603
if (typeof testProperty === 'function') {
1604
this.addNewTest(fullTestName, testProperty, obj, objChain);
1605
} else if (goog.isObject(testProperty) && !Array.isArray(testProperty)) {
1606
// To prevent infinite loops.
1607
if (!goog.array.contains(objChain, testProperty)) {
1608
goog.asserts.assertObject(testProperty);
1609
var newObjChain = objChain.slice();
1610
newObjChain.push(testProperty);
1611
this.addTestObj_(testProperty, fullTestName, newObjChain);
1612
}
1613
}
1614
}
1615
}
1616
};
1617
1618
1619
/**
1620
* Adds any functions defined in the global scope that are prefixed with
1621
* "test" to the test case.
1622
*/
1623
goog.testing.TestCase.prototype.autoDiscoverTests = function() {
1624
'use strict';
1625
this.autoDiscoverLifecycle();
1626
var prefix = this.getAutoDiscoveryPrefix();
1627
var testSources = this.getGlobals(prefix);
1628
1629
for (var i = 0; i < testSources.length; i++) {
1630
var testSource = testSources[i];
1631
this.addTestObj_(testSource, '', [this]);
1632
}
1633
1634
this.orderTests_();
1635
};
1636
1637
1638
/**
1639
* Checks to see if the test should be marked as failed before it is run.
1640
*
1641
* If there was an error in setUpPage, we treat that as a failure for all
1642
* tests and mark them all as having failed.
1643
*
1644
* @param {goog.testing.TestCase.Test} testCase The current test case.
1645
* @return {boolean} Whether the test was marked as failed.
1646
* @protected
1647
*/
1648
goog.testing.TestCase.prototype.maybeFailTestEarly = function(testCase) {
1649
'use strict';
1650
if (this.exceptionBeforeTest) {
1651
// We just use the first error to report an error on a failed test.
1652
testCase.name = 'setUpPage for ' + testCase.name;
1653
this.recordError(testCase.name, this.exceptionBeforeTest);
1654
return true;
1655
}
1656
return false;
1657
};
1658
1659
1660
/**
1661
* Cycles through the tests, yielding asynchronously if the execution time
1662
* exceeds {@link #maxRunTime}. In particular, there is no guarantee that
1663
* the test case has finished execution once this method has returned.
1664
* To be notified when the test case has finished execution, use
1665
* {@link #addCompletedCallback} or {@link #runTestsReturningPromise}.
1666
*/
1667
goog.testing.TestCase.prototype.cycleTests = function() {
1668
'use strict';
1669
this.saveMessage('Start');
1670
this.batchTime_ = this.now();
1671
if (this.running) {
1672
this.runNextTestCallback_ = () => {};
1673
// Kick off the tests. runNextTest_ will schedule all of the tests,
1674
// using a mixture of synchronous and asynchronous strategies.
1675
goog.testing.TestCase.Continuation_.run(this.runNextTest_());
1676
}
1677
};
1678
1679
1680
/**
1681
* Counts the number of files that were loaded for dependencies that are
1682
* required to run the test.
1683
* @return {number} The number of files loaded.
1684
* @private
1685
*/
1686
goog.testing.TestCase.prototype.countNumFilesLoaded_ = function() {
1687
'use strict';
1688
var scripts = goog.dom.getElementsByTagName(goog.dom.TagName.SCRIPT);
1689
var count = 0;
1690
for (var i = 0, n = scripts.length; i < n; i++) {
1691
if (scripts[i].src) {
1692
count++;
1693
}
1694
}
1695
return count;
1696
};
1697
1698
1699
/**
1700
* Calls a function after a delay, using the protected timeout.
1701
* @param {Function} fn The function to call.
1702
* @param {number} time Delay in milliseconds.
1703
* @return {number} The timeout id.
1704
* @protected
1705
*/
1706
goog.testing.TestCase.prototype.timeout = function(fn, time) {
1707
'use strict';
1708
// NOTE: invoking protectedSetTimeout_ as a member of goog.testing.TestCase
1709
// would result in an Illegal Invocation error. The method must be executed
1710
// with the global context.
1711
var protectedSetTimeout = goog.testing.TestCase.protectedSetTimeout_;
1712
return protectedSetTimeout(fn, time);
1713
};
1714
1715
1716
/**
1717
* Clears a timeout created by `this.timeout()`.
1718
* @param {number} id A timeout id.
1719
* @protected
1720
*/
1721
goog.testing.TestCase.prototype.clearTimeout = function(id) {
1722
'use strict';
1723
// NOTE: see execution note for protectedSetTimeout above.
1724
var protectedClearTimeout = goog.testing.TestCase.protectedClearTimeout_;
1725
protectedClearTimeout(id);
1726
};
1727
1728
1729
/**
1730
* @return {number} The current time in milliseconds.
1731
* @protected
1732
*/
1733
goog.testing.TestCase.prototype.now = function() {
1734
'use strict';
1735
return goog.testing.TestCase.now();
1736
};
1737
1738
1739
/**
1740
* @return {number} The current time in milliseconds.
1741
* @protected
1742
*/
1743
goog.testing.TestCase.now = function() {
1744
'use strict';
1745
// don't use goog.now as some tests override it.
1746
if (goog.testing.TestCase.protectedPerformance_) {
1747
return goog.testing.TestCase.protectedPerformance_.now();
1748
}
1749
// Fallback for IE8
1750
// Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
1751
var protectedDate = goog.testing.TestCase.protectedDate_;
1752
return new protectedDate().getTime();
1753
};
1754
1755
1756
/**
1757
* Returns the current time.
1758
* @return {string} HH:MM:SS.
1759
* @private
1760
*/
1761
goog.testing.TestCase.prototype.getTimeStamp_ = function() {
1762
'use strict';
1763
// Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
1764
var protectedDate = goog.testing.TestCase.protectedDate_;
1765
var d = new protectedDate();
1766
1767
// Ensure millis are always 3-digits
1768
var millis = '00' + d.getMilliseconds();
1769
millis = millis.slice(-3);
1770
1771
return this.pad_(d.getHours()) + ':' + this.pad_(d.getMinutes()) + ':' +
1772
this.pad_(d.getSeconds()) + '.' + millis;
1773
};
1774
1775
1776
/**
1777
* Pads a number to make it have a leading zero if it's less than 10.
1778
* @param {number} number The number to pad.
1779
* @return {string} The resulting string.
1780
* @private
1781
*/
1782
goog.testing.TestCase.prototype.pad_ = function(number) {
1783
'use strict';
1784
return number < 10 ? '0' + number : String(number);
1785
};
1786
1787
1788
/**
1789
* Trims a path to be only that after google3.
1790
* @param {string} path The path to trim.
1791
* @return {string} The resulting string.
1792
* @private
1793
*/
1794
goog.testing.TestCase.prototype.trimPath_ = function(path) {
1795
'use strict';
1796
return path.substring(path.indexOf('google3') + 8);
1797
};
1798
1799
1800
/**
1801
* Handles a test that passed.
1802
* @param {goog.testing.TestCase.Test} test The test that passed.
1803
* @protected
1804
*/
1805
goog.testing.TestCase.prototype.doSuccess = function(test) {
1806
'use strict';
1807
this.result_.successCount++;
1808
// An empty list of error messages indicates that the test passed.
1809
// If we already have a failure for this test, do not set to empty list.
1810
if (!(test.name in this.result_.resultsByName)) {
1811
this.result_.resultsByName[test.name] = [];
1812
}
1813
var message = test.name + ' : PASSED';
1814
this.saveMessage(message);
1815
this.log(message);
1816
if (this.testDone_) {
1817
this.doTestDone_(test, []);
1818
}
1819
};
1820
1821
1822
/**
1823
* Handles a test that was skipped.
1824
* @param {!goog.testing.TestCase.Test} test The test that was skipped.
1825
* @protected
1826
*/
1827
goog.testing.TestCase.prototype.doSkipped = function(test) {
1828
'use strict';
1829
this.result_.skipCount++;
1830
// An empty list of error messages indicates that the test passed.
1831
// If we already have a failure for this test, do not set to empty list.
1832
if (!(test.name in this.result_.resultsByName)) {
1833
this.result_.resultsByName[test.name] = [];
1834
}
1835
var message = test.name + ' : SKIPPED';
1836
this.saveMessage(message);
1837
this.log(message);
1838
if (this.testDone_) {
1839
this.doTestDone_(test, []);
1840
}
1841
};
1842
1843
1844
/**
1845
* Records an error that fails the current test, without throwing it.
1846
*
1847
* Use this function to implement expect()-style assertion libraries that fail a
1848
* test without breaking execution (so you can see further failures). Do not use
1849
* this from normal test code.
1850
*
1851
* Please contact js-core-libraries-team@ before using this method. If it grows
1852
* popular, we may add an expect() API to Closure.
1853
*
1854
* NOTE: If there is no active TestCase, you must throw an error.
1855
* @param {!Error} error The error to log. If it is a JsUnitException which has
1856
* already been logged, nothing will happen.
1857
*/
1858
goog.testing.TestCase.prototype.recordTestError = function(error) {
1859
'use strict';
1860
this.recordError(
1861
this.curTest_ ? this.curTest_.name : '<No active test>', error);
1862
};
1863
1864
1865
1866
/**
1867
* Records and logs an error from or related to a test.
1868
* @param {string} testName The name of the test that failed.
1869
* @param {*} error The exception object associated with the
1870
* failure or a string.
1871
* @protected
1872
*/
1873
goog.testing.TestCase.prototype.recordError = function(testName, error) {
1874
'use strict';
1875
if (error && error['isJsUnitException'] && error['loggedJsUnitException']) {
1876
// We already logged this error; don't record it again. This is particularly
1877
// important for errors from mocks, which are rethrown by $verify, called by
1878
// tearDown().
1879
return;
1880
}
1881
1882
var err = this.logError(testName, error);
1883
this.result_.errors.push(err);
1884
if (testName in this.result_.resultsByName) {
1885
this.result_.resultsByName[testName].push(err);
1886
} else {
1887
this.result_.resultsByName[testName] = [err];
1888
}
1889
1890
if (error && error['isJsUnitException']) {
1891
error['loggedJsUnitException'] = true;
1892
}
1893
};
1894
1895
1896
/**
1897
* Handles a test that failed.
1898
* @param {goog.testing.TestCase.Test} test The test that failed.
1899
* @protected
1900
*/
1901
goog.testing.TestCase.prototype.doError = function(test) {
1902
'use strict';
1903
var message = test.name + ' : FAILED';
1904
this.log(message);
1905
this.saveMessage(message);
1906
1907
if (this.testDone_) {
1908
var results = this.result_.resultsByName[test.name];
1909
var errMsgs = [];
1910
for (var i = 0; i < results.length; i++) {
1911
errMsgs.push(results[i].toString());
1912
}
1913
this.doTestDone_(test, errMsgs);
1914
}
1915
};
1916
1917
1918
/**
1919
* Makes note of an exception arising from an assertion, and then throws it.
1920
* If the test otherwise passes (i.e., because something else caught the
1921
* exception on its way to the test framework), it will be forced to fail.
1922
* @param {!goog.testing.JsUnitException} e The exception object being thrown.
1923
* @throws {goog.testing.JsUnitException}
1924
* @package
1925
*/
1926
goog.testing.TestCase.prototype.raiseAssertionException = function(e) {
1927
'use strict';
1928
this.thrownAssertionExceptions_.push(e);
1929
throw e;
1930
};
1931
1932
1933
/**
1934
* Removes the specified exception from being tracked. This only needs to be
1935
* called for internal functions that intentionally catch an exception, such
1936
* as
1937
* `#assertThrowsJsUnitException`.
1938
* @param {!goog.testing.JsUnitException} e The exception object to invalidate.
1939
* @package
1940
*/
1941
goog.testing.TestCase.prototype.invalidateAssertionException = function(e) {
1942
'use strict';
1943
goog.array.remove(this.thrownAssertionExceptions_, e);
1944
};
1945
1946
1947
/**
1948
* @param {string} name Failed test name.
1949
* @param {*} error The exception object associated with the
1950
* failure or a string.
1951
* @return {!goog.testing.TestCase.Error} Error object.
1952
* @suppress {missingProperties} message and stack properties
1953
*/
1954
goog.testing.TestCase.prototype.logError = function(name, error) {
1955
'use strict';
1956
if (error) {
1957
this.log(error);
1958
}
1959
1960
var normalizedError = goog.debug.normalizeErrorObject(error);
1961
var stack =
1962
this.cleanStackTrace_(normalizedError.stack, normalizedError.message);
1963
var err =
1964
new goog.testing.TestCase.Error(name, normalizedError.message, stack);
1965
1966
this.saveMessage(err.toString());
1967
1968
return err;
1969
};
1970
1971
/**
1972
* @param {?string} stack
1973
* @param {string} errMsg
1974
* @return {string|undefined}
1975
* @private
1976
*/
1977
goog.testing.TestCase.prototype.cleanStackTrace_ = function(stack, errMsg) {
1978
'use strict';
1979
if (!stack) {
1980
return;
1981
}
1982
1983
// The Error class includes the message in the stack. Don't duplicate it.
1984
stack = stack.replace('Error: ' + errMsg + '\n', 'Error\n');
1985
1986
// Remove extra goog.testing.TestCase frames from all stacks (main error +
1987
// causes if they exists)
1988
var index = 0;
1989
while (index < stack.length) {
1990
var extraFrameIndex = stack.search(
1991
/\s*(\bat\b)?\s*(goog\.labs\.testing\.EnvironmentTestCase_\.)?goog\.testing\.(Continuation_\.(prototype\.)?run|TestCase\.(prototype\.)?(execute|cycleTests|startNextBatch_|safeRunTest_|invokeFunction_?))/);
1992
if (extraFrameIndex < 0) {
1993
break;
1994
}
1995
1996
var causedByIndex = stack.indexOf('Caused by:', extraFrameIndex);
1997
index = causedByIndex < 0 ? stack.length : causedByIndex;
1998
1999
2000
stack = stack.substring(0, extraFrameIndex + 1) + stack.substring(index);
2001
}
2002
2003
return stack;
2004
};
2005
2006
2007
/**
2008
* A class representing a single test function.
2009
* @param {string} name The test name.
2010
* @param {?function()} ref Reference to the test function or test object.
2011
* @param {?Object=} scope Optional scope that the test function should be
2012
* called in.
2013
* @param {!Array<?>=} objChain A chain of objects used to populate setUps
2014
* and tearDowns.
2015
* @constructor
2016
*/
2017
goog.testing.TestCase.Test = function(name, ref, scope, objChain) {
2018
'use strict';
2019
/**
2020
* The name of the test.
2021
* @type {string}
2022
*/
2023
this.name = name;
2024
2025
/**
2026
* TODO(user): Rename this to something more clear.
2027
* Reference to the test function.
2028
* @type {function()}
2029
*/
2030
this.ref = ref || function() {};
2031
2032
/**
2033
* Scope that the test function should be called in.
2034
* @type {?Object}
2035
*/
2036
this.scope = scope || null;
2037
2038
/**
2039
* @type {!Array<function()>}
2040
*/
2041
this.setUps = [];
2042
2043
/**
2044
* @type {!Array<function()>}
2045
*/
2046
this.tearDowns = [];
2047
2048
/**
2049
* @type {!Array<?>}
2050
*/
2051
this.objChain = objChain || [];
2052
2053
if (objChain) {
2054
for (var i = 0; i < objChain.length; i++) {
2055
if (typeof objChain[i].setUp === 'function') {
2056
this.setUps.push(goog.bind(objChain[i].setUp, objChain[i]));
2057
}
2058
if (typeof objChain[i].tearDown === 'function') {
2059
this.tearDowns.push(goog.bind(objChain[i].tearDown, objChain[i]));
2060
}
2061
}
2062
this.tearDowns.reverse();
2063
}
2064
2065
/**
2066
* Timestamp just before the test begins execution.
2067
* @type {number}
2068
* @private
2069
*/
2070
this.startTime_;
2071
2072
/**
2073
* Timestamp just after the test ends execution.
2074
* @type {number}
2075
* @private
2076
*/
2077
this.stoppedTime_;
2078
2079
/** @package {boolean|undefined} */
2080
this.waiting;
2081
};
2082
2083
/**
2084
* Executes the test function.
2085
* @package
2086
*/
2087
goog.testing.TestCase.Test.prototype.execute = function() {
2088
'use strict';
2089
this.ref.call(this.scope);
2090
};
2091
2092
/**
2093
* Sets the start time
2094
*/
2095
goog.testing.TestCase.Test.prototype.started = function() {
2096
'use strict';
2097
this.startTime_ = goog.testing.TestCase.now();
2098
};
2099
2100
/**
2101
* Sets the stop time
2102
*/
2103
goog.testing.TestCase.Test.prototype.stopped = function() {
2104
'use strict';
2105
this.stoppedTime_ = goog.testing.TestCase.now();
2106
};
2107
2108
/**
2109
* Returns the runtime for this test function in milliseconds.
2110
* @return {number}
2111
*/
2112
goog.testing.TestCase.Test.prototype.getElapsedTime = function() {
2113
'use strict';
2114
// Round the elapsed time to the closest multiple of 0.1ms (the resolution of
2115
// performance.now()) to avoid noise due to floating point rounding errors
2116
// when it's printed.
2117
return Math.round((this.stoppedTime_ - this.startTime_) * 10) / 10;
2118
};
2119
2120
/**
2121
* A class for representing test results. A bag of public properties.
2122
* @param {goog.testing.TestCase} testCase The test case that owns this result.
2123
* @constructor
2124
* @final
2125
*/
2126
goog.testing.TestCase.Result = function(testCase) {
2127
'use strict';
2128
/**
2129
* The test case that owns this result.
2130
* @type {goog.testing.TestCase}
2131
* @private
2132
*/
2133
this.testCase_ = testCase;
2134
2135
/**
2136
* Total number of tests that should have been run.
2137
* @type {number}
2138
*/
2139
this.totalCount = 0;
2140
2141
/**
2142
* Total number of tests that were actually run.
2143
* @type {number}
2144
*/
2145
this.runCount = 0;
2146
2147
/**
2148
* Number of successful tests.
2149
* @type {number}
2150
*/
2151
this.successCount = 0;
2152
2153
/**
2154
* Number of tests skipped due to nested shouldRunTests.
2155
* @type {number}
2156
*/
2157
this.skipCount = 0;
2158
2159
/**
2160
* The amount of time the tests took to run.
2161
* @type {number}
2162
*/
2163
this.runTime = 0;
2164
2165
/**
2166
* The number of files loaded to run this test.
2167
* @type {number}
2168
*/
2169
this.numFilesLoaded = 0;
2170
2171
/**
2172
* Whether all tests were suppressed from a top-level shouldRunTests().
2173
* @type {boolean}
2174
*/
2175
this.testSuppressed = false;
2176
2177
/**
2178
* Which tests were suppressed by shouldRunTests() returning false.
2179
* @type {!Array<string>}
2180
*/
2181
this.suppressedTests = [];
2182
2183
/**
2184
* Test results for each test that was run. The test name is always added
2185
* as the key in the map, and the array of strings is an optional list
2186
* of failure messages. If the array is empty, the test passed. Otherwise,
2187
* the test failed.
2188
* @type {!Object<string, !Array<goog.testing.TestCase.Error>>}
2189
*/
2190
this.resultsByName = {};
2191
2192
/**
2193
* Errors encountered while running the test.
2194
* @type {!Array<goog.testing.TestCase.Error>}
2195
*/
2196
this.errors = [];
2197
2198
/**
2199
* Messages to show the user after running the test.
2200
* @type {!Array<string>}
2201
*/
2202
this.messages = [];
2203
2204
/**
2205
* Whether the tests have completed.
2206
* @type {boolean}
2207
*/
2208
this.complete = false;
2209
};
2210
2211
2212
/**
2213
* @return {boolean} Whether the test was successful.
2214
*/
2215
goog.testing.TestCase.Result.prototype.isSuccess = function() {
2216
'use strict';
2217
return this.complete && this.errors.length == 0;
2218
};
2219
2220
2221
/**
2222
* @return {string} A summary of the tests, including total number of tests that
2223
* passed, failed, and the time taken.
2224
*/
2225
goog.testing.TestCase.Result.prototype.getSummary = function() {
2226
'use strict';
2227
var summary = this.runCount + ' of ' + this.totalCount + ' tests run in ' +
2228
Math.round(this.runTime) + ' ms.\n';
2229
if (this.testSuppressed) {
2230
summary += 'Tests not run because shouldRunTests() returned false.';
2231
} else {
2232
var failures = this.totalCount - this.successCount - this.skipCount;
2233
var suppressionMessage = '';
2234
2235
if (this.skipCount) {
2236
suppressionMessage +=
2237
', ' + this.skipCount + ' skipped by shouldRunTests()';
2238
}
2239
2240
var countOfRunTests = this.testCase_.getActuallyRunCount();
2241
if (countOfRunTests) {
2242
failures = countOfRunTests - this.successCount - this.skipCount;
2243
suppressionMessage += ', ' + (this.totalCount - countOfRunTests) +
2244
' suppressed by querystring';
2245
}
2246
summary += this.successCount + ' passed, ' + failures + ' failed' +
2247
suppressionMessage + '.\n' + Math.round(this.runTime / this.runCount) +
2248
' ms/test. ' + this.numFilesLoaded + ' files loaded.';
2249
}
2250
2251
return summary;
2252
};
2253
2254
2255
/**
2256
* @param {function(goog.testing.TestCase.Test, !Array<string>)} testDone
2257
*/
2258
goog.testing.TestCase.prototype.setTestDoneCallback = function(testDone) {
2259
'use strict';
2260
this.testDone_ = testDone;
2261
};
2262
2263
2264
/**
2265
* @param {goog.testing.TestCase.Test} test
2266
* @param {!Array<string>} errMsgs
2267
* @private
2268
*/
2269
goog.testing.TestCase.prototype.doTestDone_ = function(test, errMsgs) {
2270
'use strict';
2271
test.stopped();
2272
this.testDone_(test, errMsgs);
2273
};
2274
2275
/**
2276
* Initializes the TestCase.
2277
* @param {goog.testing.TestCase} testCase The test case to install.
2278
* @param {function(goog.testing.TestCase.Test, Array<string>)=} opt_testDone
2279
* Called when each test completes.
2280
*/
2281
goog.testing.TestCase.initializeTestCase = function(testCase, opt_testDone) {
2282
'use strict';
2283
if (opt_testDone) {
2284
testCase.setTestDoneCallback(opt_testDone);
2285
}
2286
2287
if (testCase.shouldAutoDiscoverTests_) {
2288
testCase.autoDiscoverTests();
2289
} else {
2290
// Make sure the tests are still ordered based on provided order.
2291
testCase.orderTests_();
2292
}
2293
2294
if (goog.global.location) {
2295
var href = goog.global.location.href;
2296
testCase.setTestsToRun(goog.testing.TestCase.parseRunTests_(href));
2297
}
2298
goog.testing.TestCase.activeTestCase_ = testCase;
2299
};
2300
2301
2302
/**
2303
* Initializes the given test case with the global test runner 'G_testRunner'.
2304
* @param {goog.testing.TestCase} testCase The test case to install.
2305
* @param {function(goog.testing.TestCase.Test, Array<string>)=} opt_testDone
2306
* Called when each test completes.
2307
*/
2308
goog.testing.TestCase.initializeTestRunner = function(testCase, opt_testDone) {
2309
'use strict';
2310
goog.testing.TestCase.initializeTestCase(testCase, opt_testDone);
2311
2312
var gTestRunner = goog.global['G_testRunner'];
2313
if (gTestRunner) {
2314
gTestRunner['initialize'](testCase);
2315
} else {
2316
throw new Error(
2317
'G_testRunner is undefined. Please ensure goog.testing.jsunit' +
2318
' is included.');
2319
}
2320
};
2321
2322
2323
/**
2324
* Parses URL query parameters for the 'runTests' parameter.
2325
* @param {string} href The current URL.
2326
* @return {Object<string, boolean>} A set of test names or test indices to be
2327
* run by the test runner.
2328
* @private
2329
*/
2330
goog.testing.TestCase.parseRunTests_ = function(href) {
2331
'use strict';
2332
const queryParamIndex = href.indexOf('?');
2333
if (queryParamIndex < 0) {
2334
return null;
2335
}
2336
2337
const nonOriginParts = href.slice(queryParamIndex);
2338
2339
// Use a "fake" origin because tests may load using protocols that goog.url
2340
// doesn't support
2341
const searchParams = goog.url.getSearchParams(
2342
goog.url.resolveUrl('https://google.com' + nonOriginParts));
2343
2344
let runTestsString = null;
2345
for (const [key, value] of searchParams) {
2346
if (key.toLowerCase() === 'runtests') {
2347
runTestsString = value;
2348
}
2349
}
2350
2351
if (!runTestsString) {
2352
return null;
2353
}
2354
2355
const testsToRun = {};
2356
const arr = runTestsString.split(',');
2357
for (let i = 0, len = arr.length; i < len; i++) {
2358
try {
2359
// `TestRunner` double encodes commas in test names so we decode back here
2360
testsToRun[arr[i].replace(/%2C/g, ',')] = true;
2361
} catch (e) {
2362
return null;
2363
}
2364
}
2365
2366
return testsToRun;
2367
};
2368
2369
2370
/**
2371
* Wraps provided promise and returns a new promise which will be rejected
2372
* if the original promise does not settle within the given timeout.
2373
* @param {!IThenable<T>} promise
2374
* @param {number} timeoutInMs Number of milliseconds to wait for the promise to
2375
* settle before failing it with a timeout error.
2376
* @param {string} errorMsg Error message to use if the promise times out.
2377
* @return {!IThenable<T>} A promise that will settle with the original
2378
promise unless the timeout is exceeded.
2379
* error.
2380
* @template T
2381
* @private
2382
*/
2383
goog.testing.TestCase.prototype.rejectIfPromiseTimesOut_ = function(
2384
promise, timeoutInMs, errorMsg) {
2385
'use strict';
2386
const start = this.now();
2387
/**
2388
* @param {function(?)} resolve
2389
* @param {function(*)} reject
2390
*/
2391
const resolver = (resolve, reject) => {
2392
'use strict';
2393
const timeoutId = this.timeout(() => {
2394
'use strict';
2395
const elapsed = this.now() - start;
2396
reject(new Error(`${errorMsg}\nElapsed time: ${elapsed} ms.`));
2397
}, timeoutInMs);
2398
const clearTimeout = () => {
2399
this.clearTimeout(timeoutId);
2400
};
2401
promise.then(clearTimeout, clearTimeout);
2402
promise.then(resolve, reject);
2403
};
2404
if (this.useNativePromise_) {
2405
return new Promise(resolver);
2406
}
2407
return new goog.Promise(resolver);
2408
};
2409
2410
2411
2412
/**
2413
* A class representing an error thrown by the test
2414
* @param {string} source The name of the test which threw the error.
2415
* @param {string} message The error message.
2416
* @param {string=} opt_stack A string showing the execution stack.
2417
* @constructor
2418
* @final
2419
*/
2420
goog.testing.TestCase.Error = function(source, message, opt_stack) {
2421
'use strict';
2422
/**
2423
* The name of the test which threw the error.
2424
* @type {string}
2425
*/
2426
this.source = source;
2427
2428
/**
2429
* Reference to the test function.
2430
* @type {string}
2431
*/
2432
this.message = message;
2433
2434
/**
2435
* The stack.
2436
* @type {?string}
2437
*/
2438
this.stack = null;
2439
2440
if (opt_stack) {
2441
this.stack = opt_stack;
2442
} else {
2443
// Attempt to capture a stack trace.
2444
if (Error.captureStackTrace) {
2445
// See https://code.google.com/p/v8-wiki/wiki/JavaScriptStackTraceApi
2446
Error.captureStackTrace(this, goog.testing.TestCase.Error);
2447
} else {
2448
var stack = new Error().stack;
2449
if (stack) {
2450
this.stack = stack;
2451
}
2452
}
2453
}
2454
};
2455
2456
2457
/**
2458
* Call this from setUpPage() to prevent any Content Security Policy violations
2459
* that may have occurred during page load from being reported as errors .
2460
*/
2461
goog.testing.TestCase.prototype.ignoreStartupCspViolations = function() {
2462
this.ignoreStartupCspViolations_ = true;
2463
};
2464
2465
2466
/**
2467
* Toggles recording of Content Security Policy violations. Call this with false
2468
* during tests, setUpPage, setUp, and tearDown functions to prevent CSP
2469
* violations occurring while the function is executing from being reported as
2470
* errors. Reporting will be reset upon execution of the next test function.
2471
*
2472
* @param {boolean} enable
2473
*/
2474
goog.testing.TestCase.prototype.observeCspViolations = function(enable) {
2475
this.cspViolationObserver_.setEnabled(enable);
2476
};
2477
2478
2479
/**
2480
* Returns a string representing the error object.
2481
* @return {string} A string representation of the error.
2482
* @override
2483
*/
2484
goog.testing.TestCase.Error.prototype.toString = function() {
2485
'use strict';
2486
return 'ERROR in ' + this.source + '\n' + this.message +
2487
(this.stack && this.stack !== 'Not available' ? '\n' + this.stack : '');
2488
};
2489
2490
/**
2491
* Returns an object representing the error suitable for JSON serialization.
2492
* @return {!goog.testing.TestCase.IResult} An object
2493
* representation of the error.
2494
* @private
2495
*/
2496
goog.testing.TestCase.Error.prototype.toObject_ = function() {
2497
'use strict';
2498
return {
2499
'source': this.source,
2500
'message': this.message,
2501
'stacktrace': this.stack || ''
2502
};
2503
};
2504
2505
2506
2507
/**
2508
* @constructor
2509
* @param {function(): (?goog.testing.TestCase.Continuation_|undefined)} fn
2510
* @private
2511
*/
2512
goog.testing.TestCase.Continuation_ = function(fn) {
2513
'use strict';
2514
/** @private @const */
2515
this.fn_ = fn;
2516
};
2517
2518
2519
/** @param {?goog.testing.TestCase.Continuation_|undefined} continuation */
2520
goog.testing.TestCase.Continuation_.run = function(continuation) {
2521
'use strict';
2522
var fn = continuation && continuation.fn_;
2523
while (fn) {
2524
continuation = fn();
2525
fn = continuation && continuation.fn_;
2526
}
2527
};
2528
2529