Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / test / data / webui / test_api.js
bloba4428414559aeb6c3c346536fe40ae3d6ea5677e
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6  * @fileoverview Library providing basic test framework functionality.
7  */
9 /**
10  * Namespace for |Test|.
11  * @type {Object}
12  */
13 var testing = {};
14 (function(exports) {
15   /**
16    * Holds the original version of the |chrome| object.
17    */
18   var originalChrome = null;
20   /**
21    * Hold the currentTestCase across between preLoad and run.
22    * @type {TestCase}
23    */
24   var currentTestCase = null;
26   /**
27    * The string representation of the currently running test function.
28    * @type {?string}
29    */
30   var currentTestFunction = null;
32   /**
33    * The arguments of the currently running test.
34    * @type {Array}
35    */
36   var currentTestArguments = [];
38  /**
39    * This class will be exported as testing.Test, and is provided to hold the
40    * fixture's configuration and callback methods for the various phases of
41    * invoking a test. It is called "Test" rather than TestFixture to roughly
42    * mimic the gtest's class names.
43    * @constructor
44    */
45   function Test() {};
47   /**
48    * Make all transitions and animations take 0ms. NOTE: this will completely
49    * disable webkitTransitionEnd events. If your code relies on them firing, it
50    * will break. webkitAnimationEnd events should still work.
51    */
52   Test.disableAnimationsAndTransitions = function() {
53     var noAnimationStyle = document.createElement('style');
54     noAnimationStyle.id = 'no-animation';
55     noAnimationStyle.textContent =
56       '* {' +
57       '  -webkit-transition-duration: 0ms !important;' +
58       '  -webkit-transition-delay: 0ms !important;' +
59       '  -webkit-animation-duration: 0ms !important;' +
60       '  -webkit-animation-delay: 0ms !important;' +
61       '}';
62     document.querySelector('head').appendChild(noAnimationStyle);
63   };
65   Test.prototype = {
66     /**
67      * The name of the test.
68      */
69     name: null,
71     /**
72      * When set to a string value representing a url, generate BrowsePreload
73      * call, which will browse to the url and call fixture.preLoad of the
74      * currentTestCase.
75      * @type {string}
76      */
77     browsePreload: null,
79     /**
80      * When set to a string value representing an html page in the test
81      * directory, generate BrowsePrintPreload call, which will browse to a url
82      * representing the file, cause print, and call fixture.preLoad of the
83      * currentTestCase.
84      * @type {string}
85      */
86     browsePrintPreload: null,
88     /**
89      * When set to a function, will be called in the context of the test
90      * generation inside the function, after AddLibrary calls and before
91      * generated C++.
92      * @type {function(string,string)}
93      */
94     testGenPreamble: null,
96     /**
97      * When set to a function, will be called in the context of the test
98      * generation inside the function, and after any generated C++.
99      * @type {function(string,string)}
100      */
101     testGenPostamble: null,
103     /**
104      * When set to a non-null string, auto-generate typedef before generating
105      * TEST*: {@code typedef typedefCppFixture testFixture}.
106      * @type {string}
107      */
108     typedefCppFixture: 'WebUIBrowserTest',
110     /**
111      * This should be initialized by the test fixture and can be referenced
112      * during the test run. It holds any mocked handler methods.
113      * @type {?Mock4JS.Mock}
114      */
115     mockHandler: null,
117     /**
118      * This should be initialized by the test fixture and can be referenced
119      * during the test run. It holds any mocked global functions.
120      * @type {?Mock4JS.Mock}
121      */
122     mockGlobals: null,
124     /**
125      * Value is passed through call to C++ RunJavascriptF to invoke this test.
126      * @type {boolean}
127      */
128     isAsync: false,
130     /**
131      * True when the test is expected to fail for testing the test framework.
132      * @type {boolean}
133      */
134     testShouldFail: false,
136     /**
137      * Extra libraries to add before loading this test file.
138      * @type {Array<string>}
139      */
140     extraLibraries: [],
142     /**
143      * Extra libraries to add before loading this test file.
144      * This list is in the form of Closure library style object
145      * names.  To support this, a closure deps.js file must
146      * be specified when generating the test C++ source.
147      * The specified libraries will be included with their transitive
148      * dependencies according to the deps file.
149      * @type {Array<string>}
150      */
151     closureModuleDeps: [],
153     /**
154      * Whether to run the accessibility checks.
155      * @type {boolean}
156      */
157     runAccessibilityChecks: true,
159     /**
160      * Configuration for the accessibility audit.
161      * @type {axs.AuditConfiguration}
162      */
163     accessibilityAuditConfig_: null,
165     /**
166      * Returns the configuration for the accessibility audit, creating it
167      * on-demand.
168      * @return {axs.AuditConfiguration}
169      */
170     get accessibilityAuditConfig() {
171       // The axs namespace is not available in chromevox tests.
172       // Further, 'window' is not available in unit tests, but since the
173       // accessibility audit library pulls in the closure library,
174       // 'goog.global' has to be present if axs is, so we use that here.
175       if (!this.accessibilityAuditConfig_ &&
176           goog && goog.global && goog.global.axs) {
177         this.accessibilityAuditConfig_ = new axs.AuditConfiguration();
179         this.accessibilityAuditConfig_.showUnsupportedRulesWarning = false;
181         this.accessibilityAuditConfig_.auditRulesToIgnore = [
182             // The "elements with meaningful background image" accessibility
183             // audit (AX_IMAGE_01) does not apply, since Chrome doesn't
184             // disable background images in high-contrast mode like some
185             // browsers do.
186             "elementsWithMeaningfulBackgroundImage",
188             // Most WebUI pages are inside an IFrame, so the "web page should
189             // have a title that describes topic or purpose" test (AX_TITLE_01)
190             // generally does not apply.
191             "pageWithoutTitle",
193             // TODO(aboxhall): re-enable when crbug.com/267035 is fixed.
194             // Until then it's just noise.
195             "lowContrastElements",
196         ];
197       }
198       return this.accessibilityAuditConfig_;
199     },
201     /**
202      * Whether to treat accessibility issues (errors or warnings) as test
203      * failures. If true, any accessibility issues will cause the test to fail.
204      * If false, accessibility issues will cause a console.warn.
205      * Off by default to begin with; as we add the ability to suppress false
206      * positives, we will transition this to true.
207      * @type {boolean}
208      */
209     accessibilityIssuesAreErrors: false,
211     /**
212      * Holds any accessibility results found during the accessibility audit.
213      * @type {Array<Object>}
214      */
215     a11yResults_: [],
217     /**
218      * Gets the list of accessibility errors found during the accessibility
219      * audit. Only for use in testing.
220      * @return {Array<Object>}
221      */
222     getAccessibilityResults: function() {
223       return this.a11yResults_;
224     },
226     /**
227      * Run accessibility checks after this test completes.
228      */
229     enableAccessibilityChecks: function() {
230       this.runAccessibilityChecks = true;
231     },
233     /**
234      * Don't run accessibility checks after this test completes.
235      */
236     disableAccessibilityChecks: function() {
237       this.runAccessibilityChecks = false;
238     },
240     /**
241      * Create a new class to handle |messageNames|, assign it to
242      * |this.mockHandler|, register its messages and return it.
243      * @return {Mock} Mock handler class assigned to |this.mockHandler|.
244      */
245     makeAndRegisterMockHandler: function(messageNames) {
246       var MockClass = makeMockClass(messageNames);
247       this.mockHandler = mock(MockClass);
248       registerMockMessageCallbacks(this.mockHandler, MockClass);
249       return this.mockHandler;
250     },
252     /**
253      * Create a new class to handle |functionNames|, assign it to
254      * |this.mockGlobals|, register its global overrides, and return it.
255      * @return {Mock} Mock handler class assigned to |this.mockGlobals|.
256      * @see registerMockGlobals
257      */
258     makeAndRegisterMockGlobals: function(functionNames) {
259       var MockClass = makeMockClass(functionNames);
260       this.mockGlobals = mock(MockClass);
261       registerMockGlobals(this.mockGlobals, MockClass);
262       return this.mockGlobals;
263     },
265     /**
266       * Create a container of mocked standalone functions to handle
267       * '.'-separated |apiNames|, assign it to |this.mockApis|, register its API
268       * overrides and return it.
269       * @return {Mock} Mock handler class.
270       * @see makeMockFunctions
271       * @see registerMockApis
272       */
273     makeAndRegisterMockApis: function (apiNames) {
274       var apiMockNames = apiNames.map(function(name) {
275         return name.replace(/\./g, '_');
276       });
278       this.mockApis = makeMockFunctions(apiMockNames);
279       registerMockApis(this.mockApis);
280       return this.mockApis;
281     },
283     /**
284       * Create a container of mocked standalone functions to handle
285       * |functionNames|, assign it to |this.mockLocalFunctions| and return it.
286       * @param {!Array<string>} functionNames
287       * @return {Mock} Mock handler class.
288       * @see makeMockFunctions
289       */
290     makeMockLocalFunctions: function(functionNames) {
291       this.mockLocalFunctions = makeMockFunctions(functionNames);
292       return this.mockLocalFunctions;
293     },
295     /**
296      * Override this method to perform initialization during preload (such as
297      * creating mocks and registering handlers).
298      * @type {Function}
299      */
300     preLoad: function() {},
302     /**
303      * Override this method to perform tasks before running your test.
304      * @type {Function}
305      */
306     setUp: function() {
307       var auditConfig = this.accessibilityAuditConfig;
308       if (auditConfig) {
309         // These should be ignored in many of the web UI tests.
310         // user-image-stream and supervised-user-creation-image-stream are
311         // streaming video elements used for capturing a user image so they
312         // won't have captions and should be ignored everywhere.
313         auditConfig.ignoreSelectors('videoWithoutCaptions',
314                                     '.user-image-stream');
315         auditConfig.ignoreSelectors(
316             'videoWithoutCaptions', '.supervised-user-creation-image-stream');
317       }
318     },
320     /**
321      * Override this method to perform tasks after running your test. If you
322      * create a mock class, you must call Mock4JS.verifyAllMocks() in this
323      * phase.
324      * @type {Function}
325      */
326     tearDown: function() {
327       if (typeof document != 'undefined') {
328         var noAnimationStyle = document.getElementById('no-animation');
329         if (noAnimationStyle)
330           noAnimationStyle.parentNode.removeChild(noAnimationStyle);
331       }
333       Mock4JS.verifyAllMocks();
334     },
336     /**
337      * Called to run the body from the perspective of this fixture.
338      * @type {Function}
339      */
340     runTest: function(testBody) {
341       testBody.call(this);
342     },
344     /**
345      * Called to run the accessibility audit from the perspective of this
346      * fixture.
347      */
348     runAccessibilityAudit: function() {
349       if (!this.runAccessibilityChecks || typeof document === 'undefined')
350         return;
352       var auditConfig = this.accessibilityAuditConfig;
353       if (!runAccessibilityAudit(this.a11yResults_, auditConfig)) {
354         var report = accessibilityAuditReport(this.a11yResults_);
355         if (this.accessibilityIssuesAreErrors)
356           throw new Error(report);
357         else
358           console.warn(report);
359       }
360     },
362     /**
363      * Create a closure function for continuing the test at a later time. May be
364      * used as a listener function.
365      * @param {WhenTestDone} whenTestDone Call testDone() at the appropriate
366      *     time.
367      * @param {Function} completion The function to call to complete the test.
368      * @param {...*} var_args Arguments to pass when calling completionAction.
369      * @return {function(): void} Return a function, bound to this test fixture,
370      *     which continues the test.
371      */
372     continueTest: function(whenTestDone, completion) {
373       var savedArgs = new SaveMockArguments();
374       var completionAction = new CallFunctionAction(
375           this, savedArgs, completion,
376           Array.prototype.slice.call(arguments, 2));
377       if (whenTestDone === WhenTestDone.DEFAULT)
378         whenTestDone = WhenTestDone.ASSERT;
379       var runAll = new RunAllAction(
380           true, whenTestDone, [completionAction]);
381       return function() {
382         savedArgs.arguments = Array.prototype.slice.call(arguments);
383         runAll.invoke();
384       };
385     },
387     /**
388      * Call this during setUp to defer the call to runTest() until later. The
389      * caller must call the returned function at some point to run the test.
390      * @type {Function}
391      * @param {WhenTestDone} whenTestDone Call testDone() at the appropriate
392      *     time.
393      * @param {...*} var_args Arguments to pass when running the
394      *     |currentTestCase|.
395      * @return {function(): void} A function which will run the current body of
396      *     the currentTestCase.
397      */
398     deferRunTest: function(whenTestDone) {
399       if (whenTestDone === WhenTestDone.DEFAULT)
400         whenTestDone = WhenTestDone.ALWAYS;
402       return currentTestCase.deferRunTest.apply(
403           currentTestCase, [whenTestDone].concat(
404               Array.prototype.slice.call(arguments, 1)));
405     },
406   };
408   /**
409    * This class is not exported and is available to hold the state of the
410    * |currentTestCase| throughout preload and test run.
411    * @param {string} name The name of the test case.
412    * @param {Test} fixture The fixture object for this test case.
413    * @param {Function} body The code to run for the test.
414    * @constructor
415    */
416   function TestCase(name, fixture, body) {
417     this.name = name;
418     this.fixture = fixture;
419     this.body = body;
420   }
422   TestCase.prototype = {
423     /**
424      * The name of this test.
425      * @type {string}
426      */
427     name: null,
429     /**
430      * The test fixture to set |this| to when running the test |body|.
431      * @type {testing.Test}
432      */
433     fixture: null,
435     /**
436      * The test body to execute in runTest().
437      * @type {Function}
438      */
439     body: null,
441     /**
442      * True when the test fixture will run the test later.
443      * @type {boolean}
444      * @private
445      */
446     deferred_: false,
448     /**
449      * Called at preload time, proxies to the fixture.
450      * @type {Function}
451      */
452     preLoad: function(name) {
453       if (this.fixture)
454         this.fixture.preLoad();
455     },
457     /**
458      * Called before a test runs.
459      */
460     setUp: function() {
461       if (this.fixture)
462         this.fixture.setUp();
463     },
465     /**
466      * Called before a test is torn down (by testDone()).
467      */
468     tearDown: function() {
469       if (this.fixture)
470         this.fixture.tearDown();
471     },
473     /**
474      * Called to run this test's body.
475      */
476     runTest: function() {
477       if (this.body && this.fixture)
478         this.fixture.runTest(this.body);
479     },
481     /**
482      * Called after a test is run (in testDone) to test accessibility.
483      */
484     runAccessibilityAudit: function() {
485       if (this.fixture)
486         this.fixture.runAccessibilityAudit();
487     },
489     /**
490      * Runs this test case with |this| set to the |fixture|.
491      *
492      * Note: Tests created with TEST_F may depend upon |this| being set to an
493      * instance of this.fixture. The current implementation of TEST creates a
494      * dummy constructor, but tests created with TEST should not rely on |this|
495      * being set.
496      * @type {Function}
497      */
498     run: function() {
499       try {
500         this.setUp();
501       } catch(e) {
502         // Mock4JSException doesn't inherit from Error, so fall back on
503         // toString().
504         console.error(e.stack || e.toString());
505       }
507       if (!this.deferred_)
508         this.runTest();
510       // tearDown called by testDone().
511     },
513     /**
514      * Cause this TestCase to be deferred (don't call runTest()) until the
515      * returned function is called.
516      * @type {Function}
517      * @param {WhenTestDone} whenTestDone Call testDone() at the appropriate
518      *     time.
519      * @param {...*} var_args Arguments to pass when running the
520      *     |currentTestCase|.
521      * @return {function(): void} A function thatwill run this TestCase when
522      *     called.
523      */
524     deferRunTest: function(whenTestDone) {
525       this.deferred_ = true;
526       var savedArgs = new SaveMockArguments();
527       var completionAction = new CallFunctionAction(
528           this, savedArgs, this.runTest,
529           Array.prototype.slice.call(arguments, 1));
530       var runAll = new RunAllAction(
531           true, whenTestDone, [completionAction]);
532       return function() {
533         savedArgs.arguments = Array.prototype.slice.call(arguments);
534         runAll.invoke();
535       };
536     },
538   };
540   /**
541    * Registry of javascript-defined callbacks for {@code chrome.send}.
542    * @type {Object}
543    */
544   var sendCallbacks = {};
546   /**
547    * Registers the message, object and callback for {@code chrome.send}
548    * @param {string} name The name of the message to route to this |callback|.
549    * @param {Object} messageHandler Pass as |this| when calling the |callback|.
550    * @param {function(...)} callback Called by {@code chrome.send}.
551    * @see sendCallbacks
552    */
553   function registerMessageCallback(name, messageHandler, callback) {
554     sendCallbacks[name] = [messageHandler, callback];
555   }
557   /**
558    * Register all methods of {@code mockClass.prototype} with messages of the
559    * same name as the method, using the proxy of the |mockObject| as the
560    * |messageHandler| when registering.
561    * @param {Mock4JS.Mock} mockObject The mock to register callbacks against.
562    * @param {function(new:Object)} mockClAss Constructor for the mocked class.
563    * @see registerMessageCallback
564    * @see overrideChrome
565    */
566   function registerMockMessageCallbacks(mockObject, mockClass) {
567     if (!deferGlobalOverrides && !originalChrome)
568       overrideChrome();
569     var mockProxy = mockObject.proxy();
570     for (var func in mockClass.prototype) {
571       if (typeof mockClass.prototype[func] === 'function') {
572         registerMessageCallback(func, mockProxy, mockProxy[func]);
573       }
574     }
575   }
577   /**
578    * Holds the mapping of name -> global override information.
579    * @type {Object}
580    */
581   var globalOverrides = {};
583   /**
584    * When preloading JavaScript libraries, this is true until the
585    * DOMContentLoaded event has been received as globals cannot be overridden
586    * until the page has loaded its JavaScript.
587    * @type {boolean}
588    */
589   var deferGlobalOverrides = false;
591   /**
592    * Override the global function |funcName| with its registered mock. This
593    * should not be called twice for the same |funcName|.
594    * @param {string} funcName The name of the global function to override.
595    */
596   function overrideGlobal(funcName) {
597     assertNotEquals(undefined, this[funcName]);
598     var globalOverride = globalOverrides[funcName];
599     assertNotEquals(undefined, globalOverride);
600     assertEquals(undefined, globalOverride.original);
601     globalOverride.original = this[funcName];
602     this[funcName] = globalOverride.callback.bind(globalOverride.object);
603   }
605   /**
606    * Registers the global function name, object and callback.
607    * @param {string} name The name of the message to route to this |callback|.
608    * @param {Object} object Pass as |this| when calling the |callback|.
609    * @param {function(...)} callback Called by {@code chrome.send}.
610    * @see overrideGlobal
611    */
612   function registerMockGlobal(name, object, callback) {
613     assertEquals(undefined, globalOverrides[name]);
614     globalOverrides[name] = {
615       object: object,
616       callback: callback,
617     };
619     if (!deferGlobalOverrides)
620       overrideGlobal(name);
621   }
623   /**
624    * Registers the mock API call and its function.
625    * @param {string} name The '_'-separated name of the API call.
626    * @param {function(...)} theFunction Mock function for this API call.
627    */
628   function registerMockApi(name, theFunction) {
629     var path = name.split('_');
631     var namespace = this;
632     for(var i = 0; i < path.length - 1; i++) {
633       var fieldName = path[i];
634       if(!namespace[fieldName])
635         namespace[fieldName] = {};
637       namespace = namespace[fieldName];
638     }
640     var fieldName = path[path.length-1];
641     namespace[fieldName] = theFunction;
642   }
644   /**
645    * Empty function for use in making mocks.
646    * @const
647    */
648   function emptyFunction() {}
650   /**
651    * Make a mock from the supplied |methodNames| array.
652    * @param {Array<string>} methodNames Array of names of methods to mock.
653    * @return {Function} Constructor with prototype filled in with methods
654    *     matching |methodNames|.
655    */
656   function makeMockClass(methodNames) {
657     function MockConstructor() {}
658     for(var i = 0; i < methodNames.length; i++)
659       MockConstructor.prototype[methodNames[i]] = emptyFunction;
660     return MockConstructor;
661   }
663   /**
664     * Create a new class to handle |functionNames|, add method 'functions()'
665     * that returns a container of standalone functions based on the mock class
666     * members, and return it.
667     * @return {Mock} Mock handler class.
668     */
669   function makeMockFunctions(functionNames) {
670     var MockClass = makeMockClass(functionNames);
671     var mockFunctions = mock(MockClass);
672     var mockProxy = mockFunctions.proxy();
674     mockFunctions.functions_ = {};
676     for (var func in MockClass.prototype) {
677       if (typeof MockClass.prototype[func] === 'function')
678         mockFunctions.functions_[func] = mockProxy[func].bind(mockProxy);
679     }
681     mockFunctions.functions = function () {
682       return this.functions_;
683     };
685     return mockFunctions;
686   }
688   /**
689    * Register all methods of {@code mockClass.prototype} as overrides to global
690    * functions of the same name as the method, using the proxy of the
691    * |mockObject| to handle the functions.
692    * @param {Mock4JS.Mock} mockObject The mock to register callbacks against.
693    * @param {function(new:Object)} mockClass Constructor for the mocked class.
694    * @see registerMockGlobal
695    */
696   function registerMockGlobals(mockObject, mockClass) {
697     var mockProxy = mockObject.proxy();
698     for (var func in mockClass.prototype) {
699       if (typeof mockClass.prototype[func] === 'function')
700         registerMockGlobal(func, mockProxy, mockProxy[func]);
701     }
702   }
704   /**
705    * Register all functions in |mockObject.functions()| as global API calls.
706    * @param {Mock4JS.Mock} mockObject The mock to register callbacks against.
707    * @see registerMockApi
708    */
709   function registerMockApis(mockObject) {
710     var functions = mockObject.functions();
711     for (var func in functions) {
712       if (typeof functions[func] === 'function')
713         registerMockApi(func, functions[func]);
714     }
715   }
717   /**
718    * Overrides {@code chrome.send} for routing messages to javascript
719    * functions. Also falls back to sending with the original chrome object.
720    * @param {string} messageName The message to route.
721    */
722   function send(messageName) {
723     var callback = sendCallbacks[messageName];
724     if (callback != undefined)
725       callback[1].apply(callback[0], Array.prototype.slice.call(arguments, 1));
726     else
727       this.__proto__.send.apply(this.__proto__, arguments);
728   }
730   /**
731    * Provides a mechanism for assert* and expect* methods to fetch the signature
732    * of their caller. Assert* methods should |registerCall| and expect* methods
733    * should set |isExpect| and |expectName| properties to indicate that the
734    * interesting caller is one more level up the stack.
735    */
736   function CallHelper() {
737     this.__proto__ = CallHelper.prototype;
738   }
740   CallHelper.prototype = {
741     /**
742      * Holds the mapping of (callerCallerString, callerName) -> count of times
743      * called.
744      * @type {Object<string, Object<string, number>>}
745      */
746     counts_: {},
748     /**
749      * This information about the caller is needed from most of the following
750      * routines.
751      * @param {Function} caller the caller of the assert* routine.
752      * @return {{callerName: string, callercallerString: string}} stackInfo
753      * @private
754      */
755     getCallerInfo_: function(caller) {
756       var callerName = caller.name;
757       var callerCaller = caller.caller;
758       if (callerCaller['isExpect']) {
759         callerName = callerCaller.expectName;
760         callerCaller = callerCaller.caller;
761       }
762       var callerCallerString = callerCaller.toString();
763       return {
764         callerName: callerName,
765         callerCallerString: callerCallerString,
766       };
767     },
769     /**
770      * Register a call to an assertion class.
771      */
772     registerCall: function() {
773       var stackInfo = this.getCallerInfo_(arguments.callee.caller);
774       if (!(stackInfo.callerCallerString in this.counts_))
775         this.counts_[stackInfo.callerCallerString] = {};
776       if (!(stackInfo.callerName in this.counts_[stackInfo.callerCallerString]))
777         this.counts_[stackInfo.callerCallerString][stackInfo.callerName] = 0;
778       ++this.counts_[stackInfo.callerCallerString][stackInfo.callerName];
779     },
781     /**
782      * Get the call signature of this instance of the caller's call to this
783      * function.
784      * @param {Function} caller The caller of the assert* routine.
785      * @return {String} Call signature.
786      * @private
787      */
788     getCall_: function(caller) {
789       var stackInfo = this.getCallerInfo_(caller);
790       var count =
791           this.counts_[stackInfo.callerCallerString][stackInfo.callerName];
793       // Allow pattern to match multiple lines for text wrapping.
794       var callerRegExp =
795           new RegExp(stackInfo.callerName + '\\((.|\\n|\\r)*?\\);', 'g');
797       // Find all matches allowing wrap around such as when a helper function
798       // calls assert/expect calls and that helper function is called multiple
799       // times.
800       var matches = stackInfo.callerCallerString.match(callerRegExp);
801       var match = matches[(count - 1) % matches.length];
803       // Chop off the trailing ';'.
804       return match.substring(0, match.length-1);
805     },
807     /**
808      * Returns the text of the call signature and any |message|.
809      * @param {string=} message Addtional message text from caller.
810      */
811     getCallMessage: function(message) {
812       var callMessage = this.getCall_(arguments.callee.caller);
813       if (message)
814         callMessage += ': ' + message;
815       return callMessage;
816     },
817   };
819   /**
820    * Help register calls for better error reporting.
821    * @type {CallHelper}
822    */
823   var helper = new CallHelper();
825   /**
826    * true when testDone has been called.
827    * @type {boolean}
828    */
829   var testIsDone = false;
831   /**
832    * Holds the errors, if any, caught by expects so that the test case can
833    * fail. Cleared when results are reported from runTest() or testDone().
834    * @type {Array<Error>}
835    */
836   var errors = [];
838   /**
839    * URL to dummy WebUI page for testing framework.
840    * @type {string}
841    */
842   var DUMMY_URL = 'chrome://DummyURL';
844   /**
845    * Resets test state by clearing |errors| and |testIsDone| flags.
846    */
847   function resetTestState() {
848     errors.splice(0, errors.length);
849     testIsDone = false;
850   }
852   /**
853    * Notifies the running browser test of the test results. Clears |errors|.
854    * @param {Array<boolean, string>=} result When passed, this is used for the
855    *     testResult message.
856    */
857   function testDone(result) {
858     if (!testIsDone) {
859       testIsDone = true;
860       if (currentTestCase) {
861         var ok = true;
862         ok = createExpect(currentTestCase.runAccessibilityAudit.bind(
863             currentTestCase)).call(null) && ok;
864         ok = createExpect(currentTestCase.tearDown.bind(
865             currentTestCase)).call(null) && ok;
867         if (!ok && result)
868           result = [false, errorsToMessage(errors, result[1])];
870         currentTestCase = null;
871       }
872       if (!result)
873         result = testResult();
874       if (chrome.send) {
875         // For WebUI tests.
876         chrome.send('testResult', result);
877       } else if (window.domAutomationController.send) {
878         // For extension tests.
879         valueResult = { 'result': result[0], message: result[1] };
880         window.domAutomationController.send(JSON.stringify(valueResult));
881       }
882       errors.splice(0, errors.length);
883     } else {
884       console.warn('testIsDone already');
885     }
886   }
888   /**
889    * Converts each Error in |errors| to a suitable message, adding them to
890    * |message|, and returns the message string.
891    * @param {Array<Error>} errors Array of errors to add to |message|.
892    * @param {string?} message When supplied, error messages are appended to it.
893    * @return {string} |message| + messages of all |errors|.
894    */
895   function errorsToMessage(errors, message) {
896     for (var i = 0; i < errors.length; ++i) {
897       var errorMessage = errors[i].stack || errors[i].message;
898       if (message)
899         message += '\n';
901       message += 'Failed: ' + currentTestFunction + '(' +
902           currentTestArguments.map(JSON.stringify) +
903           ')\n' + errorMessage;
904     }
905     return message;
906   }
908   /**
909    * Returns [success, message] & clears |errors|.
910    * @param {boolean} errorsOk When true, errors are ok.
911    * @return {Array<boolean, string>}
912    */
913   function testResult(errorsOk) {
914     var result = [true, ''];
915     if (errors.length)
916       result = [!!errorsOk, errorsToMessage(errors)];
918     return result;
919   }
921   // Asserts.
922   // Use the following assertions to verify a condition within a test.
923   // If assertion fails, throw an Error with information pertinent to the test.
925   /**
926    * When |test| !== true, aborts the current test.
927    * @param {boolean} test The predicate to check against |expected|.
928    * @param {string=} message The message to include in the Error thrown.
929    * @throws {Error} upon failure.
930    */
931   function assertTrue(test, message) {
932     helper.registerCall();
933     if (test !== true)
934       throw new Error(
935           'Test Error ' + helper.getCallMessage(message) + ': ' + test);
936   }
938   /**
939    * When |test| !== false, aborts the current test.
940    * @param {boolean} test The predicate to check against |expected|.
941    * @param {string=} message The message to include in the Error thrown.
942    * @throws {Error} upon failure.
943    */
944   function assertFalse(test, message) {
945     helper.registerCall();
946     if (test !== false)
947       throw new Error(
948           'Test Error ' + helper.getCallMessage(message) + ': ' + test);
949   }
951   /**
952    * When |val1| < |val2|, aborts the current test.
953    * @param {number} val1 The number expected to be >= |val2|.
954    * @param {number} val2 The number expected to be < |val1|.
955    * @param {string=} message The message to include in the Error thrown.
956    */
957   function assertGE(val1, val2, message) {
958     helper.registerCall();
959     if (val1 < val2) {
960       throw new Error(
961           'Test Error ' + helper.getCallMessage(message) + val1 + '<' + val2);
962     }
963   }
965   /**
966    * When |val1| <= |val2|, aborts the current test.
967    * @param {number} val1 The number expected to be > |val2|.
968    * @param {number} val2 The number expected to be <= |val1|.
969    * @param {string=} message The message to include in the Error thrown.
970    */
971   function assertGT(val1, val2, message) {
972     helper.registerCall();
973     if (val1 <= val2) {
974       throw new Error(
975           'Test Error ' + helper.getCallMessage(message) + val1 + '<=' + val2);
976     }
977   }
979   /**
980    * When |expected| !== |actual|, aborts the current test.
981    * @param {*} expected The expected value of |actual|.
982    * @param {*} actual The predicate to check against |expected|.
983    * @param {string=} message The message to include in the Error thrown.
984    * @throws {Error} upon failure.
985    */
986   function assertEquals(expected, actual, message) {
987     helper.registerCall();
988     if (expected != actual) {
989       throw new Error(
990           'Test Error ' + helper.getCallMessage(message) +
991           '\nActual: ' + actual + '\nExpected: ' + expected);
992     }
993     if (typeof expected !== typeof actual) {
994       throw new Error(
995           'Test Error (type mismatch) ' + helper.getCallMessage(message) +
996           '\nActual Type: ' + typeof actual +
997           '\nExpected Type:' + typeof expected);
998     }
999   }
1001   /**
1002    * When |val1| > |val2|, aborts the current test.
1003    * @param {number} val1 The number expected to be <= |val2|.
1004    * @param {number} val2 The number expected to be > |val1|.
1005    * @param {string=} message The message to include in the Error thrown.
1006    */
1007   function assertLE(val1, val2, message) {
1008     helper.registerCall();
1009     if (val1 > val2) {
1010       throw new Error(
1011           'Test Error ' + helper.getCallMessage(message) + val1 + '>' + val2);
1012     }
1013   }
1015   /**
1016    * When |val1| >= |val2|, aborts the current test.
1017    * @param {number} val1 The number expected to be < |val2|.
1018    * @param {number} val2 The number expected to be >= |val1|.
1019    * @param {string=} message The message to include in the Error thrown.
1020    */
1021   function assertLT(val1, val2, message) {
1022     helper.registerCall();
1023     if (val1 >= val2) {
1024       throw new Error(
1025           'Test Error ' + helper.getCallMessage(message) + val1 + '>=' + val2);
1026     }
1027   }
1029   /**
1030    * When |notExpected| === |actual|, aborts the current test.
1031    * @param {*} notExpected The expected value of |actual|.
1032    * @param {*} actual The predicate to check against |notExpected|.
1033    * @param {string=} message The message to include in the Error thrown.
1034    * @throws {Error} upon failure.
1035    */
1036   function assertNotEquals(notExpected, actual, message) {
1037     helper.registerCall();
1038     if (notExpected === actual) {
1039       throw new Error(
1040           'Test Error ' + helper.getCallMessage(message) +
1041           '\nActual: ' + actual + '\nnotExpected: ' + notExpected);
1042     }
1043   }
1045   /**
1046    * Always aborts the current test.
1047    * @param {string=} message The message to include in the Error thrown.
1048    * @throws {Error} always.
1049    */
1050   function assertNotReached(message) {
1051     helper.registerCall();
1052     throw new Error(helper.getCallMessage(message));
1053   }
1055   /**
1056    * Run an accessibility audit on the current page state.
1057    * @type {Function}
1058    * @param {Array} a11yResults
1059    * @param {axs.AuditConfigutarion=} opt_config
1060    * @return {boolean} Whether there were any errors or warnings
1061    * @private
1062    */
1063   function runAccessibilityAudit(a11yResults, opt_config) {
1064     var auditResults = axs.Audit.run(opt_config);
1065     for (var i = 0; i < auditResults.length; i++) {
1066       var auditResult = auditResults[i];
1067       if (auditResult.result == axs.constants.AuditResult.FAIL) {
1068         var auditRule = auditResult.rule;
1069         // TODO(aboxhall): more useful error messages (sadly non-trivial)
1070         a11yResults.push(auditResult);
1071       }
1072     }
1073     // TODO(aboxhall): have strict (no errors or warnings) vs non-strict
1074     // (warnings ok)
1075     // TODO(aboxhall): some kind of info logging for warnings only??
1076     return (a11yResults.length == 0);
1077   }
1079   /**
1080    * Concatenates the accessibility error messages for each result in
1081    * |a11yResults| and
1082    * |a11yWarnings| in to an accessibility report, appends it to the given
1083    * |message| and returns the resulting message string.
1084    * @param {Array<string>} a11yResults The list of accessibility results
1085    * @return {string} |message| + accessibility report.
1086    */
1087   function accessibilityAuditReport(a11yResults, message) {
1088     message = message ? message + '\n\n' : '\n';
1089     message += 'Accessibility issues found on ' + window.location.href + '\n';
1090     message += axs.Audit.createReport(a11yResults);
1091     return message;
1092   }
1094   /**
1095    * Asserts that the current page state passes the accessibility audit.
1096    * @param {Array=} opt_results Array to fill with results, if desired.
1097    */
1098   function assertAccessibilityOk(opt_results) {
1099     helper.registerCall();
1100     var a11yResults = opt_results || [];
1101     var auditConfig = currentTestCase.fixture.accessibilityAuditConfig;
1102     if (!runAccessibilityAudit(a11yResults, auditConfig))
1103       throw new Error(accessibilityAuditReport(a11yResults));
1104   }
1106   /**
1107    * Creates a function based upon a function that thows an exception on
1108    * failure. The new function stuffs any errors into the |errors| array for
1109    * checking by runTest. This allows tests to continue running other checks,
1110    * while failing the overall test if any errors occurrred.
1111    * @param {Function} assertFunc The function which may throw an Error.
1112    * @return {function(...*):bool} A function that applies its arguments to
1113    *     |assertFunc| and returns true if |assertFunc| passes.
1114    * @see errors
1115    * @see runTestFunction
1116    */
1117   function createExpect(assertFunc) {
1118     var expectFunc = function() {
1119       try {
1120         assertFunc.apply(null, arguments);
1121       } catch (e) {
1122         errors.push(e);
1123         return false;
1124       }
1125       return true;
1126     };
1127     expectFunc.isExpect = true;
1128     expectFunc.expectName = assertFunc.name.replace(/^assert/, 'expect');
1129     return expectFunc;
1130   }
1132   /**
1133    * This is the starting point for tests run by WebUIBrowserTest.  If an error
1134    * occurs, it reports a failure and a message created by joining individual
1135    * error messages. This supports sync tests and async tests by calling
1136    * testDone() when |isAsync| is not true, relying on async tests to call
1137    * testDone() when they complete.
1138    * @param {boolean} isAsync When false, call testDone() with the test result
1139    *     otherwise only when assertions are caught.
1140    * @param {string} testFunction The function name to call.
1141    * @param {Array} testArguments The arguments to call |testFunction| with.
1142    * @return {boolean} true always to signal successful execution (but not
1143    *     necessarily successful results) of this test.
1144    * @see errors
1145    * @see runTestFunction
1146    */
1147   function runTest(isAsync, testFunction, testArguments) {
1148     // Avoid eval() if at all possible, since it will not work on pages
1149     // that have enabled content-security-policy.
1150     var testBody = this[testFunction];    // global object -- not a method.
1151     var testName = testFunction;
1153     // Depending on how we were called, |this| might not resolve to the global
1154     // context.
1155     if (testName == 'RUN_TEST_F' && testBody === undefined)
1156       testBody = RUN_TEST_F;
1158     if (typeof testBody === "undefined") {
1159       testBody = eval(testFunction);
1160       testName = testBody.toString();
1161     }
1162     if (testBody != RUN_TEST_F) {
1163       console.log('Running test ' + testName);
1164     }
1166     // Async allow expect errors, but not assert errors.
1167     var result = runTestFunction(testFunction, testBody, testArguments,
1168                                  isAsync);
1169     if (!isAsync || !result[0])
1170       testDone(result);
1171     return true;
1172   }
1174   /**
1175    * This is the guts of WebUIBrowserTest. It runs the test surrounded by an
1176    * expect to catch Errors. If |errors| is non-empty, it reports a failure and
1177    * a message by joining |errors|. Consumers can use this to use assert/expect
1178    * functions asynchronously, but are then responsible for reporting errors to
1179    * the browser themselves through testDone().
1180    * @param {string} testFunction The function name to report on failure.
1181    * @param {Function} testBody The function to call.
1182    * @param {Array} testArguments The arguments to call |testBody| with.
1183    * @param {boolean} onlyAssertFails When true, only assertions cause failing
1184    *     testResult.
1185    * @return {Array<boolean, string>} [test-succeeded, message-if-failed]
1186    * @see createExpect
1187    * @see testResult
1188    */
1189   function runTestFunction(testFunction, testBody, testArguments,
1190                            onlyAssertFails) {
1191     currentTestFunction = testFunction;
1192     currentTestArguments = testArguments;
1193     var ok = createExpect(testBody).apply(null, testArguments);
1194     return testResult(onlyAssertFails && ok);
1195   }
1197   /**
1198    * Creates a new test case for the given |testFixture| and |testName|. Assumes
1199    * |testFixture| describes a globally available subclass of type Test.
1200    * @param {string} testFixture The fixture for this test case.
1201    * @param {string} testName The name for this test case.
1202    * @return {TestCase} A newly created TestCase.
1203    */
1204   function createTestCase(testFixture, testName) {
1205     var fixtureConstructor = this[testFixture];
1206     var testBody = fixtureConstructor.testCaseBodies[testName];
1207     var fixture = new fixtureConstructor();
1208     fixture.name = testFixture;
1209     return new TestCase(testName, fixture, testBody);
1210   }
1212   /**
1213    * Overrides the |chrome| object to enable mocking calls to chrome.send().
1214    */
1215   function overrideChrome() {
1216     if (originalChrome) {
1217       console.error('chrome object already overridden');
1218       return;
1219     }
1221     originalChrome = chrome;
1222     chrome = {
1223       __proto__: originalChrome,
1224       send: send,
1225       originalSend: originalChrome.send.bind(originalChrome),
1226     };
1227   }
1229   /**
1230    * Used by WebUIBrowserTest to preload the javascript libraries at the
1231    * appropriate time for javascript injection into the current page. This
1232    * creates a test case and calls its preLoad for any early initialization such
1233    * as registering handlers before the page's javascript runs it's OnLoad
1234    * method. This is called before the page is loaded, so the |chrome| object is
1235    * not yet bound and this DOMContentLoaded listener will be called first to
1236    * override |chrome| in order to route messages registered in |sendCallbacks|.
1237    * @param {string} testFixture The test fixture name.
1238    * @param {string} testName The test name.
1239    * @see sendCallbacks
1240    */
1241   function preloadJavascriptLibraries(testFixture, testName) {
1242     deferGlobalOverrides = true;
1244     // The document seems to change from the point of preloading to the point of
1245     // events (and doesn't fire), whereas the window does not. Listening to the
1246     // capture phase allows this event to fire first.
1247     window.addEventListener('DOMContentLoaded', function() {
1248       overrideChrome();
1250       // Override globals at load time so they will be defined.
1251       assertTrue(deferGlobalOverrides);
1252       deferGlobalOverrides = false;
1253       for (var funcName in globalOverrides)
1254         overrideGlobal(funcName);
1255     }, true);
1256     currentTestCase = createTestCase(testFixture, testName);
1257     currentTestCase.preLoad();
1258   }
1260   /**
1261    * During generation phase, this outputs; do nothing at runtime.
1262    */
1263   function GEN() {}
1265   /**
1266    * During generation phase, this outputs; do nothing at runtime.
1267    */
1268   function GEN_INCLUDE() {}
1270   /**
1271    * At runtime, register the testName with a test fixture. Since this method
1272    * doesn't have a test fixture, create a dummy fixture to hold its |name|
1273    * and |testCaseBodies|.
1274    * @param {string} testCaseName The name of the test case.
1275    * @param {string} testName The name of the test function.
1276    * @param {Function} testBody The body to execute when running this test.
1277    */
1278   function TEST(testCaseName, testName, testBody) {
1279     var fixtureConstructor = this[testCaseName];
1280     if (fixtureConstructor === undefined) {
1281       fixtureConstructor = function() {};
1282       this[testCaseName] = fixtureConstructor;
1283       fixtureConstructor.prototype = {
1284         __proto__: Test.prototype,
1285         name: testCaseName,
1286       };
1287       fixtureConstructor.testCaseBodies = {};
1288     }
1289     fixtureConstructor.testCaseBodies[testName] = testBody;
1290   }
1292   /**
1293    * At runtime, register the testName with its fixture. Stuff the |name| into
1294    * the |testFixture|'s prototype, if needed, and the |testCaseBodies| into its
1295    * constructor.
1296    * @param {string} testFixture The name of the test fixture class.
1297    * @param {string} testName The name of the test function.
1298    * @param {Function} testBody The body to execute when running this test.
1299    */
1300   function TEST_F(testFixture, testName, testBody) {
1301     var fixtureConstructor = this[testFixture];
1302     if (!fixtureConstructor.prototype.name)
1303       fixtureConstructor.prototype.name = testFixture;
1304     if (fixtureConstructor['testCaseBodies'] === undefined)
1305       fixtureConstructor.testCaseBodies = {};
1306     fixtureConstructor.testCaseBodies[testName] = testBody;
1307   }
1309   /**
1310    * RunJavascriptTestF uses this as the |testFunction| when invoking
1311    * runTest. If |currentTestCase| is non-null at this point, verify that
1312    * |testFixture| and |testName| agree with the preloaded values. Create
1313    * |currentTestCase|, if needed, run it, and clear the |currentTestCase|.
1314    * @param {string} testFixture The name of the test fixture class.
1315    * @param {string} testName The name of the test function.
1316    * @see preloadJavascriptLibraries
1317    * @see runTest
1318    */
1319   function RUN_TEST_F(testFixture, testName) {
1320     if (!currentTestCase)
1321       currentTestCase = createTestCase(testFixture, testName);
1322     assertEquals(currentTestCase.name, testName);
1323     assertEquals(currentTestCase.fixture.name, testFixture);
1324     console.log('Running TestCase ' + testFixture + '.' + testName);
1325     currentTestCase.run();
1326   }
1328   /**
1329    * This Mock4JS matcher object pushes each |actualArgument| parameter to
1330    * match() calls onto |args|.
1331    * @param {Array} args The array to push |actualArgument| onto.
1332    * @param {Object} realMatcher The real matcher check arguments with.
1333    * @constructor
1334    * @extends {realMatcher}
1335    */
1336   function SaveMockArgumentMatcher(args, realMatcher) {
1337     this.arguments_ = args;
1338     this.realMatcher_ = realMatcher;
1339   }
1341   SaveMockArgumentMatcher.prototype = {
1342     /**
1343      * Holds the arguments to push each |actualArgument| onto.
1344      * @type {Array}
1345      * @private
1346      */
1347     arguments_: null,
1349     /**
1350      * The real Mock4JS matcher object to check arguments with.
1351      * @type {Object}
1352      */
1353     realMatcher_: null,
1355     /**
1356      * Pushes |actualArgument| onto |arguments_| and call |realMatcher_|. Clears
1357      * |arguments_| on non-match.
1358      * @param {*} actualArgument The argument to match and save.
1359      * @return {boolean} Result of calling the |realMatcher|.
1360      */
1361     argumentMatches: function(actualArgument) {
1362       this.arguments_.push(actualArgument);
1363       var match = this.realMatcher_.argumentMatches(actualArgument);
1364       if (!match)
1365         this.arguments_.splice(0, this.arguments_.length);
1367       return match;
1368     },
1370     /**
1371      * Proxy to |realMatcher_| for description.
1372      * @return {string} Description of this Mock4JS matcher.
1373      */
1374     describe: function() {
1375       return this.realMatcher_.describe();
1376     },
1377   };
1379   /**
1380    * Actions invoked by Mock4JS's "will()" syntax do not receive arguments from
1381    * the mocked method. This class works with SaveMockArgumentMatcher to save
1382    * arguments so that the invoked Action can pass arguments through to the
1383    * invoked function.
1384    * @param {!Object} realMatcher The real matcher to perform matching with.
1385    * @constructor
1386    */
1387   function SaveMockArguments() {
1388     this.arguments = [];
1389   }
1391   SaveMockArguments.prototype = {
1392     /**
1393      * Wraps the |realMatcher| with an object which will push its argument onto
1394      * |arguments| and call realMatcher.
1395      * @param {Object} realMatcher A Mock4JS matcher object for this argument.
1396      * @return {SaveMockArgumentMatcher} A new matcher which will push its
1397      *     argument onto |arguments|.
1398      */
1399     match: function(realMatcher) {
1400       return new SaveMockArgumentMatcher(this.arguments, realMatcher);
1401     },
1403     /**
1404      * Remember the argument passed to this stub invocation.
1405      * @type {Array}
1406      */
1407     arguments: null,
1408   };
1410   /**
1411    * CallFunctionAction is provided to allow mocks to have side effects.
1412    * @param {Object} obj The object to set |this| to when calling |func_|.
1413    * @param {?SaveMockArguments} savedArgs when non-null, saved arguments are
1414    *     passed to |func|.
1415    * @param {Function} func The function to call.
1416    * @param {Array=} args Any arguments to pass to func.
1417    * @constructor
1418    */
1419   function CallFunctionAction(obj, savedArgs, func, args) {
1420     this.obj_ = obj;
1421     this.savedArgs_ = savedArgs;
1422     this.func_ = func;
1423     this.args_ = args ? args : [];
1424   }
1426   CallFunctionAction.prototype = {
1427     /**
1428      * Set |this| to |obj_| when calling |func_|.
1429      * @type {?Object}
1430      */
1431     obj_: null,
1433     /**
1434      * The SaveMockArguments to hold arguments when invoking |func_|.
1435      * @type {?SaveMockArguments}
1436      * @private
1437      */
1438     savedArgs_: null,
1440     /**
1441      * The function to call when invoked.
1442      * @type {!Function}
1443      * @private
1444      */
1445     func_: null,
1447     /**
1448      * Arguments to pass to |func_| when invoked.
1449      * @type {!Array}
1450      */
1451     args_: null,
1453     /**
1454      * Accessor for |func_|.
1455      * @return {Function} The function to invoke.
1456      */
1457     get func() {
1458       return this.func_;
1459     },
1461     /**
1462      * Called by Mock4JS when using .will() to specify actions for stubs() or
1463      * expects(). Clears |savedArgs_| so it can be reused.
1464      * @return The results of calling |func_| with the concatenation of
1465      *     |savedArgs_| and |args_|.
1466      */
1467     invoke: function() {
1468       var prependArgs = [];
1469       if (this.savedArgs_) {
1470         prependArgs = this.savedArgs_.arguments.splice(
1471             0, this.savedArgs_.arguments.length);
1472       }
1473       return this.func.apply(this.obj_, prependArgs.concat(this.args_));
1474     },
1476     /**
1477      * Describe this action to Mock4JS.
1478      * @return {string} A description of this action.
1479      */
1480     describe: function() {
1481       return 'calls the given function with saved arguments and ' + this.args_;
1482     }
1483   };
1485   /**
1486    * Syntactic sugar for use with will() on a Mock4JS.Mock.
1487    * @param {Function} func The function to call when the method is invoked.
1488    * @param {...*} var_args Arguments to pass when calling func.
1489    * @return {CallFunctionAction} Action for use in will.
1490    */
1491   function callFunction(func) {
1492     return new CallFunctionAction(
1493         null, null, func, Array.prototype.slice.call(arguments, 1));
1494   }
1496   /**
1497    * Syntactic sugar for use with will() on a Mock4JS.Mock.
1498    * @param {SaveMockArguments} savedArgs Arguments saved with this object
1499    *     are passed to |func|.
1500    * @param {Function} func The function to call when the method is invoked.
1501    * @param {...*} var_args Arguments to pass when calling func.
1502    * @return {CallFunctionAction} Action for use in will.
1503    */
1504   function callFunctionWithSavedArgs(savedArgs, func) {
1505     return new CallFunctionAction(
1506         null, savedArgs, func, Array.prototype.slice.call(arguments, 2));
1507   }
1509   /**
1510    * CallGlobalAction as a subclass of CallFunctionAction looks up the original
1511    * global object in |globalOverrides| using |funcName| as the key. This allows
1512    * tests, which need to wait until a global function to be called in order to
1513    * start the test to run the original function. When used with runAllActions
1514    * or runAllActionsAsync, Mock4JS expectations may call start or continue the
1515    * test after calling the original function.
1516    * @param {?SaveMockArguments} savedArgs when non-null, saved arguments are
1517    *     passed to the global function |funcName|.
1518    * @param {string} funcName The name of the global function to call.
1519    * @param {Array} args Any arguments to pass to func.
1520    * @constructor
1521    * @extends {CallFunctionAction}
1522    * @see globalOverrides
1523    */
1524   function CallGlobalAction(savedArgs, funcName, args) {
1525     CallFunctionAction.call(this, null, savedArgs, funcName, args);
1526   }
1528   CallGlobalAction.prototype = {
1529     __proto__: CallFunctionAction.prototype,
1531     /**
1532      * Fetch and return the original global function to call.
1533      * @return {Function} The global function to invoke.
1534      * @override
1535      */
1536     get func() {
1537       var func = globalOverrides[this.func_].original;
1538       assertNotEquals(undefined, func);
1539       return func;
1540     },
1541   };
1543   /**
1544    * Syntactic sugar for use with will() on a Mock4JS.Mock.
1545    * @param {SaveMockArguments} savedArgs Arguments saved with this object
1546    *     are passed to the global function |funcName|.
1547    * @param {string} funcName The name of a registered mock global function to
1548    *     call when the method is invoked.
1549    * @param {...*} var_args Arguments to pass when calling func.
1550    * @return {CallGlobalAction} Action for use in Mock4JS will().
1551    */
1552   function callGlobalWithSavedArgs(savedArgs, funcName) {
1553     return new CallGlobalAction(
1554         savedArgs, funcName, Array.prototype.slice.call(arguments, 2));
1555   }
1557   /**
1558    * When to call testDone().
1559    * @enum {number}
1560    */
1561   var WhenTestDone = {
1562     /**
1563      * Default for the method called.
1564      */
1565     DEFAULT: -1,
1567     /**
1568      * Never call testDone().
1569      */
1570     NEVER: 0,
1572     /**
1573      * Call testDone() on assert failure.
1574      */
1575     ASSERT: 1,
1577     /**
1578      * Call testDone() if there are any assert or expect failures.
1579      */
1580     EXPECT: 2,
1582     /**
1583      * Always call testDone().
1584      */
1585     ALWAYS: 3,
1586   };
1588   /**
1589    * Runs all |actions|.
1590    * @param {boolean} isAsync When true, call testDone() on Errors.
1591    * @param {WhenTestDone} whenTestDone Call testDone() at the appropriate
1592    *     time.
1593    * @param {Array<Object>} actions Actions to run.
1594    * @constructor
1595    */
1596   function RunAllAction(isAsync, whenTestDone, actions) {
1597     this.isAsync_ = isAsync;
1598     this.whenTestDone_ = whenTestDone;
1599     this.actions_ = actions;
1600   }
1602   RunAllAction.prototype = {
1603     /**
1604      * When true, call testDone() on Errors.
1605      * @type {boolean}
1606      * @private
1607      */
1608     isAsync_: false,
1610     /**
1611      * Call testDone() at appropriate time.
1612      * @type {WhenTestDone}
1613      * @private
1614      * @see WhenTestDone
1615      */
1616     whenTestDone_: WhenTestDone.ASSERT,
1618     /**
1619      * Holds the actions to execute when invoked.
1620      * @type {Array}
1621      * @private
1622      */
1623     actions_: null,
1625     /**
1626      * Runs all |actions_|, returning the last one. When running in sync mode,
1627      * throws any exceptions to be caught by runTest() or
1628      * runTestFunction(). Call testDone() according to |whenTestDone_| setting.
1629      */
1630     invoke: function() {
1631       try {
1632         var result;
1633         for(var i = 0; i < this.actions_.length; ++i)
1634           result = this.actions_[i].invoke();
1636         if ((this.whenTestDone_ == WhenTestDone.EXPECT && errors.length) ||
1637             this.whenTestDone_ == WhenTestDone.ALWAYS)
1638           testDone();
1640         return result;
1641       } catch (e) {
1642         if (!(e instanceof Error))
1643           e = new Error(e.toString());
1645         if (!this.isAsync_)
1646           throw e;
1648         errors.push(e);
1649         if (this.whenTestDone_ != WhenTestDone.NEVER)
1650           testDone();
1651       }
1652     },
1654     /**
1655      * Describe this action to Mock4JS.
1656      * @return {string} A description of this action.
1657      */
1658     describe: function() {
1659       return 'Calls all actions: ' + this.actions_;
1660     },
1661   };
1663   /**
1664    * Syntactic sugar for use with will() on a Mock4JS.Mock.
1665    * @param {...Object} var_actions Actions to run.
1666    * @return {RunAllAction} Action for use in will.
1667    */
1668   function runAllActions() {
1669     return new RunAllAction(false, WhenTestDone.NEVER,
1670                             Array.prototype.slice.call(arguments));
1671   }
1673   /**
1674    * Syntactic sugar for use with will() on a Mock4JS.Mock.
1675    * @param {WhenTestDone} whenTestDone Call testDone() at the appropriate
1676    *     time.
1677    * @param {...Object} var_actions Actions to run.
1678    * @return {RunAllAction} Action for use in will.
1679    */
1680   function runAllActionsAsync(whenTestDone) {
1681     return new RunAllAction(true, whenTestDone,
1682                             Array.prototype.slice.call(arguments, 1));
1683   }
1685   /**
1686    * Syntactic sugar for use with will() on a Mock4JS.Mock.
1687    * Creates an action for will() that invokes a callback that the tested code
1688    * passes to a mocked function.
1689    * @param {SaveMockArguments} savedArgs Arguments that will contain the
1690    *     callback once the mocked function is called.
1691    * @param {number} callbackParameter Index of the callback parameter in
1692    *     |savedArgs|.
1693    * @param {...Object} var_args Arguments to pass to the callback.
1694    * @return {CallFunctionAction} Action for use in will().
1695    */
1696   function invokeCallback(savedArgs, callbackParameter, var_args) {
1697     var callbackArguments = Array.prototype.slice.call(arguments, 2);
1698     return callFunction(function() {
1699       savedArgs.arguments[callbackParameter].apply(null, callbackArguments);
1701       // Mock4JS does not clear the saved args after invocation.
1702       // To allow reuse of the same SaveMockArguments for multiple
1703       // invocations with similar arguments, clear them here.
1704       savedArgs.arguments.splice(0, savedArgs.arguments.length);
1705     });
1706   }
1708   /**
1709    * Mock4JS matcher object that matches the actual argument and the expected
1710    * value iff their JSON represenations are same.
1711    * @param {Object} expectedValue
1712    * @constructor
1713    */
1714   function MatchJSON(expectedValue) {
1715     this.expectedValue_ = expectedValue;
1716   }
1718   MatchJSON.prototype = {
1719     /**
1720      * Checks that JSON represenation of the actual and expected arguments are
1721      * same.
1722      * @param {Object} actualArgument The argument to match.
1723      * @return {boolean} Result of the comparison.
1724      */
1725     argumentMatches: function(actualArgument) {
1726       return JSON.stringify(this.expectedValue_) ===
1727           JSON.stringify(actualArgument);
1728     },
1730     /**
1731      * Describes the matcher.
1732      * @return {string} Description of this Mock4JS matcher.
1733      */
1734     describe: function() {
1735       return 'eqJSON(' + JSON.stringify(this.expectedValue_) + ')';
1736     },
1737   };
1739   /**
1740    * Builds a MatchJSON argument matcher for a given expected value.
1741    * @param {Object} expectedValue
1742    * @return {MatchJSON} Resulting Mock4JS matcher.
1743    */
1744   function eqJSON(expectedValue) {
1745     return new MatchJSON(expectedValue);
1746   }
1748   /**
1749    * Mock4JS matcher object that matches the actual argument and the expected
1750    * value iff the the string representation of the actual argument is equal to
1751    * the expected value.
1752    * @param {string} expectedValue
1753    * @constructor
1754    */
1755   function MatchToString(expectedValue) {
1756     this.expectedValue_ = expectedValue;
1757   }
1759   MatchToString.prototype = {
1760     /**
1761      * Checks that the the string representation of the actual argument matches
1762      * the expected value.
1763      * @param {*} actualArgument The argument to match.
1764      * @return {boolean} Result of the comparison.
1765      */
1766     argumentMatches: function(actualArgument) {
1767       return this.expectedValue_ === String(actualArgument);
1768     },
1770     /**
1771      * Describes the matcher.
1772      * @return {string} Description of this Mock4JS matcher.
1773      */
1774     describe: function() {
1775       return 'eqToString("' + this.expectedValue_ + '")';
1776     },
1777   };
1779   /**
1780    * Builds a MatchToString argument matcher for a given expected value.
1781    * @param {Object} expectedValue
1782    * @return {MatchToString} Resulting Mock4JS matcher.
1783    */
1784   function eqToString(expectedValue) {
1785     return new MatchToString(expectedValue);
1786   }
1788   // Exports.
1789   testing.Test = Test;
1790   exports.testDone = testDone;
1791   exports.assertTrue = assertTrue;
1792   exports.assertFalse = assertFalse;
1793   exports.assertGE = assertGE;
1794   exports.assertGT = assertGT;
1795   exports.assertEquals = assertEquals;
1796   exports.assertLE = assertLE;
1797   exports.assertLT = assertLT;
1798   exports.assertNotEquals = assertNotEquals;
1799   exports.assertNotReached = assertNotReached;
1800   exports.assertAccessibilityOk = assertAccessibilityOk;
1801   exports.callFunction = callFunction;
1802   exports.callFunctionWithSavedArgs = callFunctionWithSavedArgs;
1803   exports.callGlobalWithSavedArgs = callGlobalWithSavedArgs;
1804   exports.eqJSON = eqJSON;
1805   exports.eqToString = eqToString;
1806   exports.expectTrue = createExpect(assertTrue);
1807   exports.expectFalse = createExpect(assertFalse);
1808   exports.expectGE = createExpect(assertGE);
1809   exports.expectGT = createExpect(assertGT);
1810   exports.expectEquals = createExpect(assertEquals);
1811   exports.expectLE = createExpect(assertLE);
1812   exports.expectLT = createExpect(assertLT);
1813   exports.expectNotEquals = createExpect(assertNotEquals);
1814   exports.expectNotReached = createExpect(assertNotReached);
1815   exports.expectAccessibilityOk = createExpect(assertAccessibilityOk);
1816   exports.invokeCallback = invokeCallback;
1817   exports.preloadJavascriptLibraries = preloadJavascriptLibraries;
1818   exports.registerMessageCallback = registerMessageCallback;
1819   exports.registerMockGlobals = registerMockGlobals;
1820   exports.registerMockMessageCallbacks = registerMockMessageCallbacks;
1821   exports.resetTestState = resetTestState;
1822   exports.runAccessibilityAudit = runAccessibilityAudit;
1823   exports.runAllActions = runAllActions;
1824   exports.runAllActionsAsync = runAllActionsAsync;
1825   exports.runTest = runTest;
1826   exports.runTestFunction = runTestFunction;
1827   exports.SaveMockArguments = SaveMockArguments;
1828   exports.DUMMY_URL = DUMMY_URL;
1829   exports.TEST = TEST;
1830   exports.TEST_F = TEST_F;
1831   exports.RUNTIME_TEST_F = TEST_F;
1832   exports.GEN = GEN;
1833   exports.GEN_INCLUDE = GEN_INCLUDE;
1834   exports.WhenTestDone = WhenTestDone;
1836   // Import the Mock4JS helpers.
1837   Mock4JS.addMockSupport(exports);
1838 })(this);