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..9b1b250f8e593010627766b18b2587bbc80ecfcb 100644 |
--- a/chrome/test/data/webui/test_api.js |
+++ b/chrome/test/data/webui/test_api.js |
@@ -2,13 +2,233 @@ |
// 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 (this['window'] === undefined) |
+ this['window'] = this; |
+ if (this['chrome'] === undefined) { |
+ this['chrome'] = { |
+ send: function() {}, |
+ }; |
+ } |
+ if (this['console'] === undefined) { |
+ 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(); |
+ } |
+ }; |
+ |
+ /** |
+ * 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. |
+ * @param {Test} fixture The fixture object for this test case. |
+ * @param {Function} body The code to run for the test. |
+ * @constructor |
+ **/ |
+ function TestCase(name, fixture, body) { |
+ this.name = name; |
+ this.fixture = fixture; |
+ this.body = body; |
+ } |
+ |
+ TestCase.prototype = { |
+ name: null, |
+ fixture: null, |
+ body: null, |
+ |
+ /** |
+ * 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 for 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 {Boolean} test The predicate to check against |expected|. |
+ * @param {Boolean} expected The expected value of |test|. |
+ * @param {String=} 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 +239,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 {Boolean} test The predicate to check against |expected|. |
+ * @param {String=} 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 {Boolean} test The predicate to check against |expected|. |
+ * @param {String=} 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 {String=} 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 +278,216 @@ |
} |
} |
+ /** |
+ * Always aborts the current test. |
+ * @param {String=} message The message to include in the Error thrown. |
+ * @throws {Error} always. |
+ **/ |
function assertNotReached(message) { |
throw new Error(message); |
} |
+ /** |
+ * Holds the errors, if any, caught by expects so that the test case can fail. |
+ * @type {Array.<Error>} |
+ **/ |
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 The function name to call. |
+ * @param {Array} testArguments The arguments to call |testFunction| with. |
+ * @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 (this[testCaseName] === undefined) { |
+ 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} testBody 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 (fixtureConstructor['testCaseBodies'] === undefined) |
+ 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); |
})(); |