Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Unified Diff: chrome/test/data/webui/test_api.js

Issue 7645007: WebUI Testing: async support - global mocking, deferred runs, continued run. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Minor comment fix. Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/test/data/webui/print_preview.js ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
« no previous file with comments | « chrome/test/data/webui/print_preview.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698