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 4e554bd8722b8d8d7051966d576dba1cdbb20ed3..fb8108b93f85acf17551dd4a609e0500aa39986e 100644 |
--- a/chrome/test/data/webui/test_api.js |
+++ b/chrome/test/data/webui/test_api.js |
@@ -4,128 +4,180 @@ |
/** |
* @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; |
- |
-/** |
- * @type {?string} The string representation of the currently running test |
- * function. |
*/ |
-var currentTestFunction = null; |
+var testing = {}; |
+(function(window) { |
+ /** |
+ * Hold the currentTestCase across between preLoad and run. |
+ * @type {TestCase} |
+ */ |
+ var currentTestCase = null; |
-/** |
- * @type {?Array} The arguments of the currently running test. |
- */ |
-var currentTestArguments = 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, |
- }; |
- } |
+ /** |
+ * The string representation of the currently running test function. |
+ * @type {?string} |
+ */ |
+ var currentTestFunction = null; |
/** |
+ * The arguments of the currently running test. |
+ * @type {?Array} |
+ */ |
+ var currentTestArguments = null; |
+ |
+ /** |
* 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 |
+ * 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 |
+ * 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 after 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} |
- **/ |
+ * during the test run. It holds any mocked handler methods. |
+ * @type {?Mock4JS.Mock} |
+ */ |
mockHandler: null, |
/** |
+ * This should be initialized by the test fixture and can be referenced |
+ * during the test run. It holds any mocked global functions. |
+ * @type {?Mock4JS.Mock} |
+ */ |
+ mockGlobals: null, |
+ |
+ /** |
+ * Value is passed through call to C++ RunJavascriptF to invoke this test. |
+ * @type {boolean} |
+ */ |
+ isAsync: false, |
+ |
+ /** |
* Override this method to perform initialization during preload (such as |
* creating mocks and registering handlers). |
* @type {Function} |
- **/ |
- PreLoad: function() {}, |
+ */ |
+ preLoad: function() {}, |
/** |
* Override this method to perform tasks before running your test. |
* @type {Function} |
- **/ |
- SetUp: 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() { |
+ */ |
+ tearDown: function() { |
Mock4JS.verifyAllMocks(); |
- } |
+ }, |
+ |
+ /** |
+ * Called to run the body from the perspective of this fixture. |
+ * @type {Function} |
+ */ |
+ runTest: function(testBody) { |
+ testBody.call(this); |
+ }, |
+ |
+ /** |
+ * Create a closure function for continuing the test at a later time. May be |
+ * used as a listener function. |
+ * @param {WhenTestDone} whenTestDone Call testDone() at the appropriate |
+ * time. |
+ * @param {Function} completion The function to call to complete the test. |
+ * @param {...*} var_args Arguments to pass when calling completionAction. |
+ * @return {function(): void} Return a function, bound to this test fixture, |
+ * which continues the test. |
+ */ |
+ continueTest: function(whenTestDone, completion) { |
+ var savedArgs = new SaveMockArguments(); |
+ var completionAction = new CallFunctionAction( |
+ this, savedArgs, completion, |
+ Array.prototype.slice.call(arguments, 2)); |
+ if (whenTestDone === WhenTestDone.DEFAULT) |
+ whenTestDone = WhenTestDone.ASSERT; |
+ var runAll = new RunAllAction( |
+ true, whenTestDone, [completionAction]); |
+ return function() { |
+ savedArgs.arguments = Array.prototype.slice.call(arguments); |
+ runAll.invoke(); |
+ }; |
+ }, |
+ |
+ /** |
+ * Call this during setUp to defer the call to runTest() until later. The |
+ * caller must call the returned function at some point to run the test. |
+ * @type {Function} |
+ * @param {WhenTestDone} whenTestDone Call testDone() at the appropriate |
+ * time. |
+ * @param {...*} var_args Arguments to pass when running the |
+ * |currentTestCase|. |
+ * @return {function(): void} A function which will run the current body of |
+ * the currentTestCase. |
+ */ |
+ deferRunTest: function(whenTestDone) { |
+ if (whenTestDone === WhenTestDone.DEFAULT) |
+ whenTestDone = WhenTestDone.ALWAYS; |
+ |
+ return currentTestCase.deferRunTest.apply( |
+ currentTestCase, [whenTestDone].concat( |
+ Array.prototype.slice.call(arguments, 1))); |
+ }, |
}; |
/** |
@@ -135,7 +187,7 @@ var currentTestArguments = null; |
* @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; |
@@ -143,17 +195,62 @@ var currentTestArguments = null; |
} |
TestCase.prototype = { |
+ /** |
+ * The name of this test. |
+ * @type {string} |
+ */ |
name: null, |
+ |
+ /** |
+ * The test fixture to set |this| to when running the test |body|. |
+ * @type {testing.Test} |
+ */ |
fixture: null, |
+ |
+ /** |
+ * The test body to execute in runTest(). |
+ * @type {Function} |
+ */ |
body: null, |
/** |
+ * True when the test fixture will run the test later. |
+ * @type {boolean} |
+ * @private |
+ */ |
+ deferred_: false, |
+ |
+ /** |
* Called at preload time, proxies to the fixture. |
* @type {Function} |
- **/ |
- PreLoad: function(name) { |
+ */ |
+ preLoad: function(name) { |
if (this.fixture) |
- this.fixture.PreLoad(); |
+ this.fixture.preLoad(); |
+ }, |
+ |
+ /** |
+ * Called before a test runs. |
+ */ |
+ setUp: function() { |
+ if (this.fixture) |
+ this.fixture.setUp(); |
+ }, |
+ |
+ /** |
+ * Called before a test is torn down (by testDone()). |
+ */ |
+ tearDown: function() { |
+ if (this.fixture) |
+ this.fixture.tearDown(); |
+ }, |
+ |
+ /** |
+ * Called to run this test's body. |
+ */ |
+ runTest: function() { |
+ if (this.body && this.fixture) |
+ this.fixture.runTest(this.body); |
}, |
/** |
@@ -164,30 +261,60 @@ var currentTestArguments = null; |
* dummy constructor, but tests created with TEST should not rely on |this| |
* being set. |
* @type {Function} |
- **/ |
- Run: function() { |
- if (this.fixture) |
- this.fixture.SetUp(); |
- if (this.body) |
- this.body.call(this.fixture); |
- if (this.fixture) |
- this.fixture.TearDown(); |
+ */ |
+ run: function() { |
+ try { |
+ this.setUp(); |
+ } catch(e) { |
+ console.error(e.stack); |
+ } |
+ |
+ if (!this.deferred_) |
+ this.runTest(); |
+ |
+ // tearDown called by testDone(). |
+ }, |
+ |
+ /** |
+ * Cause this TestCase to be deferred (don't call runTest()) until the |
+ * returned function is called. |
+ * @type {Function} |
+ * @param {WhenTestDone} whenTestDone Call testDone() at the appropriate |
+ * time. |
+ * @param {...*} var_args Arguments to pass when running the |
+ * |currentTestCase|. |
+ * @return {function(): void} A function thatwill run this TestCase when |
+ * called. |
+ */ |
+ deferRunTest: function(whenTestDone) { |
+ this.deferred_ = true; |
+ var savedArgs = new SaveMockArguments(); |
+ var completionAction = new CallFunctionAction( |
+ this, savedArgs, this.runTest, |
+ Array.prototype.slice.call(arguments, 1)); |
+ var runAll = new RunAllAction( |
+ true, whenTestDone, [completionAction]); |
+ return function() { |
+ savedArgs.arguments = Array.prototype.slice.call(arguments); |
+ runAll.invoke(); |
+ }; |
}, |
+ |
}; |
/** |
* 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 {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]; |
} |
@@ -199,23 +326,82 @@ var currentTestArguments = null; |
* @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]); |
+ for (var func in mockClass.prototype) { |
+ if (typeof mockClass.prototype[func] === 'function') { |
+ registerMessageCallback(func, mockProxy, mockProxy[func]); |
} |
} |
} |
/** |
+ * Holds the mapping of name -> global override information. |
+ * @type {Object} |
+ */ |
+ var globalOverrides = {}; |
+ |
+ /** |
+ * When preloading JavaScript libraries, this is true until the |
+ * DOMContentLoaded event has been received as globals cannot be overridden |
+ * until the page has loaded its JavaScript. |
+ * @type {boolean} |
+ */ |
+ var deferGlobalOverrides = false; |
+ |
+ /** |
+ * Override the global function |funcName| with its registered mock. This |
+ * should not be called twice for the same |funcName|. |
+ * @param {string} funcName The name of the global function to override. |
+ */ |
+ function overrideGlobal(funcName) { |
+ assertNotEquals(undefined, this[funcName]); |
+ var globalOverride = globalOverrides[funcName]; |
+ assertNotEquals(undefined, globalOverride); |
+ assertEquals(undefined, globalOverride.original); |
+ globalOverride.original = this[funcName]; |
+ this[funcName] = globalOverride.callback.bind(globalOverride.object); |
+ } |
+ |
+ /** |
+ * Registers the global function name, object and callback. |
+ * @param {string} name The name of the message to route to this |callback|. |
+ * @param {Object} object Pass as |this| when calling the |callback|. |
+ * @param {function(...)} callback Called by {@code chrome.send}. |
+ */ |
+ function registerMockGlobal(name, object, callback) { |
+ assertEquals(undefined, globalOverrides[name]); |
+ globalOverrides[name] = { |
+ object: object, |
+ callback: callback, |
+ }; |
+ |
+ if (!deferGlobalOverrides) |
+ overrideGlobal(name); |
+ } |
+ |
+ /** |
+ * Register all methods of {@code mockClass.prototype} as overrides to global |
+ * functions of the same name as the method, using the proxy of the |
+ * |mockObject| to handle the functions. |
+ * @param {Mock4JS.Mock} mockObject The mock to register callbacks against. |
+ * @param {function(new:Object)} mockClass Constructor for the mocked class. |
+ * @see registerMessageCallback |
+ */ |
+ function registerMockGlobals(mockObject, mockClass) { |
+ var mockProxy = mockObject.proxy(); |
+ for (var func in mockClass.prototype) { |
+ if (typeof mockClass.prototype[func] === 'function') |
+ registerMockGlobal(func, mockProxy, mockProxy[func]); |
+ } |
+ } |
+ |
+ /** |
* Overrides {@code chrome.send} for routing messages to javascript |
* functions. Also falls back to sending with the original chrome object. |
* @param {string} messageName The message to route. |
- **/ |
+ */ |
function send(messageName) { |
var callback = sendCallbacks[messageName]; |
if (callback != undefined) |
@@ -229,7 +415,7 @@ var currentTestArguments = null; |
* of their caller. Assert* methods should |registerCall| and expect* methods |
* should set |isExpect| and |expectName| properties to indicate that the |
* interesting caller is one more level up the stack. |
- **/ |
+ */ |
function CallHelper() { |
this.__proto__ = CallHelper.prototype; |
} |
@@ -239,7 +425,7 @@ var currentTestArguments = null; |
* Holds the mapping of (callerCallerString, callerName) -> count of times |
* called. |
* @type {Object.<string, Object.<string, number>>} |
- **/ |
+ */ |
counts_: {}, |
/** |
@@ -248,7 +434,7 @@ var currentTestArguments = null; |
* @param {Function} caller the caller of the assert* routine. |
* @return {{callerName: string, callercallerString: string}} stackInfo |
* @private |
- **/ |
+ */ |
getCallerInfo_: function(caller) { |
var callerName = caller.name; |
var callerCaller = caller.caller; |
@@ -265,7 +451,7 @@ var currentTestArguments = null; |
/** |
* Register a call to an assertion class. |
- **/ |
+ */ |
registerCall: function() { |
var stackInfo = this.getCallerInfo_(arguments.callee.caller); |
if (!(stackInfo.callerCallerString in this.counts_)) |
@@ -281,7 +467,7 @@ var currentTestArguments = null; |
* @param {Function} caller The caller of the assert* routine. |
* @return {String} Call signature. |
* @private |
- **/ |
+ */ |
getCall_: function(caller) { |
var stackInfo = this.getCallerInfo_(caller); |
var count = |
@@ -304,7 +490,7 @@ var currentTestArguments = null; |
/** |
* Returns the text of the call signature and any |message|. |
* @param {string=} message Addtional message text from caller. |
- **/ |
+ */ |
getCallMessage: function(message) { |
var callMessage = this.getCall_(arguments.callee.caller); |
if (message) |
@@ -316,7 +502,7 @@ var currentTestArguments = null; |
/** |
* Help register calls for better error reporting. |
* @type {CallHelper} |
- **/ |
+ */ |
var helper = new CallHelper(); |
/** |
@@ -329,12 +515,18 @@ var currentTestArguments = null; |
* Holds the errors, if any, caught by expects so that the test case can |
* fail. Cleared when results are reported from runTest() or testDone(). |
* @type {Array.<Error>} |
- **/ |
+ */ |
var errors = []; |
/** |
+ * URL to dummy WebUI page for testing framework. |
+ * @type {string} |
+ */ |
+ var DUMMY_URL = 'http://DummyURL'; |
+ |
+ /** |
* Resets test state by clearing |errors| and |testIsDone| flags. |
- **/ |
+ */ |
function resetTestState() { |
errors.splice(0, errors.length); |
testIsDone = false; |
@@ -344,10 +536,18 @@ var currentTestArguments = null; |
* Notifies the running browser test of the test results. Clears |errors|. |
* @param {Array.<boolean, string>=} result When passed, this is used for the |
* testResult message. |
- **/ |
+ */ |
function testDone(result) { |
if (!testIsDone) { |
testIsDone = true; |
+ if (currentTestCase) { |
+ try { |
+ currentTestCase.tearDown(); |
+ } catch (e) { |
+ errors.push(e); |
+ } |
+ currentTestCase = null; |
+ } |
chrome.send('testResult', result ? result : testResult()); |
errors.splice(0, errors.length); |
} else { |
@@ -358,7 +558,7 @@ var currentTestArguments = null; |
/** |
* Returns [success, message] & clears |errors|. |
* @return {Array.<boolean, string>} |
- **/ |
+ */ |
function testResult() { |
var result = [true, '']; |
if (errors.length) { |
@@ -382,7 +582,7 @@ var currentTestArguments = null; |
* @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) { |
helper.registerCall(); |
if (test !== true) |
@@ -395,7 +595,7 @@ var currentTestArguments = null; |
* @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) { |
helper.registerCall(); |
if (test !== false) |
@@ -408,7 +608,7 @@ var currentTestArguments = null; |
* @param {number} val1 The number expected to be >= |val2|. |
* @param {number} val2 The number expected to be < |val1|. |
* @param {string=} message The message to include in the Error thrown. |
- **/ |
+ */ |
function assertGE(val1, val2, message) { |
helper.registerCall(); |
if (val1 < val2) { |
@@ -422,7 +622,7 @@ var currentTestArguments = null; |
* @param {number} val1 The number expected to be > |val2|. |
* @param {number} val2 The number expected to be <= |val1|. |
* @param {string=} message The message to include in the Error thrown. |
- **/ |
+ */ |
function assertGT(val1, val2, message) { |
helper.registerCall(); |
if (val1 <= val2) { |
@@ -437,7 +637,7 @@ var currentTestArguments = null; |
* @param {*} actual The predicate to check against |expected|. |
* @param {string=} message The message to include in the Error thrown. |
* @throws {Error} upon failure. |
- **/ |
+ */ |
function assertEquals(expected, actual, message) { |
helper.registerCall(); |
if (expected != actual) { |
@@ -445,7 +645,7 @@ var currentTestArguments = null; |
'Test Error ' + helper.getCallMessage(message) + |
'\nActual: ' + actual + '\nExpected: ' + expected); |
} |
- if (typeof expected != typeof actual) { |
+ if (typeof expected !== typeof actual) { |
throw new Error( |
'Test Error (type mismatch) ' + helper.getCallMessage(message) + |
'\nActual Type: ' + typeof actual + |
@@ -458,7 +658,7 @@ var currentTestArguments = null; |
* @param {number} val1 The number expected to be <= |val2|. |
* @param {number} val2 The number expected to be > |val1|. |
* @param {string=} message The message to include in the Error thrown. |
- **/ |
+ */ |
function assertLE(val1, val2, message) { |
helper.registerCall(); |
if (val1 > val2) { |
@@ -472,7 +672,7 @@ var currentTestArguments = null; |
* @param {number} val1 The number expected to be < |val2|. |
* @param {number} val2 The number expected to be >= |val1|. |
* @param {string=} message The message to include in the Error thrown. |
- **/ |
+ */ |
function assertLT(val1, val2, message) { |
helper.registerCall(); |
if (val1 >= val2) { |
@@ -487,7 +687,7 @@ var currentTestArguments = null; |
* @param {*} actual The predicate to check against |notExpected|. |
* @param {string=} message The message to include in the Error thrown. |
* @throws {Error} upon failure. |
- **/ |
+ */ |
function assertNotEquals(notExpected, actual, message) { |
helper.registerCall(); |
if (notExpected === actual) { |
@@ -501,7 +701,7 @@ var currentTestArguments = null; |
* Always aborts the current test. |
* @param {string=} message The message to include in the Error thrown. |
* @throws {Error} always. |
- **/ |
+ */ |
function assertNotReached(message) { |
helper.registerCall(); |
throw new Error(helper.getCallMessage(message)); |
@@ -516,7 +716,7 @@ var currentTestArguments = null; |
* @return {Function} A function that applies its arguments to |assertFunc|. |
* @see errors |
* @see runTest |
- **/ |
+ */ |
function createExpect(assertFunc) { |
var expectFunc = function() { |
try { |
@@ -543,7 +743,7 @@ var currentTestArguments = null; |
* necessarily successful results) of this test. |
* @see errors |
* @see runTestFunction |
- **/ |
+ */ |
function runTest(isAsync, testFunction, testArguments) { |
// Avoid eval() if at all possible, since it will not work on pages |
// that have enabled content-security-policy. |
@@ -574,7 +774,7 @@ var currentTestArguments = null; |
* @return {Array.<boolean, string>} [test-succeeded, message-if-failed] |
* @see createExpect |
* @see testDone |
- **/ |
+ */ |
function runTestFunction(testFunction, testBody, testArguments) { |
currentTestFunction = testFunction; |
currentTestArguments = testArguments; |
@@ -588,7 +788,7 @@ var currentTestArguments = null; |
* @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]; |
@@ -600,7 +800,7 @@ var currentTestArguments = null; |
/** |
* 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 |
+ * 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. This is called before the page is loaded, so the |chrome| object is |
* not yet bound and this DOMContentLoaded listener will be called first to |
@@ -608,22 +808,30 @@ var currentTestArguments = null; |
* @param {string} testFixture The test fixture name. |
* @param {string} testName The test name. |
* @see sendCallbacks |
- **/ |
+ */ |
function preloadJavascriptLibraries(testFixture, testName) { |
+ deferGlobalOverrides = true; |
+ |
window.addEventListener('DOMContentLoaded', function() { |
var oldChrome = chrome; |
chrome = { |
__proto__: oldChrome, |
send: send, |
}; |
+ |
+ // Override globals at load time so they will be defined. |
+ assertTrue(deferGlobalOverrides); |
+ deferGlobalOverrides = false; |
+ for (var funcName in globalOverrides) |
+ overrideGlobal(funcName); |
}); |
currentTestCase = createTestCase(testFixture, testName); |
- currentTestCase.PreLoad(); |
+ currentTestCase.preLoad(); |
} |
/** |
* During generation phase, this outputs; do nothing at runtime. |
- **/ |
+ */ |
function GEN() {} |
/** |
@@ -633,7 +841,7 @@ var currentTestArguments = null; |
* @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) { |
var fixtureConstructor = this[testCaseName]; |
if (fixtureConstructor === undefined) { |
@@ -655,7 +863,7 @@ var currentTestArguments = null; |
* @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) |
@@ -674,94 +882,373 @@ var currentTestArguments = null; |
* @param {string} testName The name of the test function. |
* @see preloadJavascriptLibraries |
* @see runTest |
- **/ |
+ */ |
function RUN_TEST_F(testFixture, testName) { |
- try { |
- if (!currentTestCase) |
- currentTestCase = createTestCase(testFixture, testName); |
- assertEquals(currentTestCase.name, testName); |
- assertEquals(currentTestCase.fixture.name, testFixture); |
- console.log('Running TestCase ' + testFixture + '.' + testName); |
- currentTestCase.Run(); |
- } finally { |
- currentTestCase = null; |
- } |
+ if (!currentTestCase) |
+ currentTestCase = createTestCase(testFixture, testName); |
+ assertEquals(currentTestCase.name, testName); |
+ assertEquals(currentTestCase.fixture.name, testFixture); |
+ console.log('Running TestCase ' + testFixture + '.' + testName); |
+ currentTestCase.run(); |
} |
/** |
+ * This Mock4JS matcher object pushes each |actualArgument| parameter to |
+ * match() calls onto |args|. |
+ * @param {Array} args The array to push |actualArgument| onto. |
+ * @param {Object} realMatcher The real matcher check arguments with. |
+ * @constructor |
+ * @extends {realMatcher} |
+ */ |
+ function SaveMockArgumentMatcher(args, realMatcher) { |
+ this.arguments_ = args; |
+ this.realMatcher_ = realMatcher; |
+ } |
+ |
+ SaveMockArgumentMatcher.prototype = { |
+ /** |
+ * Holds the arguments to push each |actualArgument| onto. |
+ * @type {Array} |
+ * @private |
+ */ |
+ arguments_: null, |
+ |
+ /** |
+ * The real Mock4JS matcher object to check arguments with. |
+ * @type {Object} |
+ */ |
+ realMatcher_: null, |
+ |
+ /** |
+ * Pushes |actualArgument| onto |arguments_| and call |realMatcher_|. Clears |
+ * |arguments_| on non-match. |
+ * @param {*} actualArgument The argument to match and save. |
+ * @return {boolean} Result of calling the |realMatcher|. |
+ */ |
+ argumentMatches: function(actualArgument) { |
+ this.arguments_.push(actualArgument); |
+ var match = this.realMatcher_.argumentMatches(actualArgument); |
+ if (!match) |
+ this.arguments_.splice(0, this.arguments_.length); |
+ |
+ return match; |
+ }, |
+ |
+ /** |
+ * Proxy to |realMatcher_| for description. |
+ * @return {string} Description of this Mock4JS matcher. |
+ */ |
+ describe: function() { |
+ return this.realMatcher_.describe(); |
+ }, |
+ }; |
+ |
+ /** |
+ * Actions invoked by Mock4JS's "will()" syntax do not receive arguments from |
+ * the mocked method. This class works with SaveMockArgumentMatcher to save |
+ * arguments so that the invoked Action can pass arguments through to the |
+ * invoked function. |
+ * @param {!Object} realMatcher The real matcher to perform matching with. |
+ * @constructor |
+ */ |
+ function SaveMockArguments() { |
+ this.arguments = []; |
+ } |
+ |
+ SaveMockArguments.prototype = { |
+ /** |
+ * Wraps the |realMatcher| with an object which will push its argument onto |
+ * |arguments| and call realMatcher. |
+ * @param {Object} realMatcher A Mock4JS matcher object for this argument. |
+ * @return {SaveMockArgumentMatcher} A new matcher which will push its |
+ * argument onto |arguments|. |
+ */ |
+ match: function(realMatcher) { |
+ return new SaveMockArgumentMatcher(this.arguments, realMatcher); |
+ }, |
+ |
+ /** |
+ * Remember the argument passed to this stub invocation. |
+ * @type {Array} |
+ */ |
+ arguments: null, |
+ }; |
+ |
+ /** |
* CallFunctionAction is provided to allow mocks to have side effects. |
+ * @param {Object} obj The object to set |this| to when calling |func_|. |
+ * @param {?SaveMockArguments} savedArgs when non-null, saved arguments are |
+ * passed to |func|. |
* @param {Function} func The function to call. |
- * @param {Array} args Any arguments to pass to func. |
+ * @param {Array=} args Any arguments to pass to func. |
* @constructor |
- **/ |
- function CallFunctionAction(func, args) { |
- this._func = func; |
- this._args = args; |
+ */ |
+ function CallFunctionAction(obj, savedArgs, func, args) { |
+ this.obj_ = obj; |
+ this.savedArgs_ = savedArgs; |
+ this.func_ = func; |
+ this.args_ = args ? args : []; |
} |
CallFunctionAction.prototype = { |
+ /** |
+ * Set |this| to |obj_| when calling |func_|. |
+ * @type {?Object} |
+ */ |
+ obj_: null, |
+ |
+ /** |
+ * The SaveMockArguments to hold arguments when invoking |func_|. |
+ * @type {?SaveMockArguments} |
+ * @private |
+ */ |
+ savedArgs_: null, |
+ |
+ /** |
+ * The function to call when invoked. |
+ * @type {!Function} |
+ * @private |
+ */ |
+ func_: null, |
+ |
+ /** |
+ * Arguments to pass to |func_| when invoked. |
+ * @type {!Array} |
+ */ |
+ args_: null, |
+ |
+ /** |
+ * Accessor for |func_|. |
+ * @return {Function} The function to invoke. |
+ */ |
+ get func() { |
+ return this.func_; |
+ }, |
+ |
+ /** |
+ * Called by Mock4JS when using .will() to specify actions for stubs() or |
+ * expects(). Clears |savedArgs_| so it can be reused. |
+ * @return The results of calling |func_| with the concatenation of |
+ * |savedArgs_| and |args_|. |
+ */ |
invoke: function() { |
- return this._func.apply(null, this._args); |
+ var prependArgs = []; |
+ if (this.savedArgs_) { |
+ prependArgs = this.savedArgs_.arguments.splice( |
+ 0, this.savedArgs_.arguments.length); |
+ } |
+ return this.func.apply(this.obj_, prependArgs.concat(this.args_)); |
}, |
+ |
+ /** |
+ * Describe this action to Mock4JS. |
+ * @return {string} A description of this action. |
+ */ |
describe: function() { |
- return 'calls the given function with arguments ' + this._args; |
+ return 'calls the given function with saved arguments and ' + 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. |
- **/ |
+ * Syntactic sugar for use with 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. |
+ * @return {CallFunctionAction} Action for use in will. |
+ */ |
function callFunction(func) { |
- return new CallFunctionAction(func, |
- Array.prototype.slice.call(arguments, 1)); |
+ return new CallFunctionAction( |
+ null, null, func, Array.prototype.slice.call(arguments, 1)); |
} |
/** |
- * Allow mock stubs() and expects() to know what arguments were passed to the |
- * |realMatcher|. |
- * @param {!Object} realMatcher The real matcher to perform matching with. |
+ * Syntactic sugar for use with will() on a Mock4JS.Mock. |
+ * @param {SaveMockArguments} savedArgs Arguments saved with this object |
+ * are passed to |func|. |
+ * @param {Function} func The function to call when the method is invoked. |
+ * @param {...*} var_args Arguments to pass when calling func. |
+ * @return {CallFunctionAction} Action for use in will. |
+ */ |
+ function callFunctionWithSavedArgs(savedArgs, func) { |
+ return new CallFunctionAction( |
+ null, savedArgs, func, Array.prototype.slice.call(arguments, 2)); |
+ } |
+ |
+ /** |
+ * CallGlobalAction as a subclass of CallFunctionAction looks up the original |
+ * global object in |globalOverrides| using |funcName| as the key. This allows |
+ * tests, which need to wait until a global function to be called in order to |
+ * start the test to run the original function. When used with runAllActions |
+ * or runAllActionsAsync, Mock4JS expectations may call start or continue the |
+ * test after calling the original function. |
+ * @param {?SaveMockArguments} savedArgs when non-null, saved arguments are |
+ * passed to the global function |funcName|. |
+ * @param {string} funcName The name of the global function to call. |
+ * @param {Array} args Any arguments to pass to func. |
* @constructor |
- **/ |
- function SaveArgumentsMatcher(realMatcher) { |
- this.realMatcher_ = realMatcher; |
+ * @extends {CallFunctionAction} |
+ * @see globalOverrides |
+ */ |
+ function CallGlobalAction(savedArgs, funcName, args) { |
+ CallFunctionAction.call(this, null, savedArgs, funcName, args); |
} |
- SaveArgumentsMatcher.prototype = { |
+ CallGlobalAction.prototype = { |
+ __proto__: CallFunctionAction.prototype, |
+ |
/** |
- * Remember the argument passed to this stub invocation. |
- * @type {*} |
- **/ |
- argument: undefined, |
+ * Fetch and return the original global function to call. |
+ * @return {Function} The global function to invoke. |
+ * @override |
+ */ |
+ get func() { |
+ var func = globalOverrides[this.func_].original; |
+ assertNotEquals(undefined, func); |
+ return func; |
+ }, |
+ }; |
+ |
+ /** |
+ * Syntactic sugar for use with will() on a Mock4JS.Mock. |
+ * @param {SaveMockArguments} savedArgs Arguments saved with this object |
+ * are passed to the global function |funcName|. |
+ * @param {string} funcName The name of a registered mock global function to |
+ * call when the method is invoked. |
+ * @param {...*} var_args Arguments to pass when calling func. |
+ * @return {CallGlobalAction} Action for use in Mock4JS will(). |
+ */ |
+ function callGlobalWithSavedArgs(savedArgs, funcName) { |
+ return new CallGlobalAction( |
+ savedArgs, funcName, Array.prototype.slice.call(arguments, 2)); |
+ } |
+ |
+ /** |
+ * When to call testDone(). |
+ * @enum {number} |
+ */ |
+ var WhenTestDone = { |
+ /** |
+ * Default for the method called. |
+ */ |
+ DEFAULT: -1, |
+ |
+ /** |
+ * Never call testDone(). |
+ */ |
+ NEVER: 0, |
+ |
+ /** |
+ * Call testDone() on assert failure. |
+ */ |
+ ASSERT: 1, |
+ |
+ /** |
+ * Call testDone() if there are any assert or expect failures. |
+ */ |
+ EXPECT: 2, |
+ |
+ /** |
+ * Always call testDone(). |
+ */ |
+ ALWAYS: 3, |
+ }; |
+ |
+ /** |
+ * Runs all |actions|. |
+ * @param {boolean} isAsync When true, call testDone() on Errors. |
+ * @param {WhenTestDone} whenTestDone Call testDone() at the appropriate |
+ * time. |
+ * @param {Array.<Object>} actions Actions to run. |
+ * @constructor |
+ */ |
+ function RunAllAction(isAsync, whenTestDone, actions) { |
+ this.isAsync_ = isAsync; |
+ this.whenTestDone_ = whenTestDone; |
+ this.actions_ = actions; |
+ } |
+ RunAllAction.prototype = { |
/** |
- * @type {Object} the object performing the real match. |
+ * When true, call testDone() on Errors. |
+ * @type {boolean} |
* @private |
*/ |
- realMatcher_: null, |
+ isAsync_: false, |
/** |
- * Saves |actualArgument| for later use by the mock stub or expect. |
- * @param {*} actualArgument The argument to match and save. |
- * @return {boolean} value of calling the |realMatcher_|. |
- **/ |
- argumentMatches: function(actualArgument) { |
- this.argument = actualArgument; |
- return this.realMatcher_.argumentMatches.call(this.realMatcher_, |
- actualArgument); |
+ * Call testDone() at appropriate time. |
+ * @type {WhenTestDone} |
+ * @private |
+ * @see WhenTestDone |
+ */ |
+ whenTestDone_: WhenTestDone.ASSERT, |
+ |
+ /** |
+ * Holds the actions to execute when invoked. |
+ * @type {Array} |
+ * @private |
+ */ |
+ actions_: null, |
+ |
+ /** |
+ * Runs all |actions_|, returning the last one. When running in sync mode, |
+ * throws any exceptions to be caught by runTest() or |
+ * runTestFunction(). Call testDone() according to |whenTestDone_| setting. |
+ */ |
+ invoke: function() { |
+ try { |
+ var result; |
+ for(var i = 0; i < this.actions_.length; ++i) |
+ result = this.actions_[i].invoke(); |
+ |
+ if ((this.whenTestDone_ == WhenTestDone.EXPECT && errors.length) || |
+ this.whenTestDone_ == WhenTestDone.ALWAYS) |
+ testDone(); |
+ |
+ return result; |
+ } catch (e) { |
+ if (!(e instanceof Error)) |
+ e = new Error(e.toString()); |
+ |
+ if (!this.isAsync_) |
+ throw e; |
+ |
+ errors.push(e); |
+ if (this.whenTestDone_ != WhenTestDone.NEVER) |
+ testDone(); |
+ } |
}, |
/** |
- * Generic description for this Matcher object. |
- * @return {string} description of the matcher for this argument. |
- **/ |
+ * Describe this action to Mock4JS. |
+ * @return {string} A description of this action. |
+ */ |
describe: function() { |
- return 'SaveArguments(' + |
- this.realMatcher_.describe.call(this.realMatcher_) + ')'; |
+ return 'Calls all actions: ' + this.actions_; |
}, |
}; |
+ /** |
+ * Syntactic sugar for use with will() on a Mock4JS.Mock. |
+ * @param {...Object} var_actions Actions to run. |
+ * @return {RunAllAction} Action for use in will. |
+ */ |
+ function runAllActions() { |
+ return new RunAllAction(false, WhenTestDone.NEVER, |
+ Array.prototype.slice.call(arguments)); |
+ } |
+ |
+ /** |
+ * Syntactic sugar for use with will() on a Mock4JS.Mock. |
+ * @param {WhenTestDone} whenTestDone Call testDone() at the appropriate |
+ * time. |
+ * @param {...Object} var_actions Actions to run. |
+ * @return {RunAllAction} Action for use in will. |
+ */ |
+ function runAllActionsAsync(whenTestDone) { |
+ return new RunAllAction(true, whenTestDone, |
+ Array.prototype.slice.call(arguments, 1)); |
+ } |
+ |
// Exports. |
testing.Test = Test; |
window.testDone = testDone; |
@@ -775,6 +1262,8 @@ var currentTestArguments = null; |
window.assertNotEquals = assertNotEquals; |
window.assertNotReached = assertNotReached; |
window.callFunction = callFunction; |
+ window.callFunctionWithSavedArgs = callFunctionWithSavedArgs; |
+ window.callGlobalWithSavedArgs = callGlobalWithSavedArgs; |
window.expectTrue = createExpect(assertTrue); |
window.expectFalse = createExpect(assertFalse); |
window.expectGE = createExpect(assertGE); |
@@ -786,15 +1275,20 @@ var currentTestArguments = null; |
window.expectNotReached = createExpect(assertNotReached); |
window.preloadJavascriptLibraries = preloadJavascriptLibraries; |
window.registerMessageCallback = registerMessageCallback; |
+ window.registerMockGlobals = registerMockGlobals; |
window.registerMockMessageCallbacks = registerMockMessageCallbacks; |
window.resetTestState = resetTestState; |
+ window.runAllActions = runAllActions; |
+ window.runAllActionsAsync = runAllActionsAsync; |
window.runTest = runTest; |
window.runTestFunction = runTestFunction; |
- window.SaveArgumentsMatcher = SaveArgumentsMatcher; |
+ window.SaveMockArguments = SaveMockArguments; |
+ window.DUMMY_URL = DUMMY_URL; |
window.TEST = TEST; |
window.TEST_F = TEST_F; |
window.GEN = GEN; |
+ window.WhenTestDone = WhenTestDone; |
// Import the Mock4JS helpers. |
Mock4JS.addMockSupport(window); |
-})(); |
+})(('window' in this) ? window : this); |