summaryrefslogtreecommitdiff
path: root/chromium/chrome/browser/resources/google_now/common_test_util.js
blob: fe5708234b67dfa7bcb38b8036e4f7bcd4ffc496 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// 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<Function>} 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)));
  }
}