1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Common test utilities. 6 7 /** 8 * Allows console.log output. 9 */ 10 var showConsoleLogOutput = false; 11 12 /** 13 * Conditionally allow console.log output based off of showConsoleLogOutput. 14 */ 15 console.log = function() { 16 var originalConsoleLog = console.log; 17 return function() { 18 if (showConsoleLogOutput) { 19 originalConsoleLog.apply(console, arguments); 20 } 21 }; 22 }(); 23 24 function emptyMock() {} 25 26 // Container for event handlers added by mocked 'addListener' functions. 27 var mockEventHandlers = {}; 28 29 /** 30 * Mocks 'addListener' function of an API event. The mocked function will keep 31 * track of handlers. 32 * @param {Object} topLevelContainer Top-level container of the original 33 * function. Can be either 'chrome' or 'instrumented'. 34 * @param {string} eventIdentifier Event identifier, such as 35 * 'runtime.onSuspend'. 36 */ 37 function mockChromeEvent(topLevelContainer, eventIdentifier) { 38 var eventIdentifierParts = eventIdentifier.split('.'); 39 var eventName = eventIdentifierParts.pop(); 40 var originalMethodContainer = topLevelContainer; 41 var mockEventContainer = mockEventHandlers; 42 eventIdentifierParts.forEach(function(fragment) { 43 originalMethodContainer = 44 originalMethodContainer[fragment] = 45 originalMethodContainer[fragment] || {}; 46 mockEventContainer = 47 mockEventContainer[fragment] = 48 mockEventContainer[fragment] || {}; 49 }); 50 51 mockEventContainer[eventName] = []; 52 originalMethodContainer[eventName] = { 53 addListener: function(callback) { 54 mockEventContainer[eventName].push(callback); 55 } 56 }; 57 } 58 59 /** 60 * Gets the array of event handlers added by a mocked 'addListener' function. 61 * @param {string} eventIdentifier Event identifier, such as 62 * 'runtime.onSuspend'. 63 * @return {Array.<Function>} Array of handlers. 64 */ 65 function getMockHandlerContainer(eventIdentifier) { 66 var eventIdentifierParts = eventIdentifier.split('.'); 67 var mockEventContainer = mockEventHandlers; 68 eventIdentifierParts.forEach(function(fragment) { 69 mockEventContainer = mockEventContainer[fragment]; 70 }); 71 72 return mockEventContainer; 73 } 74 75 /** 76 * MockPromise 77 * The JS test harness expects all calls to complete synchronously. 78 * As a result, we can't use built-in JS promises since they run asynchronously. 79 * Instead of mocking all possible calls to promises, a skeleton 80 * implementation is provided to get the tests to pass. 81 * 82 * This functionality and logic originates from ECMAScript 6's spec of promises. 83 */ 84 var Promise = function() { 85 function PromisePrototypeObject(asyncTask) { 86 function isThenable(value) { 87 return (typeof value === 'object') && isCallable(value.then); 88 } 89 90 function isCallable(value) { 91 return typeof value === 'function'; 92 } 93 94 function callResolveRejectFunc(func) { 95 var funcResult; 96 var funcResolved = false; 97 func( 98 function(resolveResult) { 99 funcResult = resolveResult; 100 funcResolved = true; 101 }, 102 function(rejectResult) { 103 funcResult = rejectResult; 104 funcResolved = false; 105 }); 106 return { result: funcResult, resolved: funcResolved }; 107 } 108 109 function then(onResolve, onReject) { 110 var resolutionHandler = 111 isCallable(onResolve) ? onResolve : function() { return result; }; 112 var rejectionHandler = 113 isCallable(onReject) ? onReject : function() { return result; }; 114 var handlerResult = 115 resolved ? resolutionHandler(result) : rejectionHandler(result); 116 var promiseResolved = resolved; 117 if (isThenable(handlerResult)) { 118 var resolveReject = callResolveRejectFunc(handlerResult.then); 119 handlerResult = resolveReject.result; 120 promiseResolved = resolveReject.resolved; 121 } 122 123 if (promiseResolved) { 124 return Promise.resolve(handlerResult); 125 } else { 126 return Promise.reject(handlerResult); 127 } 128 } 129 130 // Promises use the function name "catch" to call back error handlers. 131 // We can't use "catch" since function or variable names cannot use the word 132 // "catch". 133 function catchFunc(onRejected) { 134 return this.then(undefined, onRejected); 135 } 136 137 var resolveReject = callResolveRejectFunc(asyncTask); 138 var result = resolveReject.result; 139 var resolved = resolveReject.resolved; 140 141 if (isThenable(result)) { 142 var thenResolveReject = callResolveRejectFunc(result.then); 143 result = thenResolveReject.result; 144 resolved = thenResolveReject.resolved; 145 } 146 147 return {then: then, catch: catchFunc, isPromise: true}; 148 } 149 150 function all(arrayOfPromises) { 151 var results = []; 152 for (i = 0; i < arrayOfPromises.length; i++) { 153 if (arrayOfPromises[i].isPromise) { 154 arrayOfPromises[i].then(function(result) { 155 results[i] = result; 156 }); 157 } else { 158 results[i] = arrayOfPromises[i]; 159 } 160 } 161 var promise = new PromisePrototypeObject(function(resolve) { 162 resolve(results); 163 }); 164 return promise; 165 } 166 167 function resolve(value) { 168 var promise = new PromisePrototypeObject(function(resolve) { 169 resolve(value); 170 }); 171 return promise; 172 } 173 174 function reject(value) { 175 var promise = new PromisePrototypeObject(function(resolve, reject) { 176 reject(value); 177 }); 178 return promise; 179 } 180 181 PromisePrototypeObject.all = all; 182 PromisePrototypeObject.resolve = resolve; 183 PromisePrototypeObject.reject = reject; 184 return PromisePrototypeObject; 185 }(); 186 187 /** 188 * Sets up the test to expect a Chrome Local Storage call. 189 * @param {Object} fixture Mock JS Test Object. 190 * @param {Object} defaultObject Storage request default object. 191 * @param {Object} result Storage result. 192 * @param {boolean=} opt_AllowRejection Allow Promise Rejection 193 */ 194 function expectChromeLocalStorageGet( 195 fixture, defaultObject, result, opt_AllowRejection) { 196 if (opt_AllowRejection === undefined) { 197 fixture.mockApis.expects(once()). 198 fillFromChromeLocalStorage(eqJSON(defaultObject)). 199 will(returnValue(Promise.resolve(result))); 200 } else { 201 fixture.mockApis.expects(once()). 202 fillFromChromeLocalStorage(eqJSON(defaultObject), opt_AllowRejection). 203 will(returnValue(Promise.resolve(result))); 204 } 205 } 206