// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Common test utilities. /** * Allows console.log output. */ var showConsoleLogOutput = false; /** * Conditionally allow console.log output based off of showConsoleLogOutput. */ console.log = function() { var originalConsoleLog = console.log; return function() { if (showConsoleLogOutput) { originalConsoleLog.apply(console, arguments); } }; }(); function emptyMock() {} // Container for event handlers added by mocked 'addListener' functions. var mockEventHandlers = {}; /** * Mocks 'addListener' function of an API event. The mocked function will keep * track of handlers. * @param {Object} topLevelContainer Top-level container of the original * function. Can be either 'chrome' or 'instrumented'. * @param {string} eventIdentifier Event identifier, such as * 'runtime.onSuspend'. */ function mockChromeEvent(topLevelContainer, eventIdentifier) { var eventIdentifierParts = eventIdentifier.split('.'); var eventName = eventIdentifierParts.pop(); var originalMethodContainer = topLevelContainer; var mockEventContainer = mockEventHandlers; eventIdentifierParts.forEach(function(fragment) { originalMethodContainer = originalMethodContainer[fragment] = originalMethodContainer[fragment] || {}; mockEventContainer = mockEventContainer[fragment] = mockEventContainer[fragment] || {}; }); mockEventContainer[eventName] = []; originalMethodContainer[eventName] = { addListener: function(callback) { mockEventContainer[eventName].push(callback); } }; } /** * Gets the array of event handlers added by a mocked 'addListener' function. * @param {string} eventIdentifier Event identifier, such as * 'runtime.onSuspend'. * @return {Array.} Array of handlers. */ function getMockHandlerContainer(eventIdentifier) { var eventIdentifierParts = eventIdentifier.split('.'); var mockEventContainer = mockEventHandlers; eventIdentifierParts.forEach(function(fragment) { mockEventContainer = mockEventContainer[fragment]; }); return mockEventContainer; } /** * MockPromise * The JS test harness expects all calls to complete synchronously. * As a result, we can't use built-in JS promises since they run asynchronously. * Instead of mocking all possible calls to promises, a skeleton * implementation is provided to get the tests to pass. * * This functionality and logic originates from ECMAScript 6's spec of promises. */ var Promise = function() { function PromisePrototypeObject(asyncTask) { function isThenable(value) { return (typeof value === 'object') && isCallable(value.then); } function isCallable(value) { return typeof value === 'function'; } function callResolveRejectFunc(func) { var funcResult; var funcResolved = false; func( function(resolveResult) { funcResult = resolveResult; funcResolved = true; }, function(rejectResult) { funcResult = rejectResult; funcResolved = false; }); return { result: funcResult, resolved: funcResolved }; } function then(onResolve, onReject) { var resolutionHandler = isCallable(onResolve) ? onResolve : function() { return result; }; var rejectionHandler = isCallable(onReject) ? onReject : function() { return result; }; var handlerResult = resolved ? resolutionHandler(result) : rejectionHandler(result); var promiseResolved = resolved; if (isThenable(handlerResult)) { var resolveReject = callResolveRejectFunc(handlerResult.then); handlerResult = resolveReject.result; promiseResolved = resolveReject.resolved; } if (promiseResolved) { return Promise.resolve(handlerResult); } else { return Promise.reject(handlerResult); } } // Promises use the function name "catch" to call back error handlers. // We can't use "catch" since function or variable names cannot use the word // "catch". function catchFunc(onRejected) { return this.then(undefined, onRejected); } var resolveReject = callResolveRejectFunc(asyncTask); var result = resolveReject.result; var resolved = resolveReject.resolved; if (isThenable(result)) { var thenResolveReject = callResolveRejectFunc(result.then); result = thenResolveReject.result; resolved = thenResolveReject.resolved; } return {then: then, catch: catchFunc, isPromise: true}; } function all(arrayOfPromises) { var results = []; for (i = 0; i < arrayOfPromises.length; i++) { if (arrayOfPromises[i].isPromise) { arrayOfPromises[i].then(function(result) { results[i] = result; }); } else { results[i] = arrayOfPromises[i]; } } var promise = new PromisePrototypeObject(function(resolve) { resolve(results); }); return promise; } function resolve(value) { var promise = new PromisePrototypeObject(function(resolve) { resolve(value); }); return promise; } function reject(value) { var promise = new PromisePrototypeObject(function(resolve, reject) { reject(value); }); return promise; } PromisePrototypeObject.all = all; PromisePrototypeObject.resolve = resolve; PromisePrototypeObject.reject = reject; return PromisePrototypeObject; }(); /** * Sets up the test to expect a Chrome Local Storage call. * @param {Object} fixture Mock JS Test Object. * @param {Object} defaultObject Storage request default object. * @param {Object} result Storage result. * @param {boolean=} opt_AllowRejection Allow Promise Rejection */ function expectChromeLocalStorageGet( fixture, defaultObject, result, opt_AllowRejection) { if (opt_AllowRejection === undefined) { fixture.mockApis.expects(once()). fillFromChromeLocalStorage(eqJSON(defaultObject)). will(returnValue(Promise.resolve(result))); } else { fixture.mockApis.expects(once()). fillFromChromeLocalStorage(eqJSON(defaultObject), opt_AllowRejection). will(returnValue(Promise.resolve(result))); } }