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