Home | History | Annotate | Download | only in google_now
      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