• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
10var showConsoleLogOutput = false;
11
12/**
13 * Conditionally allow console.log output based off of showConsoleLogOutput.
14 */
15console.log = function() {
16  var originalConsoleLog = console.log;
17  return function() {
18    if (showConsoleLogOutput) {
19      originalConsoleLog.apply(console, arguments);
20    }
21  };
22}();
23
24function emptyMock() {}
25
26// Container for event handlers added by mocked 'addListener' functions.
27var 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 */
37function 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 */
65function 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 */
84var 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 */
194function 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