testcase.js 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969
  1. // Copyright 2007 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview A class representing a set of test functions to be run.
  16. *
  17. * Testing code should not have dependencies outside of goog.testing so as to
  18. * reduce the chance of masking missing dependencies.
  19. *
  20. * This file does not compile correctly with --collapse_properties. Use
  21. * --property_renaming=ALL_UNQUOTED instead.
  22. *
  23. */
  24. goog.setTestOnly('goog.testing.TestCase');
  25. goog.provide('goog.testing.TestCase');
  26. goog.provide('goog.testing.TestCase.Error');
  27. goog.provide('goog.testing.TestCase.Order');
  28. goog.provide('goog.testing.TestCase.Result');
  29. goog.provide('goog.testing.TestCase.Test');
  30. goog.require('goog.Promise');
  31. goog.require('goog.Thenable');
  32. goog.require('goog.array');
  33. goog.require('goog.asserts');
  34. goog.require('goog.dom');
  35. goog.require('goog.dom.TagName');
  36. goog.require('goog.json');
  37. goog.require('goog.object');
  38. goog.require('goog.testing.JsUnitException');
  39. goog.require('goog.testing.asserts');
  40. /**
  41. * A class representing a JsUnit test case. A TestCase is made up of a number
  42. * of test functions which can be run. Individual test cases can override the
  43. * following functions to set up their test environment:
  44. * - runTests - completely override the test's runner
  45. * - setUpPage - called before any of the test functions are run
  46. * - tearDownPage - called after all tests are finished
  47. * - setUp - called before each of the test functions
  48. * - tearDown - called after each of the test functions
  49. * - shouldRunTests - called before a test run, all tests are skipped if it
  50. * returns false. Can be used to disable tests on browsers
  51. * where they aren't expected to pass.
  52. * <p>
  53. * TestCase objects are usually constructed by inspecting the global environment
  54. * to discover functions that begin with the prefix <code>test</code>.
  55. * (See {@link #autoDiscoverLifecycle} and {@link #autoDiscoverTests}.)
  56. * </p>
  57. *
  58. * <h2>Testing asychronous code with promises</h2>
  59. *
  60. * <p>
  61. * In the simplest cases, the behavior that the developer wants to test
  62. * is synchronous, and the test functions exercising the behavior execute
  63. * synchronously. But TestCase can also be used to exercise asynchronous code
  64. * through the use of <a
  65. * href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">
  66. * promises</a>. If a test function returns an object that has a
  67. * <code>then</code> method defined on it, the test framework switches to an
  68. * asynchronous execution strategy: the next test function will not begin
  69. * execution until the returned promise is resolved or rejected. Instead of
  70. * writing test assertions at the top level inside a test function, the test
  71. * author chains them on the end of the returned promise. For example:
  72. * </p>
  73. * <pre>
  74. * function testPromiseBasedAPI() {
  75. * return promiseBasedAPI().then(function(value) {
  76. * // Will run when the promise resolves, and before the next
  77. * // test function begins execution.
  78. * assertEquals('foo', value.bar);
  79. * });
  80. * }
  81. * </pre>
  82. * <p>
  83. * Synchronous and asynchronous tests can be mixed in the same TestCase.
  84. * Test functions that return an object with a <code>then</code> method are
  85. * executed asynchronously, and all other test functions are executed
  86. * synchronously. While this is convenient for test authors (since it doesn't
  87. * require any explicit configuration for asynchronous tests), it can lead to
  88. * confusion if the test author forgets to return the promise from the test
  89. * function. For example:
  90. * </p>
  91. * <pre>
  92. * function testPromiseBasedAPI() {
  93. * // This test should never succeed.
  94. * promiseBasedAPI().then(fail, fail);
  95. * // Oops! The promise isn't returned to the framework,
  96. * // so this test actually does succeed.
  97. * }
  98. * </pre>
  99. * <p>
  100. * Since the test framework knows nothing about the promise created
  101. * in the test function, it will run the function synchronously, record
  102. * a success, and proceed immediately to the next test function.
  103. * </p>
  104. * <p>
  105. * Promises returned from test functions can time out. If a returned promise
  106. * is not resolved or rejected within {@link promiseTimeout} milliseconds,
  107. * the test framework rejects the promise without a timeout error message.
  108. * Test cases can configure the value of {@code promiseTimeout} by setting
  109. * <pre>
  110. * goog.testing.TestCase.getActiveTestCase().promiseTimeout = ...
  111. * </pre>
  112. * in their {@code setUpPage} methods.
  113. * </p>
  114. *
  115. * @param {string=} opt_name The name of the test case, defaults to
  116. * 'Untitled Test Case'.
  117. * @constructor
  118. */
  119. goog.testing.TestCase = function(opt_name) {
  120. /**
  121. * A name for the test case.
  122. * @type {string}
  123. * @private
  124. */
  125. this.name_ = opt_name || 'Untitled Test Case';
  126. /**
  127. * Array of test functions that can be executed.
  128. * @type {!Array<!goog.testing.TestCase.Test>}
  129. * @private
  130. */
  131. this.tests_ = [];
  132. /**
  133. * Set of test names and/or indices to execute, or null if all tests should
  134. * be executed.
  135. *
  136. * Indices are included to allow automation tools to run a subset of the
  137. * tests without knowing the exact contents of the test file.
  138. *
  139. * Indices should only be used with SORTED ordering.
  140. *
  141. * Example valid values:
  142. * <ul>
  143. * <li>[testName]
  144. * <li>[testName1, testName2]
  145. * <li>[2] - will run the 3rd test in the order specified
  146. * <li>[1,3,5]
  147. * <li>[testName1, testName2, 3, 5] - will work
  148. * <ul>
  149. * @type {Object}
  150. * @private
  151. */
  152. this.testsToRun_ = null;
  153. /**
  154. * A call back for each test.
  155. * @private {?function(goog.testing.TestCase.Test, !Array<string>)}
  156. */
  157. this.testDone_ = null;
  158. /**
  159. * The order to run the auto-discovered tests in.
  160. * @type {string}
  161. */
  162. this.order = goog.testing.TestCase.Order.SORTED;
  163. /** @private {function(!goog.testing.TestCase.Result)} */
  164. this.runNextTestCallback_ = goog.nullFunction;
  165. /** @private {goog.testing.TestCase.Test} */
  166. this.curTest_ = null;
  167. /**
  168. * Object used to encapsulate the test results.
  169. * @type {!goog.testing.TestCase.Result}
  170. * @protected
  171. * @suppress {underscore|visibility}
  172. */
  173. this.result_ = new goog.testing.TestCase.Result(this);
  174. /**
  175. * An array of exceptions generated by {@code assert} statements.
  176. * @private {!Array<!goog.testing.JsUnitException>}
  177. */
  178. this.thrownAssertionExceptions_ = [];
  179. /**
  180. * Whether the test should fail if exceptions arising from an assert statement
  181. * never bubbled up to the testing framework.
  182. * @type {boolean}
  183. */
  184. this.failOnUnreportedAsserts = true;
  185. /**
  186. * The maximum time in milliseconds a promise returned from a test function
  187. * may remain pending before the test fails due to timeout.
  188. * @type {number}
  189. */
  190. this.promiseTimeout = 1000; // 1s
  191. };
  192. /**
  193. * The order to run the auto-discovered tests.
  194. * @enum {string}
  195. */
  196. goog.testing.TestCase.Order = {
  197. /**
  198. * This is browser dependent and known to be different in FF and Safari
  199. * compared to others.
  200. */
  201. NATURAL: 'natural',
  202. /** Random order. */
  203. RANDOM: 'random',
  204. /** Sorted based on the name. */
  205. SORTED: 'sorted'
  206. };
  207. /**
  208. * @return {string} The name of the test.
  209. */
  210. goog.testing.TestCase.prototype.getName = function() {
  211. return this.name_;
  212. };
  213. /**
  214. * The maximum amount of time in milliseconds that the test case can take
  215. * before it is forced to yield and reschedule. This prevents the test runner
  216. * from blocking the browser and potentially hurting the test harness.
  217. * @type {number}
  218. */
  219. goog.testing.TestCase.maxRunTime = 200;
  220. /**
  221. * Save a reference to {@code window.setTimeout}, so any code that overrides the
  222. * default behavior (the MockClock, for example) doesn't affect our runner.
  223. * @type {function((Function|string), number=, *=): number}
  224. * @private
  225. */
  226. goog.testing.TestCase.protectedSetTimeout_ = goog.global.setTimeout;
  227. /**
  228. * Save a reference to {@code window.clearTimeout}, so any code that overrides
  229. * the default behavior (e.g. MockClock) doesn't affect our runner.
  230. * @type {function((null|number|undefined)): void}
  231. * @private
  232. */
  233. goog.testing.TestCase.protectedClearTimeout_ = goog.global.clearTimeout;
  234. /**
  235. * Save a reference to {@code window.Date}, so any code that overrides
  236. * the default behavior doesn't affect our runner.
  237. * @type {function(new: Date)}
  238. * @private
  239. */
  240. goog.testing.TestCase.protectedDate_ = Date;
  241. /**
  242. * Save a reference to {@code window.performance}, so any code that overrides
  243. * the default behavior doesn't affect our runner.
  244. * @type {?Performance}
  245. * @private
  246. */
  247. goog.testing.TestCase.protectedPerformance_ =
  248. window.performance && window.performance.now ? performance : null;
  249. /**
  250. * TODO(user) replace this with prototype.currentTest.
  251. * Name of the current test that is running, or null if none is running.
  252. * @type {?string}
  253. */
  254. goog.testing.TestCase.currentTestName = null;
  255. /**
  256. * Avoid a dependency on goog.userAgent and keep our own reference of whether
  257. * the browser is IE.
  258. * @type {boolean}
  259. */
  260. goog.testing.TestCase.IS_IE = typeof opera == 'undefined' &&
  261. !!goog.global.navigator &&
  262. goog.global.navigator.userAgent.indexOf('MSIE') != -1;
  263. /**
  264. * Exception object that was detected before a test runs.
  265. * @type {*}
  266. * @protected
  267. */
  268. goog.testing.TestCase.prototype.exceptionBeforeTest;
  269. /**
  270. * Whether the test case has ever tried to execute.
  271. * @type {boolean}
  272. */
  273. goog.testing.TestCase.prototype.started = false;
  274. /**
  275. * Whether the test case is running.
  276. * @type {boolean}
  277. */
  278. goog.testing.TestCase.prototype.running = false;
  279. /**
  280. * Timestamp for when the test was started.
  281. * @type {number}
  282. * @private
  283. */
  284. goog.testing.TestCase.prototype.startTime_ = 0;
  285. /**
  286. * Time since the last batch of tests was started, if batchTime exceeds
  287. * {@link #maxRunTime} a timeout will be used to stop the tests blocking the
  288. * browser and a new batch will be started.
  289. * @type {number}
  290. * @private
  291. */
  292. goog.testing.TestCase.prototype.batchTime_ = 0;
  293. /**
  294. * Pointer to the current test.
  295. * @type {number}
  296. * @private
  297. */
  298. goog.testing.TestCase.prototype.currentTestPointer_ = 0;
  299. /**
  300. * Optional callback that will be executed when the test has finalized.
  301. * @type {Function}
  302. * @private
  303. */
  304. goog.testing.TestCase.prototype.onCompleteCallback_ = null;
  305. /**
  306. * Adds a new test to the test case.
  307. * @param {!goog.testing.TestCase.Test} test The test to add.
  308. */
  309. goog.testing.TestCase.prototype.add = function(test) {
  310. goog.asserts.assert(test);
  311. if (this.started) {
  312. throw Error(
  313. 'Tests cannot be added after execute() has been called. ' +
  314. 'Test: ' + test.name);
  315. }
  316. this.tests_.push(test);
  317. };
  318. /**
  319. * Creates and adds a new test.
  320. *
  321. * Convenience function to make syntax less awkward when not using automatic
  322. * test discovery.
  323. *
  324. * @param {string} name The test name.
  325. * @param {!Function} ref Reference to the test function.
  326. * @param {!Object=} opt_scope Optional scope that the test function should be
  327. * called in.
  328. */
  329. goog.testing.TestCase.prototype.addNewTest = function(name, ref, opt_scope) {
  330. var test = new goog.testing.TestCase.Test(name, ref, opt_scope || this);
  331. this.add(test);
  332. };
  333. /**
  334. * Sets the tests.
  335. * @param {!Array<goog.testing.TestCase.Test>} tests A new test array.
  336. * @protected
  337. */
  338. goog.testing.TestCase.prototype.setTests = function(tests) {
  339. this.tests_ = tests;
  340. };
  341. /**
  342. * Gets the tests.
  343. * @return {!Array<goog.testing.TestCase.Test>} The test array.
  344. */
  345. goog.testing.TestCase.prototype.getTests = function() {
  346. return this.tests_;
  347. };
  348. /**
  349. * Returns the number of tests contained in the test case.
  350. * @return {number} The number of tests.
  351. */
  352. goog.testing.TestCase.prototype.getCount = function() {
  353. return this.tests_.length;
  354. };
  355. /**
  356. * Returns the number of tests actually run in the test case, i.e. subtracting
  357. * any which are skipped.
  358. * @return {number} The number of un-ignored tests.
  359. */
  360. goog.testing.TestCase.prototype.getActuallyRunCount = function() {
  361. return this.testsToRun_ ? goog.object.getCount(this.testsToRun_) : 0;
  362. };
  363. /**
  364. * Returns the current test and increments the pointer.
  365. * @return {goog.testing.TestCase.Test} The current test case.
  366. */
  367. goog.testing.TestCase.prototype.next = function() {
  368. var test;
  369. while ((test = this.tests_[this.currentTestPointer_++])) {
  370. if (!this.testsToRun_ || this.testsToRun_[test.name] ||
  371. this.testsToRun_[this.currentTestPointer_ - 1]) {
  372. return test;
  373. }
  374. }
  375. return null;
  376. };
  377. /**
  378. * Resets the test case pointer, so that next returns the first test.
  379. */
  380. goog.testing.TestCase.prototype.reset = function() {
  381. this.currentTestPointer_ = 0;
  382. this.result_ = new goog.testing.TestCase.Result(this);
  383. };
  384. /**
  385. * Sets the callback function that should be executed when the tests have
  386. * completed.
  387. * @param {Function} fn The callback function.
  388. */
  389. goog.testing.TestCase.prototype.setCompletedCallback = function(fn) {
  390. this.onCompleteCallback_ = fn;
  391. };
  392. /**
  393. * @param {goog.testing.TestCase.Order} order The sort order for running tests.
  394. */
  395. goog.testing.TestCase.prototype.setOrder = function(order) {
  396. this.order = order;
  397. };
  398. /**
  399. * @param {Object<string, boolean>} testsToRun Set of tests to run. Entries in
  400. * the set may be test names, like "testFoo", or numeric indicies. Only
  401. * tests identified by name or by index will be executed.
  402. */
  403. goog.testing.TestCase.prototype.setTestsToRun = function(testsToRun) {
  404. this.testsToRun_ = testsToRun;
  405. };
  406. /**
  407. * Can be overridden in test classes to indicate whether the tests in a case
  408. * should be run in that particular situation. For example, this could be used
  409. * to stop tests running in a particular browser, where browser support for
  410. * the class under test was absent.
  411. * @return {boolean} Whether any of the tests in the case should be run.
  412. */
  413. goog.testing.TestCase.prototype.shouldRunTests = function() {
  414. return true;
  415. };
  416. /**
  417. * Executes the tests, yielding asynchronously if execution time exceeds
  418. * {@link maxRunTime}. There is no guarantee that the test case has finished
  419. * once this method has returned. To be notified when the test case
  420. * has finished, use {@link #setCompletedCallback} or
  421. * {@link #runTestsReturningPromise}.
  422. */
  423. goog.testing.TestCase.prototype.execute = function() {
  424. if (!this.prepareForRun_()) {
  425. return;
  426. }
  427. this.log('Starting tests: ' + this.name_);
  428. this.cycleTests();
  429. };
  430. /**
  431. * Sets up the internal state of the test case for a run.
  432. * @return {boolean} If false, preparation failed because the test case
  433. * is not supposed to run in the present environment.
  434. * @private
  435. */
  436. goog.testing.TestCase.prototype.prepareForRun_ = function() {
  437. this.started = true;
  438. this.reset();
  439. this.startTime_ = this.now();
  440. this.running = true;
  441. this.result_.totalCount = this.getCount();
  442. if (!this.shouldRunTests()) {
  443. this.log('shouldRunTests() returned false, skipping these tests.');
  444. this.result_.testSuppressed = true;
  445. this.finalize();
  446. return false;
  447. }
  448. return true;
  449. };
  450. /**
  451. * Finalizes the test case, called when the tests have finished executing.
  452. */
  453. goog.testing.TestCase.prototype.finalize = function() {
  454. this.saveMessage('Done');
  455. this.tearDownPage();
  456. this.endTime_ = this.now();
  457. this.running = false;
  458. this.result_.runTime = this.endTime_ - this.startTime_;
  459. this.result_.numFilesLoaded = this.countNumFilesLoaded_();
  460. this.result_.complete = true;
  461. this.log(this.result_.getSummary());
  462. if (this.result_.isSuccess()) {
  463. this.log('Tests complete');
  464. } else {
  465. this.log('Tests Failed');
  466. }
  467. if (this.onCompleteCallback_) {
  468. var fn = this.onCompleteCallback_;
  469. // Execute's the completed callback in the context of the global object.
  470. fn();
  471. this.onCompleteCallback_ = null;
  472. }
  473. };
  474. /**
  475. * Saves a message to the result set.
  476. * @param {string} message The message to save.
  477. */
  478. goog.testing.TestCase.prototype.saveMessage = function(message) {
  479. this.result_.messages.push(this.getTimeStamp_() + ' ' + message);
  480. };
  481. /**
  482. * @return {boolean} Whether the test case is running inside the multi test
  483. * runner.
  484. */
  485. goog.testing.TestCase.prototype.isInsideMultiTestRunner = function() {
  486. var top = goog.global['top'];
  487. return top && typeof top['_allTests'] != 'undefined';
  488. };
  489. /**
  490. * Logs an object to the console, if available.
  491. * @param {*} val The value to log. Will be ToString'd.
  492. */
  493. goog.testing.TestCase.prototype.log = function(val) {
  494. if (!this.isInsideMultiTestRunner() && goog.global.console) {
  495. if (typeof val == 'string') {
  496. val = this.getTimeStamp_() + ' : ' + val;
  497. }
  498. if (val instanceof Error && val.stack) {
  499. // Chrome does console.log asynchronously in a different process
  500. // (http://code.google.com/p/chromium/issues/detail?id=50316).
  501. // This is an acute problem for Errors, which almost never survive.
  502. // Grab references to the immutable strings so they survive.
  503. goog.global.console.log(val, val.message, val.stack);
  504. // TODO(gboyer): Consider for Chrome cloning any object if we can ensure
  505. // there are no circular references.
  506. } else {
  507. goog.global.console.log(val);
  508. }
  509. }
  510. };
  511. /**
  512. * @return {boolean} Whether the test was a success.
  513. */
  514. goog.testing.TestCase.prototype.isSuccess = function() {
  515. return !!this.result_ && this.result_.isSuccess();
  516. };
  517. /**
  518. * Returns a string detailing the results from the test.
  519. * @param {boolean=} opt_verbose If true results will include data about all
  520. * tests, not just what failed.
  521. * @return {string} The results from the test.
  522. */
  523. goog.testing.TestCase.prototype.getReport = function(opt_verbose) {
  524. var rv = [];
  525. if (this.running) {
  526. rv.push(this.name_ + ' [RUNNING]');
  527. } else if (this.result_.runCount == 0) {
  528. rv.push(this.name_ + ' [NO TESTS RUN]');
  529. } else {
  530. var label = this.result_.isSuccess() ? 'PASSED' : 'FAILED';
  531. rv.push(this.name_ + ' [' + label + ']');
  532. }
  533. if (goog.global.location) {
  534. rv.push(this.trimPath_(goog.global.location.href));
  535. }
  536. rv.push(this.result_.getSummary());
  537. if (opt_verbose) {
  538. rv.push('.', this.result_.messages.join('\n'));
  539. } else if (!this.result_.isSuccess()) {
  540. rv.push(this.result_.errors.join('\n'));
  541. }
  542. rv.push(' ');
  543. return rv.join('\n');
  544. };
  545. /**
  546. * Returns the test results.
  547. * @return {!goog.testing.TestCase.Result}
  548. * @package
  549. */
  550. goog.testing.TestCase.prototype.getResult = function() {
  551. return this.result_;
  552. };
  553. /**
  554. * Returns the amount of time it took for the test to run.
  555. * @return {number} The run time, in milliseconds.
  556. */
  557. goog.testing.TestCase.prototype.getRunTime = function() {
  558. return this.result_.runTime;
  559. };
  560. /**
  561. * Returns the number of script files that were loaded in order to run the test.
  562. * @return {number} The number of script files.
  563. */
  564. goog.testing.TestCase.prototype.getNumFilesLoaded = function() {
  565. return this.result_.numFilesLoaded;
  566. };
  567. /**
  568. * Represents a test result.
  569. * @typedef {{
  570. * 'source': string,
  571. * 'message': string,
  572. * 'stacktrace': string
  573. * }}
  574. */
  575. goog.testing.TestCase.IResult;
  576. /**
  577. * Returns the test results object: a map from test names to a list of test
  578. * failures (if any exist).
  579. * @return {!Object<string, !Array<goog.testing.TestCase.IResult>>} Test
  580. * results object.
  581. */
  582. goog.testing.TestCase.prototype.getTestResults = function() {
  583. var map = {};
  584. goog.object.forEach(this.result_.resultsByName, function(resultArray, key) {
  585. // Make sure we only use properties on the actual map
  586. if (!Object.prototype.hasOwnProperty.call(
  587. this.result_.resultsByName, key)) {
  588. return;
  589. }
  590. map[key] = [];
  591. for (var j = 0; j < resultArray.length; j++) {
  592. map[key].push(resultArray[j].toObject_());
  593. }
  594. }, this);
  595. return map;
  596. };
  597. /**
  598. * Returns the test results as json.
  599. * This is called by the testing infrastructure through G_testrunner.
  600. * @return {string} Tests results object.
  601. */
  602. goog.testing.TestCase.prototype.getTestResultsAsJson = function() {
  603. return goog.json.serialize(this.getTestResults());
  604. };
  605. /**
  606. * Executes each of the tests, yielding asynchronously if execution time
  607. * exceeds {@link #maxRunTime}. There is no guarantee that the test case
  608. * has finished execution once this method has returned.
  609. * To be notified when the test case has finished execution, use
  610. * {@link #setCompletedCallback} or {@link #runTestsReturningPromise}.
  611. *
  612. * Overridable by the individual test case. This allows test cases to defer
  613. * when the test is actually started. If overridden, finalize must be called
  614. * by the test to indicate it has finished.
  615. */
  616. goog.testing.TestCase.prototype.runTests = function() {
  617. goog.testing.Continuation_.run(this.runSetUpPage_(this.execute));
  618. };
  619. /**
  620. * Executes each of the tests, returning a promise that resolves with the
  621. * test results once they are done running.
  622. * @return {!IThenable<!goog.testing.TestCase.Result>}
  623. * @final
  624. * @package
  625. */
  626. goog.testing.TestCase.prototype.runTestsReturningPromise = function() {
  627. return new goog.Promise(function(resolve) {
  628. goog.testing.Continuation_.run(this.runSetUpPage_(function() {
  629. if (!this.prepareForRun_()) {
  630. resolve(this.result_);
  631. return;
  632. }
  633. this.log('Starting tests: ' + this.name_);
  634. this.saveMessage('Start');
  635. this.batchTime_ = this.now();
  636. this.runNextTestCallback_ = resolve;
  637. goog.testing.Continuation_.run(this.runNextTest_());
  638. }));
  639. }, this);
  640. };
  641. /**
  642. * Runs the setUpPage methods.
  643. * @param {function(this:goog.testing.TestCase)} runTestsFn Callback to invoke
  644. * after setUpPage has completed.
  645. * @return {?goog.testing.Continuation_}
  646. * @private
  647. */
  648. goog.testing.TestCase.prototype.runSetUpPage_ = function(runTestsFn) {
  649. return this.invokeTestFunction_(this.setUpPage, runTestsFn, function(e) {
  650. this.exceptionBeforeTest = e;
  651. runTestsFn.call(this);
  652. }, 'setUpPage');
  653. };
  654. /**
  655. * Executes the next test method synchronously or with promises, depending on
  656. * the test method's return value.
  657. *
  658. * If the test method returns a promise, the next test method will run once
  659. * the promise is resolved or rejected. If the test method does not
  660. * return a promise, it is assumed to be synchronous, and execution proceeds
  661. * immediately to the next test method. This means that test cases can run
  662. * partially synchronously and partially asynchronously, depending on
  663. * the return values of their test methods. In particular, a test case
  664. * executes synchronously until the first promise is returned from a
  665. * test method (or until a resource limit is reached; see
  666. * {@link finishTestInvocation_}).
  667. * @return {?goog.testing.Continuation_}
  668. * @private
  669. */
  670. goog.testing.TestCase.prototype.runNextTest_ = function() {
  671. this.curTest_ = this.next();
  672. if (!this.curTest_ || !this.running) {
  673. this.finalize();
  674. return new goog.testing.Continuation_(
  675. goog.bind(this.runNextTestCallback_, this, this.result_));
  676. }
  677. this.curTest_.started();
  678. this.result_.runCount++;
  679. this.log('Running test: ' + this.curTest_.name);
  680. if (this.maybeFailTestEarly(this.curTest_)) {
  681. return new goog.testing.Continuation_(
  682. goog.bind(this.finishTestInvocation_, this));
  683. }
  684. goog.testing.TestCase.currentTestName = this.curTest_.name;
  685. return this.invokeTestFunction_(
  686. this.setUp, this.safeRunTest_, this.safeTearDown_, 'setUp');
  687. };
  688. /**
  689. * Calls the given test function, handling errors appropriately.
  690. * @return {?goog.testing.Continuation_}
  691. * @private
  692. */
  693. goog.testing.TestCase.prototype.safeRunTest_ = function() {
  694. return this.invokeTestFunction_(
  695. goog.bind(this.curTest_.ref, this.curTest_.scope), this.safeTearDown_,
  696. this.safeTearDown_, this.curTest_.name);
  697. };
  698. /**
  699. * Calls {@link tearDown}, handling errors appropriately.
  700. * @param {*=} opt_error Error associated with the test, if any.
  701. * @return {?goog.testing.Continuation_}
  702. * @private
  703. */
  704. goog.testing.TestCase.prototype.safeTearDown_ = function(opt_error) {
  705. if (arguments.length == 1) {
  706. this.doError(this.curTest_, opt_error);
  707. }
  708. return this.invokeTestFunction_(
  709. this.tearDown, this.finishTestInvocation_, this.finishTestInvocation_,
  710. 'tearDown');
  711. };
  712. /**
  713. * Calls the given {@code fn}, then calls either {@code onSuccess} or
  714. * {@code onFailure}, either synchronously or using promises, depending on
  715. * {@code fn}'s return value.
  716. *
  717. * If {@code fn} throws an exception, {@code onFailure} is called immediately
  718. * with the exception.
  719. *
  720. * If {@code fn} returns a promise, and the promise is eventually resolved,
  721. * {@code onSuccess} is called with no arguments. If the promise is eventually
  722. * rejected, {@code onFailure} is called with the rejection reason.
  723. *
  724. * Otherwise, if {@code fn} neither returns a promise nor throws an exception,
  725. * {@code onSuccess} is called immediately with no arguments.
  726. *
  727. * {@code fn}, {@code onSuccess}, and {@code onFailure} are all called with
  728. * the TestCase instance as the method receiver.
  729. *
  730. * @param {function()} fn The function to call.
  731. * @param {function(): (?goog.testing.Continuation_|undefined)} onSuccess
  732. * @param {function(*): (?goog.testing.Continuation_|undefined)} onFailure
  733. * @param {string} fnName Name of the function being invoked e.g. 'setUp'.
  734. * @return {?goog.testing.Continuation_}
  735. * @private
  736. */
  737. goog.testing.TestCase.prototype.invokeTestFunction_ = function(
  738. fn, onSuccess, onFailure, fnName) {
  739. var testCase = this;
  740. this.thrownAssertionExceptions_ = [];
  741. try {
  742. var retval = fn.call(this);
  743. if (goog.Thenable.isImplementedBy(retval) ||
  744. goog.isFunction(retval && retval['then'])) {
  745. // Resolve Thenable into a proper Promise to avoid hard to debug problems.
  746. var promise = goog.Promise.resolve(retval);
  747. var self = this;
  748. promise = this.rejectIfPromiseTimesOut_(
  749. promise, self.promiseTimeout,
  750. 'Timed out while waiting for a promise returned from ' + fnName +
  751. ' to resolve. Set goog.testing.TestCase.getActiveTestCase()' +
  752. '.promiseTimeout to adjust the timeout.');
  753. promise.then(
  754. function() {
  755. self.resetBatchTimeAfterPromise_();
  756. if (testCase.thrownAssertionExceptions_.length == 0) {
  757. goog.testing.Continuation_.run(onSuccess.call(self));
  758. } else {
  759. goog.testing.Continuation_.run(onFailure.call(
  760. self,
  761. testCase.reportUnpropagatedAssertionExceptions_(fnName)));
  762. }
  763. },
  764. function(e) {
  765. self.resetBatchTimeAfterPromise_();
  766. goog.testing.Continuation_.run(onFailure.call(self, e));
  767. });
  768. return null;
  769. } else {
  770. if (this.thrownAssertionExceptions_.length == 0) {
  771. return new goog.testing.Continuation_(goog.bind(onSuccess, this));
  772. } else {
  773. return new goog.testing.Continuation_(goog.bind(
  774. onFailure, this,
  775. this.reportUnpropagatedAssertionExceptions_(fnName)));
  776. }
  777. }
  778. } catch (e) {
  779. return new goog.testing.Continuation_(goog.bind(onFailure, this, e));
  780. }
  781. };
  782. /**
  783. * Logs all of the exceptions generated from failing assertions, and returns a
  784. * generic exception informing the user that one or more exceptions were not
  785. * propagated, causing the test to erroneously pass.
  786. * @param {string} testName The test function's name.
  787. * @return {!goog.testing.JsUnitException}
  788. * @private
  789. */
  790. goog.testing.TestCase.prototype.reportUnpropagatedAssertionExceptions_ =
  791. function(testName) {
  792. var numExceptions = this.thrownAssertionExceptions_.length;
  793. for (var i = 0; i < numExceptions; i++) {
  794. this.recordError_(testName, this.thrownAssertionExceptions_[i]);
  795. }
  796. return new goog.testing.JsUnitException(
  797. 'One or more assertions were raised but not caught by the testing ' +
  798. 'framework. These assertions may have been unintentionally captured ' +
  799. 'by a catch block or a thenCatch resolution of a Promise.');
  800. };
  801. /**
  802. * Resets the batch run timer. This should only be called after resolving a
  803. * promise since Promise.then() has an implicit yield.
  804. * @private
  805. */
  806. goog.testing.TestCase.prototype.resetBatchTimeAfterPromise_ = function() {
  807. this.batchTime_ = this.now();
  808. };
  809. /**
  810. * Finishes up bookkeeping for the current test function, and schedules
  811. * the next test function to run, either immediately or asychronously.
  812. * @param {*=} opt_error Optional error resulting from the test invocation.
  813. * @return {?goog.testing.Continuation_}
  814. * @private
  815. */
  816. goog.testing.TestCase.prototype.finishTestInvocation_ = function(opt_error) {
  817. if (arguments.length == 1) {
  818. this.doError(this.curTest_, opt_error);
  819. }
  820. // If no errors have been recorded for the test, it is a success.
  821. if (!(this.curTest_.name in this.result_.resultsByName) ||
  822. !this.result_.resultsByName[this.curTest_.name].length) {
  823. this.doSuccess(this.curTest_);
  824. }
  825. goog.testing.TestCase.currentTestName = null;
  826. // If the test case has consumed too much time or stack space,
  827. // yield to avoid blocking the browser. Otherwise, proceed to the next test.
  828. if (this.now() - this.batchTime_ > goog.testing.TestCase.maxRunTime) {
  829. this.saveMessage('Breaking async');
  830. this.timeout(goog.bind(this.startNextBatch_, this), 0);
  831. return null;
  832. } else {
  833. return new goog.testing.Continuation_(goog.bind(this.runNextTest_, this));
  834. }
  835. };
  836. /**
  837. * Start a new batch to tests after yielding, resetting batchTime and depth.
  838. * @private
  839. */
  840. goog.testing.TestCase.prototype.startNextBatch_ = function() {
  841. this.batchTime_ = this.now();
  842. goog.testing.Continuation_.run(this.runNextTest_());
  843. };
  844. /**
  845. * Reorders the tests depending on the {@code order} field.
  846. * @private
  847. */
  848. goog.testing.TestCase.prototype.orderTests_ = function() {
  849. switch (this.order) {
  850. case goog.testing.TestCase.Order.RANDOM:
  851. // Fisher-Yates shuffle
  852. var i = this.tests_.length;
  853. while (i > 1) {
  854. // goog.math.randomInt is inlined to reduce dependencies.
  855. var j = Math.floor(Math.random() * i); // exclusive
  856. i--;
  857. var tmp = this.tests_[i];
  858. this.tests_[i] = this.tests_[j];
  859. this.tests_[j] = tmp;
  860. }
  861. break;
  862. case goog.testing.TestCase.Order.SORTED:
  863. this.tests_.sort(function(t1, t2) {
  864. if (t1.name == t2.name) {
  865. return 0;
  866. }
  867. return t1.name < t2.name ? -1 : 1;
  868. });
  869. break;
  870. // Do nothing for NATURAL.
  871. }
  872. };
  873. /**
  874. * Gets list of objects that potentially contain test cases. For IE 8 and below,
  875. * this is the global "this" (for properties set directly on the global this or
  876. * window) and the RuntimeObject (for global variables and functions). For all
  877. * other browsers, the array simply contains the global this.
  878. *
  879. * @param {string=} opt_prefix An optional prefix. If specified, only get things
  880. * under this prefix. Note that the prefix is only honored in IE, since it
  881. * supports the RuntimeObject:
  882. * http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
  883. * TODO: Remove this option.
  884. * @return {!Array<!Object>} A list of objects that should be inspected.
  885. */
  886. goog.testing.TestCase.prototype.getGlobals = function(opt_prefix) {
  887. return goog.testing.TestCase.getGlobals(opt_prefix);
  888. };
  889. /**
  890. * Gets list of objects that potentially contain test cases. For IE 8 and below,
  891. * this is the global "this" (for properties set directly on the global this or
  892. * window) and the RuntimeObject (for global variables and functions). For all
  893. * other browsers, the array simply contains the global this.
  894. *
  895. * @param {string=} opt_prefix An optional prefix. If specified, only get things
  896. * under this prefix. Note that the prefix is only honored in IE, since it
  897. * supports the RuntimeObject:
  898. * http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
  899. * TODO: Remove this option.
  900. * @return {!Array<!Object>} A list of objects that should be inspected.
  901. */
  902. goog.testing.TestCase.getGlobals = function(opt_prefix) {
  903. // Look in the global scope for most browsers, on IE we use the little known
  904. // RuntimeObject which holds references to all globals. We reference this
  905. // via goog.global so that there isn't an aliasing that throws an exception
  906. // in Firefox.
  907. return typeof goog.global['RuntimeObject'] != 'undefined' ?
  908. [goog.global['RuntimeObject']((opt_prefix || '') + '*'), goog.global] :
  909. [goog.global];
  910. };
  911. /**
  912. * @private {?goog.testing.TestCase}
  913. */
  914. goog.testing.TestCase.activeTestCase_ = null;
  915. /**
  916. * @return {?goog.testing.TestCase} currently active test case or null if not
  917. * test is currently running. Tries the G_testRunner first then the stored
  918. * value (when run outside of G_testRunner.
  919. */
  920. goog.testing.TestCase.getActiveTestCase = function() {
  921. var gTestRunner = goog.global['G_testRunner'];
  922. if (gTestRunner && gTestRunner.testCase) {
  923. return gTestRunner.testCase;
  924. } else {
  925. return goog.testing.TestCase.activeTestCase_;
  926. }
  927. };
  928. /**
  929. * Calls {@link goog.testing.TestCase.prototype.invalidateAssertionException} on
  930. * the active test case if it is installed, and logs an error otherwise.
  931. * @param {!goog.testing.JsUnitException} e The exception object to invalidate.
  932. * @package
  933. */
  934. goog.testing.TestCase.invalidateAssertionException = function(e) {
  935. var testCase = goog.testing.TestCase.getActiveTestCase();
  936. if (testCase) {
  937. testCase.invalidateAssertionException(e);
  938. } else {
  939. goog.global.console.error(
  940. 'Failed to remove expected exception: no test case is installed.');
  941. }
  942. };
  943. /**
  944. * Gets called before any tests are executed. Can be overridden to set up the
  945. * environment for the whole test case.
  946. * @return {!Thenable|undefined}
  947. */
  948. goog.testing.TestCase.prototype.setUpPage = function() {};
  949. /**
  950. * Gets called after all tests have been executed. Can be overridden to tear
  951. * down the entire test case.
  952. */
  953. goog.testing.TestCase.prototype.tearDownPage = function() {};
  954. /**
  955. * Gets called before every goog.testing.TestCase.Test is been executed. Can be
  956. * overridden to add set up functionality to each test.
  957. * @return {!Thenable|undefined}
  958. */
  959. goog.testing.TestCase.prototype.setUp = function() {};
  960. /**
  961. * Gets called after every goog.testing.TestCase.Test has been executed. Can be
  962. * overriden to add tear down functionality to each test.
  963. * @return {!Thenable|undefined}
  964. */
  965. goog.testing.TestCase.prototype.tearDown = function() {};
  966. /**
  967. * @return {string} The function name prefix used to auto-discover tests.
  968. */
  969. goog.testing.TestCase.prototype.getAutoDiscoveryPrefix = function() {
  970. return 'test';
  971. };
  972. /**
  973. * @return {number} Time since the last batch of tests was started.
  974. * @protected
  975. */
  976. goog.testing.TestCase.prototype.getBatchTime = function() {
  977. return this.batchTime_;
  978. };
  979. /**
  980. * @param {number} batchTime Time since the last batch of tests was started.
  981. * @protected
  982. */
  983. goog.testing.TestCase.prototype.setBatchTime = function(batchTime) {
  984. this.batchTime_ = batchTime;
  985. };
  986. /**
  987. * Creates a {@code goog.testing.TestCase.Test} from an auto-discovered
  988. * function.
  989. * @param {string} name The name of the function.
  990. * @param {function() : void} ref The auto-discovered function.
  991. * @return {!goog.testing.TestCase.Test} The newly created test.
  992. * @protected
  993. */
  994. goog.testing.TestCase.prototype.createTestFromAutoDiscoveredFunction = function(
  995. name, ref) {
  996. return new goog.testing.TestCase.Test(name, ref, goog.global);
  997. };
  998. /**
  999. * Adds any functions defined on 'obj' (the global object, by default)
  1000. * that correspond to lifecycle events for the test case. Overrides
  1001. * setUp, tearDown, setUpPage, tearDownPage, runTests, and shouldRunTests
  1002. * if they are defined on 'obj'.
  1003. * @param {!Object=} opt_obj Defaults to goog.global.
  1004. */
  1005. goog.testing.TestCase.prototype.autoDiscoverLifecycle = function(opt_obj) {
  1006. var obj = opt_obj || goog.global;
  1007. if (obj['setUp']) {
  1008. this.setUp = goog.bind(obj['setUp'], obj);
  1009. }
  1010. if (obj['tearDown']) {
  1011. this.tearDown = goog.bind(obj['tearDown'], obj);
  1012. }
  1013. if (obj['setUpPage']) {
  1014. this.setUpPage = goog.bind(obj['setUpPage'], obj);
  1015. }
  1016. if (obj['tearDownPage']) {
  1017. this.tearDownPage = goog.bind(obj['tearDownPage'], obj);
  1018. }
  1019. if (obj['runTests']) {
  1020. this.runTests = goog.bind(obj['runTests'], obj);
  1021. }
  1022. if (obj['shouldRunTests']) {
  1023. this.shouldRunTests = goog.bind(obj['shouldRunTests'], obj);
  1024. }
  1025. };
  1026. // TODO(johnlenz): make this package private
  1027. /**
  1028. * @param {!Object} obj An object from which to extract test and lifecycle
  1029. * methods.
  1030. */
  1031. goog.testing.TestCase.prototype.setTestObj = function(obj) {
  1032. // Drop any previously added (likely auto-discovered) tests, only one source
  1033. // of discovered test and life-cycle methods is allowed.
  1034. goog.asserts.assert(
  1035. this.tests_.length == 0, 'Test methods have already been configured.');
  1036. var regex = new RegExp('^' + this.getAutoDiscoveryPrefix());
  1037. var properties = goog.object.getAllPropertyNames(obj);
  1038. for (var i = 0; i < properties.length; i++) {
  1039. var name = properties[i];
  1040. if (regex.test(name)) {
  1041. var testMethod = obj[name];
  1042. if (goog.isFunction(testMethod)) {
  1043. this.addNewTest(name, testMethod, obj);
  1044. }
  1045. }
  1046. }
  1047. if (obj['getTestName']) {
  1048. this.name_ = obj['getTestName']();
  1049. }
  1050. this.autoDiscoverLifecycle(obj);
  1051. };
  1052. /**
  1053. * Adds any functions defined in the global scope that are prefixed with "test"
  1054. * to the test case.
  1055. */
  1056. goog.testing.TestCase.prototype.autoDiscoverTests = function() {
  1057. var prefix = this.getAutoDiscoveryPrefix();
  1058. var testSources = this.getGlobals(prefix);
  1059. var foundTests = [];
  1060. for (var i = 0; i < testSources.length; i++) {
  1061. var testSource = testSources[i];
  1062. for (var name in testSource) {
  1063. if ((new RegExp('^' + prefix)).test(name)) {
  1064. var ref;
  1065. try {
  1066. ref = testSource[name];
  1067. } catch (ex) {
  1068. // NOTE(brenneman): When running tests from a file:// URL on Firefox
  1069. // 3.5 for Windows, any reference to goog.global.sessionStorage raises
  1070. // an "Operation is not supported" exception. Ignore any exceptions
  1071. // raised by simply accessing global properties.
  1072. ref = undefined;
  1073. }
  1074. if (goog.isFunction(ref)) {
  1075. foundTests.push(this.createTestFromAutoDiscoveredFunction(name, ref));
  1076. }
  1077. }
  1078. }
  1079. }
  1080. for (var i = 0; i < foundTests.length; i++) {
  1081. this.add(foundTests[i]);
  1082. }
  1083. this.orderTests_();
  1084. this.log(this.getCount() + ' tests auto-discovered');
  1085. // TODO(user): Do this as a separate call. Unfortunately, a lot of projects
  1086. // currently override autoDiscoverTests and expect lifecycle events to be
  1087. // registered as a part of this call.
  1088. this.autoDiscoverLifecycle();
  1089. };
  1090. /**
  1091. * Checks to see if the test should be marked as failed before it is run.
  1092. *
  1093. * If there was an error in setUpPage, we treat that as a failure for all tests
  1094. * and mark them all as having failed.
  1095. *
  1096. * @param {goog.testing.TestCase.Test} testCase The current test case.
  1097. * @return {boolean} Whether the test was marked as failed.
  1098. * @protected
  1099. */
  1100. goog.testing.TestCase.prototype.maybeFailTestEarly = function(testCase) {
  1101. if (this.exceptionBeforeTest) {
  1102. // We just use the first error to report an error on a failed test.
  1103. testCase.name = 'setUpPage for ' + testCase.name;
  1104. this.doError(testCase, this.exceptionBeforeTest);
  1105. return true;
  1106. }
  1107. return false;
  1108. };
  1109. /**
  1110. * Cycles through the tests, yielding asynchronously if the execution time
  1111. * exceeds {@link #maxRunTime}. In particular, there is no guarantee that
  1112. * the test case has finished execution once this method has returned.
  1113. * To be notified when the test case has finished execution, use
  1114. * {@link #setCompletedCallback} or {@link #runTestsReturningPromise}.
  1115. */
  1116. goog.testing.TestCase.prototype.cycleTests = function() {
  1117. this.saveMessage('Start');
  1118. this.batchTime_ = this.now();
  1119. if (this.running) {
  1120. this.runNextTestCallback_ = goog.nullFunction;
  1121. // Kick off the tests. runNextTest_ will schedule all of the tests,
  1122. // using a mixture of synchronous and asynchronous strategies.
  1123. goog.testing.Continuation_.run(this.runNextTest_());
  1124. }
  1125. };
  1126. /**
  1127. * Counts the number of files that were loaded for dependencies that are
  1128. * required to run the test.
  1129. * @return {number} The number of files loaded.
  1130. * @private
  1131. */
  1132. goog.testing.TestCase.prototype.countNumFilesLoaded_ = function() {
  1133. var scripts = goog.dom.getElementsByTagName(goog.dom.TagName.SCRIPT);
  1134. var count = 0;
  1135. for (var i = 0, n = scripts.length; i < n; i++) {
  1136. if (scripts[i].src) {
  1137. count++;
  1138. }
  1139. }
  1140. return count;
  1141. };
  1142. /**
  1143. * Calls a function after a delay, using the protected timeout.
  1144. * @param {Function} fn The function to call.
  1145. * @param {number} time Delay in milliseconds.
  1146. * @return {number} The timeout id.
  1147. * @protected
  1148. */
  1149. goog.testing.TestCase.prototype.timeout = function(fn, time) {
  1150. // NOTE: invoking protectedSetTimeout_ as a member of goog.testing.TestCase
  1151. // would result in an Illegal Invocation error. The method must be executed
  1152. // with the global context.
  1153. var protectedSetTimeout = goog.testing.TestCase.protectedSetTimeout_;
  1154. return protectedSetTimeout(fn, time);
  1155. };
  1156. /**
  1157. * Clears a timeout created by {@code this.timeout()}.
  1158. * @param {number} id A timeout id.
  1159. * @protected
  1160. */
  1161. goog.testing.TestCase.prototype.clearTimeout = function(id) {
  1162. // NOTE: see execution note for protectedSetTimeout above.
  1163. var protectedClearTimeout = goog.testing.TestCase.protectedClearTimeout_;
  1164. protectedClearTimeout(id);
  1165. };
  1166. /**
  1167. * @return {number} The current time in milliseconds.
  1168. * @protected
  1169. */
  1170. goog.testing.TestCase.prototype.now = function() {
  1171. return goog.testing.TestCase.now();
  1172. };
  1173. /**
  1174. * @return {number} The current time in milliseconds.
  1175. * @protected
  1176. */
  1177. goog.testing.TestCase.now = function() {
  1178. // don't use goog.now as some tests override it.
  1179. if (goog.testing.TestCase.protectedPerformance_) {
  1180. return goog.testing.TestCase.protectedPerformance_.now();
  1181. }
  1182. // Fallback for IE8
  1183. // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
  1184. var protectedDate = goog.testing.TestCase.protectedDate_;
  1185. return new protectedDate().getTime();
  1186. };
  1187. /**
  1188. * Returns the current time.
  1189. * @return {string} HH:MM:SS.
  1190. * @private
  1191. */
  1192. goog.testing.TestCase.prototype.getTimeStamp_ = function() {
  1193. // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
  1194. var protectedDate = goog.testing.TestCase.protectedDate_;
  1195. var d = new protectedDate();
  1196. // Ensure millis are always 3-digits
  1197. var millis = '00' + d.getMilliseconds();
  1198. millis = millis.substr(millis.length - 3);
  1199. return this.pad_(d.getHours()) + ':' + this.pad_(d.getMinutes()) + ':' +
  1200. this.pad_(d.getSeconds()) + '.' + millis;
  1201. };
  1202. /**
  1203. * Pads a number to make it have a leading zero if it's less than 10.
  1204. * @param {number} number The number to pad.
  1205. * @return {string} The resulting string.
  1206. * @private
  1207. */
  1208. goog.testing.TestCase.prototype.pad_ = function(number) {
  1209. return number < 10 ? '0' + number : String(number);
  1210. };
  1211. /**
  1212. * Trims a path to be only that after google3.
  1213. * @param {string} path The path to trim.
  1214. * @return {string} The resulting string.
  1215. * @private
  1216. */
  1217. goog.testing.TestCase.prototype.trimPath_ = function(path) {
  1218. return path.substring(path.indexOf('google3') + 8);
  1219. };
  1220. /**
  1221. * Handles a test that passed.
  1222. * @param {goog.testing.TestCase.Test} test The test that passed.
  1223. * @protected
  1224. */
  1225. goog.testing.TestCase.prototype.doSuccess = function(test) {
  1226. this.result_.successCount++;
  1227. // An empty list of error messages indicates that the test passed.
  1228. // If we already have a failure for this test, do not set to empty list.
  1229. if (!(test.name in this.result_.resultsByName)) {
  1230. this.result_.resultsByName[test.name] = [];
  1231. }
  1232. var message = test.name + ' : PASSED';
  1233. this.saveMessage(message);
  1234. this.log(message);
  1235. if (this.testDone_) {
  1236. this.doTestDone_(test, []);
  1237. }
  1238. };
  1239. /**
  1240. * Records and logs a test failure.
  1241. * @param {string} testName The name of the test that failed.
  1242. * @param {*=} opt_e The exception object associated with the
  1243. * failure or a string.
  1244. * @private
  1245. */
  1246. goog.testing.TestCase.prototype.recordError_ = function(testName, opt_e) {
  1247. var message = testName + ' : FAILED';
  1248. this.log(message);
  1249. this.saveMessage(message);
  1250. var err = this.logError(testName, opt_e);
  1251. this.result_.errors.push(err);
  1252. if (testName in this.result_.resultsByName) {
  1253. this.result_.resultsByName[testName].push(err);
  1254. } else {
  1255. this.result_.resultsByName[testName] = [err];
  1256. }
  1257. };
  1258. /**
  1259. * Handles a test that failed.
  1260. * @param {goog.testing.TestCase.Test} test The test that failed.
  1261. * @param {*=} opt_e The exception object associated with the
  1262. * failure or a string.
  1263. * @protected
  1264. */
  1265. goog.testing.TestCase.prototype.doError = function(test, opt_e) {
  1266. if (!test || !test.name) {
  1267. console.error('no name!' + opt_e);
  1268. }
  1269. this.recordError_(test.name, opt_e);
  1270. if (this.testDone_) {
  1271. var results = this.result_.resultsByName[test.name];
  1272. var errMsgs = [];
  1273. for (var i = 0; i < results.length; i++) {
  1274. errMsgs.push(results[i].toString());
  1275. }
  1276. this.doTestDone_(test, errMsgs);
  1277. }
  1278. };
  1279. /**
  1280. * Makes note of an exception arising from an assertion, and then throws it. If
  1281. * the test otherwise passes (i.e., because something else caught the exception
  1282. * on its way to the test framework), it will be forced to fail.
  1283. * @param {!goog.testing.JsUnitException} e The exception object being thrown.
  1284. * @throws {goog.testing.JsUnitException}
  1285. * @package
  1286. */
  1287. goog.testing.TestCase.prototype.raiseAssertionException = function(e) {
  1288. if (this.failOnUnreportedAsserts) {
  1289. this.thrownAssertionExceptions_.push(e);
  1290. }
  1291. throw e;
  1292. };
  1293. /**
  1294. * Removes the specified exception from being tracked. This only needs to be
  1295. * called for internal functions that intentionally catch an exception, such as
  1296. * {@code #assertThrowsJsUnitException}.
  1297. * @param {!goog.testing.JsUnitException} e The exception object to invalidate.
  1298. * @package
  1299. */
  1300. goog.testing.TestCase.prototype.invalidateAssertionException = function(e) {
  1301. if (this.failOnUnreportedAsserts) {
  1302. goog.array.remove(this.thrownAssertionExceptions_, e);
  1303. }
  1304. };
  1305. /**
  1306. * @param {string} name Failed test name.
  1307. * @param {*=} opt_e The exception object associated with the
  1308. * failure or a string.
  1309. * @return {!goog.testing.TestCase.Error} Error object.
  1310. */
  1311. goog.testing.TestCase.prototype.logError = function(name, opt_e) {
  1312. var errMsg = null;
  1313. var stack = null;
  1314. if (opt_e) {
  1315. this.log(opt_e);
  1316. if (goog.isString(opt_e)) {
  1317. errMsg = opt_e;
  1318. } else {
  1319. errMsg = opt_e.message || opt_e.description || opt_e.toString();
  1320. stack = opt_e.stack ? opt_e.stack : opt_e['stackTrace'];
  1321. }
  1322. } else {
  1323. errMsg = 'An unknown error occurred';
  1324. }
  1325. if (stack) {
  1326. // Remove extra goog.testing.TestCase frames from the end.
  1327. stack = stack.replace(
  1328. /\n.*goog\.testing\.TestCase\.(prototype\.)?invokeTestFunction[^\0]*/m,
  1329. '');
  1330. }
  1331. var err = new goog.testing.TestCase.Error(name, errMsg, stack);
  1332. // Avoid double logging.
  1333. if (!opt_e || !opt_e['isJsUnitException'] ||
  1334. !opt_e['loggedJsUnitException']) {
  1335. this.saveMessage(err.toString());
  1336. }
  1337. if (opt_e && opt_e['isJsUnitException']) {
  1338. opt_e['loggedJsUnitException'] = true;
  1339. }
  1340. return err;
  1341. };
  1342. /**
  1343. * A class representing a single test function.
  1344. * @param {string} name The test name.
  1345. * @param {Function} ref Reference to the test function.
  1346. * @param {Object=} opt_scope Optional scope that the test function should be
  1347. * called in.
  1348. * @constructor
  1349. */
  1350. goog.testing.TestCase.Test = function(name, ref, opt_scope) {
  1351. /**
  1352. * The name of the test.
  1353. * @type {string}
  1354. */
  1355. this.name = name;
  1356. /**
  1357. * Reference to the test function.
  1358. * @type {Function}
  1359. */
  1360. this.ref = ref;
  1361. /**
  1362. * Scope that the test function should be called in.
  1363. * @type {Object}
  1364. */
  1365. this.scope = opt_scope || null;
  1366. /**
  1367. * Timestamp just before the test begins execution.
  1368. * @type {number}
  1369. * @private
  1370. */
  1371. this.startTime_;
  1372. /**
  1373. * Timestamp just after the test ends execution.
  1374. * @type {number}
  1375. * @private
  1376. */
  1377. this.stoppedTime_;
  1378. };
  1379. /**
  1380. * Executes the test function.
  1381. * @package
  1382. */
  1383. goog.testing.TestCase.Test.prototype.execute = function() {
  1384. this.ref.call(this.scope);
  1385. };
  1386. /**
  1387. * Sets the start time
  1388. */
  1389. goog.testing.TestCase.Test.prototype.started = function() {
  1390. this.startTime_ = goog.testing.TestCase.now();
  1391. };
  1392. /**
  1393. * Sets the stop time
  1394. */
  1395. goog.testing.TestCase.Test.prototype.stopped = function() {
  1396. this.stoppedTime_ = goog.testing.TestCase.now();
  1397. };
  1398. /**
  1399. * Returns the runtime for this test function
  1400. * @return {number} milliseconds takenn by the test.
  1401. */
  1402. goog.testing.TestCase.Test.prototype.getElapsedTime = function() {
  1403. return this.stoppedTime_ - this.startTime_;
  1404. };
  1405. /**
  1406. * A class for representing test results. A bag of public properties.
  1407. * @param {goog.testing.TestCase} testCase The test case that owns this result.
  1408. * @constructor
  1409. * @final
  1410. */
  1411. goog.testing.TestCase.Result = function(testCase) {
  1412. /**
  1413. * The test case that owns this result.
  1414. * @type {goog.testing.TestCase}
  1415. * @private
  1416. */
  1417. this.testCase_ = testCase;
  1418. /**
  1419. * Total number of tests that should have been run.
  1420. * @type {number}
  1421. */
  1422. this.totalCount = 0;
  1423. /**
  1424. * Total number of tests that were actually run.
  1425. * @type {number}
  1426. */
  1427. this.runCount = 0;
  1428. /**
  1429. * Number of successful tests.
  1430. * @type {number}
  1431. */
  1432. this.successCount = 0;
  1433. /**
  1434. * The amount of time the tests took to run.
  1435. * @type {number}
  1436. */
  1437. this.runTime = 0;
  1438. /**
  1439. * The number of files loaded to run this test.
  1440. * @type {number}
  1441. */
  1442. this.numFilesLoaded = 0;
  1443. /**
  1444. * Whether this test case was suppressed by shouldRunTests() returning false.
  1445. * @type {boolean}
  1446. */
  1447. this.testSuppressed = false;
  1448. /**
  1449. * Test results for each test that was run. The test name is always added
  1450. * as the key in the map, and the array of strings is an optional list
  1451. * of failure messages. If the array is empty, the test passed. Otherwise,
  1452. * the test failed.
  1453. * @type {!Object<string, !Array<goog.testing.TestCase.Error>>}
  1454. */
  1455. this.resultsByName = {};
  1456. /**
  1457. * Errors encountered while running the test.
  1458. * @type {!Array<goog.testing.TestCase.Error>}
  1459. */
  1460. this.errors = [];
  1461. /**
  1462. * Messages to show the user after running the test.
  1463. * @type {!Array<string>}
  1464. */
  1465. this.messages = [];
  1466. /**
  1467. * Whether the tests have completed.
  1468. * @type {boolean}
  1469. */
  1470. this.complete = false;
  1471. };
  1472. /**
  1473. * @return {boolean} Whether the test was successful.
  1474. */
  1475. goog.testing.TestCase.Result.prototype.isSuccess = function() {
  1476. return this.complete && this.errors.length == 0;
  1477. };
  1478. /**
  1479. * @return {string} A summary of the tests, including total number of tests that
  1480. * passed, failed, and the time taken.
  1481. */
  1482. goog.testing.TestCase.Result.prototype.getSummary = function() {
  1483. var summary = this.runCount + ' of ' + this.totalCount + ' tests run in ' +
  1484. this.runTime + 'ms.\n';
  1485. if (this.testSuppressed) {
  1486. summary += 'Tests not run because shouldRunTests() returned false.';
  1487. } else {
  1488. var failures = this.totalCount - this.successCount;
  1489. var suppressionMessage = '';
  1490. var countOfRunTests = this.testCase_.getActuallyRunCount();
  1491. if (countOfRunTests) {
  1492. failures = countOfRunTests - this.successCount;
  1493. suppressionMessage = ', ' + (this.totalCount - countOfRunTests) +
  1494. ' suppressed by querystring';
  1495. }
  1496. summary += this.successCount + ' passed, ' + failures + ' failed' +
  1497. suppressionMessage + '.\n' + Math.round(this.runTime / this.runCount) +
  1498. ' ms/test. ' + this.numFilesLoaded + ' files loaded.';
  1499. }
  1500. return summary;
  1501. };
  1502. /**
  1503. * @param {function(goog.testing.TestCase.Test, !Array<string>)} testDone
  1504. */
  1505. goog.testing.TestCase.prototype.setTestDoneCallback = function(testDone) {
  1506. this.testDone_ = testDone;
  1507. };
  1508. /**
  1509. * @param {goog.testing.TestCase.Test} test
  1510. * @param {!Array<string>} errMsgs
  1511. * @private
  1512. */
  1513. goog.testing.TestCase.prototype.doTestDone_ = function(test, errMsgs) {
  1514. test.stopped();
  1515. this.testDone_(test, errMsgs);
  1516. };
  1517. /** Initializes the TestCase.
  1518. * @param {goog.testing.TestCase} testCase The test case to install.
  1519. * @param {function(goog.testing.TestCase.Test, Array<string>)=} opt_testDone
  1520. * Called when each test completes.
  1521. */
  1522. goog.testing.TestCase.initializeTestCase = function(testCase, opt_testDone) {
  1523. if (opt_testDone) {
  1524. testCase.setTestDoneCallback(opt_testDone);
  1525. }
  1526. testCase.autoDiscoverTests();
  1527. if (goog.global.location) {
  1528. var search = goog.global.location.search;
  1529. testCase.setOrder(
  1530. goog.testing.TestCase.parseOrder_(search) ||
  1531. goog.testing.TestCase.Order.SORTED);
  1532. testCase.setTestsToRun(goog.testing.TestCase.parseRunTests_(search));
  1533. }
  1534. goog.testing.TestCase.activeTestCase_ = testCase;
  1535. };
  1536. /**
  1537. * Initializes the given test case with the global test runner 'G_testRunner'.
  1538. * @param {goog.testing.TestCase} testCase The test case to install.
  1539. * @param {function(goog.testing.TestCase.Test, Array<string>)=} opt_testDone
  1540. * Called when each test completes.
  1541. */
  1542. goog.testing.TestCase.initializeTestRunner = function(testCase, opt_testDone) {
  1543. goog.testing.TestCase.initializeTestCase(testCase, opt_testDone);
  1544. var gTestRunner = goog.global['G_testRunner'];
  1545. if (gTestRunner) {
  1546. gTestRunner['initialize'](testCase);
  1547. } else {
  1548. throw Error(
  1549. 'G_testRunner is undefined. Please ensure goog.testing.jsunit' +
  1550. ' is included.');
  1551. }
  1552. };
  1553. /**
  1554. * Parses URL query parameters for the 'order' parameter.
  1555. * @param {string} search The URL query string.
  1556. * @return {?goog.testing.TestCase.Order} The sort order for running tests.
  1557. * @private
  1558. */
  1559. goog.testing.TestCase.parseOrder_ = function(search) {
  1560. var order = null;
  1561. var orderMatch = search.match(/(?:\?|&)order=(natural|random|sorted)/i);
  1562. if (orderMatch) {
  1563. order = /** @type {goog.testing.TestCase.Order} */ (
  1564. orderMatch[1].toLowerCase());
  1565. }
  1566. return order;
  1567. };
  1568. /**
  1569. * Parses URL query parameters for the 'runTests' parameter.
  1570. * @param {string} search The URL query string.
  1571. * @return {Object<string, boolean>} A set of test names or test indices to be
  1572. * run by the test runner.
  1573. * @private
  1574. */
  1575. goog.testing.TestCase.parseRunTests_ = function(search) {
  1576. var testsToRun = null;
  1577. var runTestsMatch = search.match(/(?:\?|&)runTests=([^?&]+)/i);
  1578. if (runTestsMatch) {
  1579. testsToRun = {};
  1580. var arr = runTestsMatch[1].split(',');
  1581. for (var i = 0, len = arr.length; i < len; i++) {
  1582. testsToRun[arr[i]] = true;
  1583. }
  1584. }
  1585. return testsToRun;
  1586. };
  1587. /**
  1588. * Wraps provided promise and returns a new promise which will be rejected
  1589. * if the original promise does not settle within the given timeout.
  1590. * @param {!goog.Promise<T>} promise
  1591. * @param {number} timeoutInMs Number of milliseconds to wait for the promise to
  1592. * settle before failing it with a timeout error.
  1593. * @param {string} errorMsg Error message to use if the promise times out.
  1594. * @return {!goog.Promise<T>} A promise that will settle with the original
  1595. promise unless the timeout is exceeded.
  1596. * errror.
  1597. * @template T
  1598. * @private
  1599. */
  1600. goog.testing.TestCase.prototype.rejectIfPromiseTimesOut_ = function(
  1601. promise, timeoutInMs, errorMsg) {
  1602. var self = this;
  1603. var start = this.now();
  1604. return new goog.Promise(function(resolve, reject) {
  1605. var timeoutId = self.timeout(function() {
  1606. var elapsed = self.now() - start;
  1607. reject(new Error(errorMsg + '\nElapsed time: ' + elapsed + 'ms.'));
  1608. }, timeoutInMs);
  1609. promise.then(resolve, reject);
  1610. var clearTimeout = goog.bind(self.clearTimeout, self, timeoutId);
  1611. promise.then(clearTimeout, clearTimeout);
  1612. });
  1613. };
  1614. /**
  1615. * A class representing an error thrown by the test
  1616. * @param {string} source The name of the test which threw the error.
  1617. * @param {string} message The error message.
  1618. * @param {string=} opt_stack A string showing the execution stack.
  1619. * @constructor
  1620. * @final
  1621. */
  1622. goog.testing.TestCase.Error = function(source, message, opt_stack) {
  1623. /**
  1624. * The name of the test which threw the error.
  1625. * @type {string}
  1626. */
  1627. this.source = source;
  1628. /**
  1629. * Reference to the test function.
  1630. * @type {string}
  1631. */
  1632. this.message = message;
  1633. /**
  1634. * The stack.
  1635. * @type {?string}
  1636. */
  1637. this.stack = null;
  1638. if (opt_stack) {
  1639. this.stack = opt_stack;
  1640. } else {
  1641. // Attempt to capture a stack trace.
  1642. if (Error.captureStackTrace) {
  1643. // See https://code.google.com/p/v8-wiki/wiki/JavaScriptStackTraceApi
  1644. Error.captureStackTrace(this, goog.testing.TestCase.Error);
  1645. } else {
  1646. var stack = new Error().stack;
  1647. if (stack) {
  1648. this.stack = stack;
  1649. }
  1650. }
  1651. }
  1652. };
  1653. /**
  1654. * Returns a string representing the error object.
  1655. * @return {string} A string representation of the error.
  1656. * @override
  1657. */
  1658. goog.testing.TestCase.Error.prototype.toString = function() {
  1659. return 'ERROR in ' + this.source + '\n' + this.message +
  1660. (this.stack ? '\n' + this.stack : '');
  1661. };
  1662. /**
  1663. * Returns an object representing the error suitable for JSON serialization.
  1664. * @return {!goog.testing.TestCase.IResult} An object
  1665. * representation of the error.
  1666. * @private
  1667. */
  1668. goog.testing.TestCase.Error.prototype.toObject_ = function() {
  1669. return {
  1670. 'source': this.source,
  1671. 'message': this.message,
  1672. 'stacktrace': this.stack || ''
  1673. };
  1674. };
  1675. /**
  1676. * @constructor
  1677. * @param {!function(): (?goog.testing.Continuation_|undefined)} fn
  1678. * @private
  1679. */
  1680. goog.testing.Continuation_ = function(fn) {
  1681. /** @private @const */
  1682. this.fn_ = fn;
  1683. };
  1684. /** @param {?goog.testing.Continuation_|undefined} continuation */
  1685. goog.testing.Continuation_.run = function(continuation) {
  1686. var fn = continuation && continuation.fn_;
  1687. while (fn) {
  1688. continuation = fn();
  1689. fn = continuation && continuation.fn_;
  1690. }
  1691. };