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