Chromium Code Reviews| Index: chrome/test/data/webui/test_api.js |
| diff --git a/chrome/test/data/webui/test_api.js b/chrome/test/data/webui/test_api.js |
| index c9dddc252f351a37714c5fbc80980648d4680103..1cfebd48306d133381119a5e18489e4124afccb1 100644 |
| --- a/chrome/test/data/webui/test_api.js |
| +++ b/chrome/test/data/webui/test_api.js |
| @@ -2,13 +2,247 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| -// Library providing basic test framework functionality. |
| +/** |
| + * @fileoverview Library providing basic test framework functionality. |
| + **/ |
| + |
| +/** |
| + * Namespace for |Test|. |
| + * @type {Object} |
| + **/ |
| +var testing = {}; |
| + |
| +/** |
| + * Hold the currentTestCase across between PreLoad and Run. |
| + * @type {TestCase} |
| + **/ |
| +var currentTestCase = null; |
| (function() { |
| + // Provide global objects for generation case. |
| + if (!('window' in this)) |
| + this['window'] = this; |
| + if (!('chrome' in this)) { |
| + this['chrome'] = { |
| + send: function() {}, |
| + }; |
| + } |
| + if (!('console' in this)) { |
| + this['console'] = { |
| + log: print, |
| + }; |
| + } |
| + |
| + /** |
| + * This class will be exported as testing.Test, and is provided to hold the |
| + * fixture's configuration and callback methods for the various phases of |
| + * invoking a test. It is called "Test" rather than TestFixture to roughly |
| + * mimic the gtest's class names. |
| + * @constructor |
| + **/ |
| + function Test() {} |
| + |
| + Test.prototype = { |
| + /** |
| + * The name of the test. |
| + **/ |
| + name: null, |
| + |
| + /** |
| + * When set to a string value representing a url, generate BrowsePreload |
| + * call, which will browse to the url and call fixture.PreLoad of the |
| + * currentTestCase. |
| + * @type {String} |
| + **/ |
| + browsePreload: null, |
| + |
| + /** |
| + * When set to a string value representing an html page in the test |
| + * directory, generate BrowsePrintPreload call, which will browse to a url |
| + * representing the file, cause print, and call fixture.PreLoad of the |
| + * currentTestCase. |
| + * @type {String} |
| + **/ |
| + browsePrintPreload: null, |
| + |
| + /** |
| + * When set to a function, will be called in the context of the test |
| + * generation inside the function, and before any generated C++. |
| + * @type {function(string,string)} |
| + **/ |
| + testGenPreamble: null, |
| + |
| + /** |
| + * When set to a function, will be called in the context of the test |
| + * generation inside the function, and before any generated C++. |
| + * @type {function(string,string)} |
| + **/ |
| + testGenPostamble: null, |
| + |
| + /** |
| + * When set to a non-null String, auto-generate typedef before generating |
| + * TEST*: {@code typedef typedefCppFixture testFixture}. |
| + * @type {String} |
| + **/ |
| + typedefCppFixture: 'WebUIBrowserTest', |
| + |
| + /** |
| + * This should be initialized by the test fixture and can be referenced |
| + * during the test run. |
| + * @type {Mock4JS.Mock} |
| + **/ |
| + mockHandler: null, |
| + |
| + /** |
| + * Override this method to perform initialization during preload (such as |
| + * creating mocks and registering handlers). |
| + * @type {Function} |
| + **/ |
| + PreLoad: function() {}, |
| + |
| + /** |
| + * Override this method to perform tasks before running your test. |
| + * @type {Function} |
| + **/ |
| + SetUp: function() {}, |
| + |
| + /** |
| + * Override this method to perform tasks after running your test. If you |
| + * create a mock class, you must call Mock4JS.verifyAllMocks() in this |
| + * phase. |
| + * @type {Function} |
| + **/ |
| + TearDown: function() { |
| + Mock4JS.verifyAllMocks(); |
|
Evan Stade
2011/07/11 18:33:57
wrong indent
Sheridan Rawlins
2011/07/11 20:36:08
Done.
|
| + } |
| + }; |
| + |
| + /** |
| + * This class is not exported and is available to hold the state of the |
| + * |currentTestCase| throughout preload and test run. |
| + * @param {String} name the name of the test case. |
|
Evan Stade
2011/07/11 18:33:57
capitalize 'the'
Sheridan Rawlins
2011/07/11 20:36:08
Done.
|
| + * @param {Test} fixture the fixture object for this test case. |
|
Evan Stade
2011/07/11 18:33:57
capitalize 'the'
Sheridan Rawlins
2011/07/11 20:36:08
Done.
|
| + * @param {Function} body the code to run for the test. |
|
Evan Stade
2011/07/11 18:33:57
capitalize 'the'
Sheridan Rawlins
2011/07/11 20:36:08
Done.
|
| + * @constructor |
| + **/ |
| + function TestCase(name, fixture, body) { |
| + this.name = name; |
| + this.fixture = fixture; |
| + this.body = body; |
| + } |
| + |
| + TestCase.prototype = { |
| + /** |
| + * The name of this test case. |
| + * @type {String} |
| + **/ |
| + name: null, |
| + |
| + /** |
| + * The fixture of this test case. |
| + * @type {Test} |
| + **/ |
| + fixture: null, |
| + |
| + /** |
| + * The code to call when running this test case. |
| + * @type {Function} |
| + **/ |
| + body: null, |
|
Evan Stade
2011/07/11 18:33:57
I would say it's not really necessary to write out
Sheridan Rawlins
2011/07/11 20:36:08
Done.
|
| + |
| + /** |
| + * Called at preload time, proxies to the fixture. |
| + * @type {Function} |
| + **/ |
| + PreLoad: function(name) { |
| + if (this.fixture) |
| + this.fixture.PreLoad(); |
| + }, |
| + |
| + /** |
| + * Runs this test case. |
| + * @type {Function} |
| + **/ |
| + Run: function() { |
| + if (this.fixture) |
| + this.fixture.SetUp(); |
| + if (this.body) |
| + this.body.call(this.fixture); |
| + if (this.fixture) |
| + this.fixture.TearDown(); |
| + }, |
| + }; |
| + |
| + /** |
| + * Registry of javascript-defined callbacks for {@code chrome.send}. |
| + * @type {Object} |
| + **/ |
| + var sendCallbacks = {}; |
| + |
| + /** |
| + * Registers the message, object and callback for {@code chrome.send} |
| + * @param {String} name the name of the message to route to this |callback|. |
| + * @param {Object} messageHandler pass as |this| when calling the |callback|. |
| + * @param {function(...)} callback called by {@code chrome.send}. |
| + * @see sendCallbacks |
| + **/ |
| + function registerMessageCallback(name, messageHandler, callback) { |
| + sendCallbacks[name] = [messageHandler, callback]; |
| + } |
| + |
| + /** |
| + * Register all methods of {@code mockClass.prototype} with messages of the |
| + * same name as the method, using the proxy of the |mockObject| as the |
| + * |messageHandler| when registering. |
| + * @param {Mock4JS.Mock} mockObject the mock to register callbacks against. |
| + * @param {function(new:Object)} mockClass constructor of the mocked class. |
| + * @see registerMessageCallback |
| + **/ |
| + function registerMockMessageCallbacks(mockObject, mockClass) { |
| + var mockProxy = mockObject.proxy(); |
| + for (func in mockClass.prototype) { |
| + if (typeof(mockClass.prototype[func]) == 'function') { |
| + registerMessageCallback(func, |
| + mockProxy, |
| + mockProxy[func]); |
| + } |
| + } |
| + } |
| + |
| + /** |
| + * Holds the old chrome object when overriding for preload and registry of |
| + * handlers. |
| + * @type {Object} |
| + **/ |
| + var oldChrome = chrome; |
| + |
| + /** |
| + * Overrides {@code chrome.send} for routing messages to javascript |
| + * functions. Also fallsback to sending with the |oldChrome| object. |
| + * @param {String} messageName the message to route. |
| + * @see oldChrome |
| + **/ |
| + function send(messageName) { |
| + var callback = sendCallbacks[messageName]; |
| + var args = Array.prototype.slice.call(arguments, 1); |
| + if (callback != undefined) |
| + callback[1].apply(callback[0], args); |
| + else |
| + oldChrome.send.apply(oldChrome, args); |
| + } |
| + |
| // Asserts. |
| // Use the following assertions to verify a condition within a test. |
| // If assertion fails, the C++ backend will be immediately notified. |
| // If assertion passes, no notification will be sent to the C++ backend. |
| + |
| + /** |
| + * When |test| !== |expected|, aborts the current test. |
| + * @param test the predicate to check against |expected|. |
| + * @param expected the expected value of |test|. |
| + * @param message the message to include in the Error thrown. |
| + * @throws {Error} upon failure. |
| + **/ |
| function assertBool(test, expected, message) { |
| if (test !== expected) { |
| if (message) |
| @@ -19,30 +253,33 @@ |
| } |
| } |
| - var old_chrome = chrome; |
| - var send_callbacks = {}; |
| - |
| - function registerMessageCallback(name, object, callback) { |
| - send_callbacks[name] = [object, callback]; |
| - } |
| - |
| - function send(messageName) { |
| - var callback = send_callbacks[messageName]; |
| - var args = Array.prototype.slice.call(arguments, 1); |
| - if (callback != undefined) |
| - callback[1].apply(callback[0], args); |
| - else |
| - old_chrome.send.apply(old_chrome, args); |
| - } |
| - |
| + /** |
| + * When |test| !== true, aborts the current test. |
| + * @param test the predicate to check against |expected|. |
| + * @param message the message to include in the Error thrown. |
| + * @throws {Error} upon failure. |
| + **/ |
| function assertTrue(test, message) { |
| assertBool(test, true, message); |
| } |
| + /** |
| + * When |test| !== false, aborts the current test. |
| + * @param test the predicate to check against |expected|. |
| + * @param message the message to include in the Error thrown. |
| + * @throws {Error} upon failure. |
| + **/ |
| function assertFalse(test, message) { |
| assertBool(test, false, message); |
| } |
| + /** |
| + * When |expected| !== |actual|, aborts the current test. |
| + * @param expected the predicate to check against |expected|. |
| + * @param actual the expected value of |test|. |
| + * @param message the message to include in the Error thrown. |
| + * @throws {Error} upon failure. |
| + **/ |
| function assertEquals(expected, actual, message) { |
| if (expected != actual) { |
| throw new Error('Test Error. Actual: ' + actual + '\nExpected: ' + |
| @@ -55,56 +292,216 @@ |
| } |
| } |
| + /** |
| + * Always aborts the current test. |
| + * @param message the message to include in the Error thrown. |
| + * @throws {Error} |
| + **/ |
| function assertNotReached(message) { |
| throw new Error(message); |
| } |
| + /** |
| + * Holds the errors, if any, caught by expects so that the test case can fail. |
| + * @type {Array} |
| + **/ |
| var errors = []; |
| + /** |
| + * Creates a function based upon a function that thows an exception on |
| + * failure. The new function stuffs any errors into the |errors| array for |
| + * checking by runTest. This allows tests to continue running other checks, |
| + * while failing the overal test if any errors occurrred. |
| + * @param {Function} assertFunc the function which may throw an Error. |
| + * @return {Function} a function that applies its arguments to |assertFunc|. |
| + * @see errors |
| + * @see runTest |
| + **/ |
| function createExpect(assertFunc) { |
| return function() { |
| try { |
| assertFunc.apply(null, arguments); |
| } catch (e) { |
| - console.log('Failed: ' + currentTest.name + '\n' + e.stack); |
| errors.push(e); |
| } |
| }; |
| } |
| + /** |
| + * This is the starting point for tests run by WebUIBrowserTest. It clears |
| + * |errors|, runs the test surrounded by an expect to catch Errors. If |
| + * |errors| is non-empty, it reports a failure and a message by joining |
| + * |errors|. |
| + * @param {String} testFunction |
| + * @param {Array} testArguments |
| + * @return {Array.<Boolean, String>} [test-succeeded, message-if-failed] |
| + * @see errors |
| + * @see createExpect |
| + **/ |
| function runTest(testFunction, testArguments) { |
| errors = []; |
| // Avoid eval() if at all possible, since it will not work on pages |
| // that have enabled content-security-policy. |
| - currentTest = this[testFunction]; // global object -- not a method. |
| - if (typeof currentTest === "undefined") { |
| - currentTest = eval(testFunction); |
| - } |
| - console.log('Running test ' + currentTest.name); |
| - createExpect(currentTest).apply(null, testArguments); |
| + var testBody = this[testFunction]; // global object -- not a method. |
| + if (typeof testBody === "undefined") |
| + testBody = eval(testFunction); |
| + if (testBody != RUN_TEST_F) |
| + console.log('Running test ' + testBody.name); |
| + createExpect(testBody).apply(null, testArguments); |
| if (errors.length) { |
| + for (var i = 0; i < errors.length; ++i) { |
| + console.log('Failed: ' + testFunction + '(' + |
| + testArguments.toString() + ')\n' + errors[i].stack); |
| + } |
| return [false, errors.join('\n')]; |
| + } else { |
| + return [true]; |
| } |
| + } |
| - return [true]; |
| + /** |
| + * Creates a new test case for the given |testFixture| and |testName|. Assumes |
| + * |testFixture| describes a globally available subclass of type Test. |
| + * @param {String} testFixture the fixture for this test case. |
| + * @param {String} testName the name for this test case. |
| + * @return {TestCase} a newly created TestCase. |
| + **/ |
| + function createTestCase(testFixture, testName) { |
| + var fixtureConstructor = this[testFixture]; |
| + var testBody = fixtureConstructor.testCaseBodies[testName]; |
| + var fixture = new fixtureConstructor(); |
| + fixture.name = testFixture; |
| + return new TestCase(testName, fixture, testBody); |
| } |
| - function preloadJavascriptLibraries(overload_chrome_send) { |
| - if (overload_chrome_send) |
| - chrome = { 'send': send }; |
| + /** |
| + * Used by WebUIBrowserTest to preload the javascript libraries at the |
| + * appropriate time for javascript injection into the current page. This |
| + * creates a test case and calls its PreLoad for any early initialization such |
| + * as registering handlers before the page's javascript runs it's OnLoad |
| + * method. |
| + * @param {String} testFixture the test fixture name. |
| + * @param {String} testName the test name. |
| + **/ |
| + function preloadJavascriptLibraries(testFixture, testName) { |
| + chrome = { |
| + __proto__: oldChrome, |
| + send: send, |
| + }; |
| + currentTestCase = createTestCase(testFixture, testName); |
| + currentTestCase.PreLoad(); |
| + } |
| + |
| + /** |
| + * During generation phase, this outputs; do nothing at runtime. |
| + **/ |
| + function GEN() {} |
| + |
| + /** |
| + * At runtime, register the testName with a test fixture. Since this method |
| + * doesn't have a test fixture, we create a dummy fixture to hold its |name| |
| + * and |testCaseBodies|. |
| + * @param {String} testCaseName the name of the test case. |
| + * @param {String} testName the name of the test function. |
| + * @param {Function} testBody the body to execute when running this test. |
| + **/ |
| + function TEST(testCaseName, testName, testBody) { |
| + if (!(testCaseName in this)) { |
| + var fixtureConstructor = new function() {}; |
| + this[testCaseName] = fixtureConstructor; |
| + fixtureConstructor.prototype.name = testCaseName; |
| + fixtureConstructor.testCaseBodies = {}; |
| + } |
| + fixtureConstructor.testCaseBodies[testName] = testBody; |
| + } |
| + |
| + /** |
| + * At runtime, register the testName with its fixture. Stuff the |name| into |
| + * the |testFixture|'s prototype, if needed, and the |testCaseBodies| into its |
| + * constructor. |
| + * @param {String} testFixture the name of the test fixture class. |
| + * @param {String} testName the name of the test function. |
| + * @param {Function} the body to execute when running this test. |
| + **/ |
| + function TEST_F(testFixture, testName, testBody) { |
| + var fixtureConstructor = this[testFixture]; |
| + if (!fixtureConstructor.prototype.name) |
| + fixtureConstructor.prototype.name = testFixture; |
| + if (!('testCaseBodies' in fixtureConstructor)) |
| + fixtureConstructor.testCaseBodies = {}; |
| + fixtureConstructor.testCaseBodies[testName] = testBody; |
| + } |
| + |
| + /** |
| + * RunJavascriptTestF uses this as the |testFunction| when invoking |
| + * runTest. If |currentTestCase| is non-null at this point, verify that |
| + * |testFixture| and |testName| agree with the preloaded values. Create |
| + * |currentTestCase|, if needed, run it, and clear the |currentTestCase|. |
| + * @param {String} testFixture the name of the test fixture class. |
| + * @param {String} testName the name of the test function. |
| + * @see preloadJavascriptLibraries |
| + * @see runTest |
| + **/ |
| + function RUN_TEST_F(testFixture, testName) { |
| + if (!currentTestCase) |
| + currentTestCase = createTestCase(testFixture, testName); |
| + assertEquals(currentTestCase.name, testName); |
| + assertEquals(currentTestCase.fixture.name, testFixture); |
| + console.log('Running TestCase ' + testFixture + '.' + testName); |
| + currentTestCase.Run(); |
| + currentTestCase = null; |
| + } |
| + |
| + /** |
| + * CallFunctionAction is provided to allow mocks to have side effects. |
| + * @param {Function} func the function to call. |
| + * @param {Array} args any arguments to pass to func. |
| + * @constructor |
| + **/ |
| + function CallFunctionAction(func, args) { |
| + this._func = func; |
| + this._args = args; |
| + } |
| + |
| + CallFunctionAction.prototype = { |
| + invoke: function() { |
| + return this._func.apply(null, this._args); |
| + }, |
| + describe: function() { |
| + return 'calls the given function with arguments ' + this._args; |
| + } |
| + }; |
| + |
| + /** |
| + * Syntactic sugar for will() on a Mock4JS.Mock. |
| + * @param {Function} func the function to call when the method is invoked. |
| + * @param {...*} var_args arguments to pass when calling func. |
| + **/ |
| + function callFunction(func) { |
| + return new CallFunctionAction(func, |
| + Array.prototype.slice.call(arguments, 1)); |
| } |
| // Exports. |
| + testing.Test = Test; |
| window.assertTrue = assertTrue; |
| window.assertFalse = assertFalse; |
| window.assertEquals = assertEquals; |
| window.assertNotReached = assertNotReached; |
| + window.callFunction = callFunction; |
| window.expectTrue = createExpect(assertTrue); |
| window.expectFalse = createExpect(assertFalse); |
| window.expectEquals = createExpect(assertEquals); |
| window.expectNotReached = createExpect(assertNotReached); |
| window.registerMessageCallback = registerMessageCallback; |
| + window.registerMockMessageCallbacks = registerMockMessageCallbacks; |
| window.runTest = runTest; |
| window.preloadJavascriptLibraries = preloadJavascriptLibraries; |
| + window.TEST = TEST; |
| + window.TEST_F = TEST_F; |
| + window.GEN = GEN; |
| + |
| + // Import the Mock4JS helpers. |
| + Mock4JS.addMockSupport(window); |
| })(); |