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 // TODO(robliao,vadimt): Determine the granularity of testing to perform.
      6 
      7 /**
      8  * Test fixture for background.js.
      9  * @constructor
     10  * @extends {testing.Test}
     11  */
     12 function GoogleNowBackgroundUnitTest () {
     13   testing.Test.call(this);
     14 }
     15 
     16 GoogleNowBackgroundUnitTest.prototype = {
     17   __proto__: testing.Test.prototype,
     18 
     19   /** @override */
     20   extraLibraries: [
     21     'common_test_util.js',
     22     'background_test_util.js',
     23     'background.js'
     24   ]
     25 };
     26 
     27 var TEST_NAME = 'GoogleNowBackgroundUnitTest';
     28 
     29 /**
     30  * Tasks Conflict Test
     31  */
     32 TEST_F(TEST_NAME, 'AreTasksConflicting', function() {
     33   function testTaskPair(newTaskName, scheduledTaskName, expected) {
     34     assertTrue(areTasksConflicting(newTaskName, scheduledTaskName) == expected,
     35                '(' + newTaskName + ', ' + scheduledTaskName + ')');
     36   }
     37 
     38   testTaskPair(UPDATE_CARDS_TASK_NAME, UPDATE_CARDS_TASK_NAME, true);
     39   testTaskPair(UPDATE_CARDS_TASK_NAME, DISMISS_CARD_TASK_NAME, false);
     40   testTaskPair(UPDATE_CARDS_TASK_NAME, RETRY_DISMISS_TASK_NAME, false);
     41   testTaskPair(UPDATE_CARDS_TASK_NAME, STATE_CHANGED_TASK_NAME, false);
     42 
     43   testTaskPair(DISMISS_CARD_TASK_NAME, UPDATE_CARDS_TASK_NAME, false);
     44   testTaskPair(DISMISS_CARD_TASK_NAME, DISMISS_CARD_TASK_NAME, false);
     45   testTaskPair(DISMISS_CARD_TASK_NAME, RETRY_DISMISS_TASK_NAME, false);
     46   testTaskPair(DISMISS_CARD_TASK_NAME, STATE_CHANGED_TASK_NAME, false);
     47 
     48   testTaskPair(RETRY_DISMISS_TASK_NAME, UPDATE_CARDS_TASK_NAME, true);
     49   testTaskPair(RETRY_DISMISS_TASK_NAME, DISMISS_CARD_TASK_NAME, true);
     50   testTaskPair(RETRY_DISMISS_TASK_NAME, RETRY_DISMISS_TASK_NAME, true);
     51   testTaskPair(RETRY_DISMISS_TASK_NAME, STATE_CHANGED_TASK_NAME, false);
     52 
     53   testTaskPair(STATE_CHANGED_TASK_NAME, UPDATE_CARDS_TASK_NAME, false);
     54   testTaskPair(STATE_CHANGED_TASK_NAME, DISMISS_CARD_TASK_NAME, false);
     55   testTaskPair(STATE_CHANGED_TASK_NAME, RETRY_DISMISS_TASK_NAME, false);
     56   testTaskPair(STATE_CHANGED_TASK_NAME, STATE_CHANGED_TASK_NAME, false);
     57 });
     58 
     59 /**
     60  * Server Request Tests
     61  */
     62 TEST_F(TEST_NAME, 'AuthServerRequestSuccess', function() {
     63   expectServerRequests(this, 200, '{}');
     64   var callbackCalled = false;
     65   requestFromServer('GET', 'test/target').then(function(request) {
     66     callbackCalled = true;
     67     assertTrue(request.status === 200);
     68     assertTrue(request.responseText === '{}');
     69   });
     70   assertTrue(callbackCalled);
     71 });
     72 
     73 TEST_F(TEST_NAME, 'AuthServerRequestForbidden', function() {
     74   this.makeAndRegisterMockApis(['authenticationManager.removeToken']);
     75   this.mockApis.expects(once()).authenticationManager_removeToken(ANYTHING);
     76 
     77   expectServerRequests(this, 403, '');
     78 
     79   var thenCalled = false;
     80   var catchCalled = false;
     81   requestFromServer('GET', 'test/target').then(function(request) {
     82     thenCalled = true;
     83   }).catch(function(request) {
     84     // The promise is rejected on HTTP failures.
     85     catchCalled = true;
     86     assertTrue(request.status === 403);
     87   });
     88   assertFalse(thenCalled);
     89   assertTrue(catchCalled);
     90 });
     91 
     92 TEST_F(TEST_NAME, 'AuthServerRequestNoAuth', function() {
     93   this.makeAndRegisterMockApis(['authenticationManager.removeToken']);
     94   this.mockApis.expects(once()).authenticationManager_removeToken(ANYTHING);
     95 
     96   expectServerRequests(this, 401, '');
     97 
     98   var thenCalled = false;
     99   var catchCalled = false;
    100   requestFromServer('GET', 'test/target').then(function(request) {
    101     thenCalled = true;
    102   }).catch(function(request) {
    103     // The promise is rejected on HTTP failures.
    104     catchCalled = true;
    105     assertTrue(request.status === 401);
    106   });
    107   assertFalse(thenCalled);
    108   assertTrue(catchCalled);
    109 });
    110 
    111 function expectServerRequests(fixture, httpStatus, responseText) {
    112   fixture.makeAndRegisterMockApis([
    113     'authenticationManager.getAuthToken',
    114     'buildServerRequest'
    115   ]);
    116 
    117   function XMLHttpRequest() {}
    118 
    119   XMLHttpRequest.prototype = {
    120     addEventListener: function(type, listener, wantsUntrusted) {},
    121     setRequestHeader: function(header, value) {},
    122     send: function() {}
    123   }
    124 
    125   fixture.mockApis.expects(once()).authenticationManager_getAuthToken()
    126       .will(returnValue(Promise.resolve('token')));
    127 
    128   var mockXMLHttpRequest = mock(XMLHttpRequest);
    129   var mockXMLHttpRequestProxy = mockXMLHttpRequest.proxy();
    130   fixture.mockApis.expects(once())
    131       .buildServerRequest(ANYTHING, ANYTHING, ANYTHING)
    132       .will(returnValue(mockXMLHttpRequestProxy));
    133 
    134   mockXMLHttpRequest.expects(once())
    135       .setRequestHeader('Authorization', 'Bearer token');
    136 
    137   var loadEndSavedArgs = new SaveMockArguments();
    138   mockXMLHttpRequest.expects(once())
    139       .addEventListener(
    140           loadEndSavedArgs.match(eq('loadend')),
    141           loadEndSavedArgs.match(ANYTHING),
    142           loadEndSavedArgs.match(eq(false)));
    143 
    144   mockXMLHttpRequestProxy.status = httpStatus;
    145   mockXMLHttpRequestProxy.response = responseText;
    146   mockXMLHttpRequestProxy.responseText = responseText;
    147 
    148   mockXMLHttpRequest.expects(once()).send()
    149       .will(invokeCallback(loadEndSavedArgs, 1, mockXMLHttpRequestProxy));
    150 }
    151 
    152 TEST_F(TEST_NAME, 'AuthServerRequestNoToken', function() {
    153   this.makeAndRegisterMockApis([
    154     'authenticationManager.getAuthToken',
    155     'buildServerRequest'
    156   ]);
    157 
    158   this.mockApis.expects(once()).authenticationManager_getAuthToken()
    159       .will(returnValue(Promise.reject()));
    160   this.mockApis.expects(never()).buildServerRequest()
    161 
    162   var thenCalled = false;
    163   var catchCalled = false;
    164   requestFromServer('GET', 'test/target').then(function(request) {
    165     thenCalled = true;
    166   }).catch(function() {
    167     catchCalled = true;
    168   });
    169   assertFalse(thenCalled);
    170   assertTrue(catchCalled);
    171 })
    172 
    173 /**
    174  * requestNotificationGroupsFromServer Tests
    175  */
    176 TEST_F(TEST_NAME, 'RequestNotificationGroupsFromServerEmpty', function() {
    177   this.makeAndRegisterMockGlobals([
    178     'shouldShowExplanatoryCard',
    179     'recordEvent',
    180     'requestFromServer'
    181   ]);
    182 
    183   this.mockGlobals.expects(once()).shouldShowExplanatoryCard()
    184       .will(returnValue(false));
    185 
    186   this.mockGlobals.expects(once()).recordEvent(
    187       GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL);
    188 
    189   this.mockGlobals.expects(once()).recordEvent(
    190       GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS);
    191 
    192   var requestFromServerArgs = new SaveMockArguments();
    193   this.mockGlobals.expects(once()).requestFromServer(
    194       requestFromServerArgs.match(eq('GET')),
    195       requestFromServerArgs.match(ANYTHING))
    196       .will(returnValue(
    197           Promise.resolve({status: 200, responseText: "{}"})));
    198 
    199   var thenCalled = false;
    200   var catchCalled = false;
    201   requestNotificationGroupsFromServer([]).then(function() {
    202     thenCalled = true;
    203   }).catch(function() {
    204     catchCalled = true;
    205   });
    206   assertTrue(thenCalled);
    207   assertFalse(catchCalled);
    208 
    209   var pathAndQuery = requestFromServerArgs.arguments[1];
    210   var query = pathAndQuery.split('?')[1];
    211   assertTrue(query.search('timeZoneOffsetMs') >= 0);
    212   assertTrue(query.search('uiLocale') >= 0);
    213   assertFalse(query.search('cardExplanation') >= 0);
    214 });
    215 
    216 TEST_F(TEST_NAME, 'RequestNotificationGroupsFromServerWithGroups', function() {
    217   this.makeAndRegisterMockGlobals([
    218     'shouldShowExplanatoryCard',
    219     'recordEvent',
    220     'requestFromServer'
    221   ]);
    222 
    223   this.mockGlobals.expects(once()).shouldShowExplanatoryCard()
    224       .will(returnValue(false));
    225 
    226   this.mockGlobals.expects(once()).recordEvent(
    227       GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL);
    228 
    229   this.mockGlobals.expects(once()).recordEvent(
    230       GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS);
    231 
    232   var requestFromServerArgs = new SaveMockArguments();
    233   this.mockGlobals.expects(once()).requestFromServer(
    234       requestFromServerArgs.match(eq('GET')),
    235       requestFromServerArgs.match(ANYTHING))
    236       .will(returnValue(
    237           Promise.resolve({status: 200, responseText: "{}"})));
    238 
    239   var thenCalled = false;
    240   var catchCalled = false;
    241   requestNotificationGroupsFromServer(['A', 'B', 'C']).then(function() {
    242     thenCalled = true;
    243   }).catch(function() {
    244     catchCalled = true;
    245   });
    246   assertTrue(thenCalled);
    247   assertFalse(catchCalled);
    248 
    249   var pathAndQuery = requestFromServerArgs.arguments[1];
    250   var query = pathAndQuery.split('?')[1];
    251   assertTrue(query.search('timeZoneOffsetMs') >= 0);
    252   assertTrue(query.search('uiLocale') >= 0);
    253   assertFalse(query.search('cardExplanation') >= 0);
    254   assertTrue(query.search('requestTypes=A') >= 0);
    255   assertTrue(query.search('requestTypes=B') >= 0);
    256   assertTrue(query.search('requestTypes=C') >= 0);
    257 });
    258 
    259 TEST_F(TEST_NAME, 'RequestNotificationGroupsFromServerExplanatory', function() {
    260   this.makeAndRegisterMockGlobals([
    261     'shouldShowExplanatoryCard',
    262     'recordEvent',
    263     'requestFromServer'
    264   ]);
    265 
    266   this.mockGlobals.expects(once()).shouldShowExplanatoryCard()
    267       .will(returnValue(true));
    268 
    269   this.mockGlobals.expects(once()).recordEvent(
    270       GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL);
    271 
    272   this.mockGlobals.expects(once()).recordEvent(
    273       GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS);
    274 
    275   var requestFromServerArgs = new SaveMockArguments();
    276   this.mockGlobals.expects(once()).requestFromServer(
    277       requestFromServerArgs.match(eq('GET')),
    278       requestFromServerArgs.match(ANYTHING))
    279       .will(returnValue(
    280           Promise.resolve({status: 200, responseText: "{}"})));
    281 
    282   var thenCalled = false;
    283   var catchCalled = false;
    284   requestNotificationGroupsFromServer([]).then(function() {
    285     thenCalled = true;
    286   }).catch(function() {
    287     catchCalled = true;
    288   });
    289   assertTrue(thenCalled);
    290   assertFalse(catchCalled);
    291 
    292   var pathAndQuery = requestFromServerArgs.arguments[1];
    293   var query = pathAndQuery.split('?')[1];
    294   assertTrue(query.search('timeZoneOffsetMs') >= 0);
    295   assertTrue(query.search('uiLocale') >= 0);
    296   assertTrue(query.search('cardExplanation=true') >= 0);
    297 });
    298 
    299 TEST_F(TEST_NAME, 'RequestNotificationGroupsFromServerFailure', function() {
    300   this.makeAndRegisterMockGlobals([
    301     'shouldShowExplanatoryCard',
    302     'recordEvent',
    303     'requestFromServer'
    304   ]);
    305 
    306   this.mockGlobals.expects(once()).shouldShowExplanatoryCard()
    307       .will(returnValue(false));
    308 
    309   this.mockGlobals.expects(once()).recordEvent(
    310       GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL);
    311 
    312   this.mockGlobals.expects(never()).recordEvent(
    313       GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS);
    314 
    315   var requestFromServerArgs = new SaveMockArguments();
    316   this.mockGlobals.expects(once()).requestFromServer(
    317       requestFromServerArgs.match(eq('GET')),
    318       requestFromServerArgs.match(ANYTHING))
    319       .will(returnValue(
    320           Promise.reject({status: 401})));
    321 
    322   var thenCalled = false;
    323   var catchCalled = false;
    324   requestNotificationGroupsFromServer([]).then(function() {
    325     thenCalled = true;
    326   }).catch(function() {
    327     catchCalled = true;
    328   });
    329   assertFalse(thenCalled);
    330   assertTrue(catchCalled);
    331 });
    332 
    333 /**
    334  * requestAndUpdateOptIn Tests
    335  */
    336 TEST_F(TEST_NAME, 'RequestAndUpdateOptInOptedIn', function() {
    337   this.makeAndRegisterMockApis([
    338     'chrome.storage.local.set',
    339     'requestFromServer'
    340   ]);
    341 
    342   this.mockApis.expects(once()).requestFromServer('GET', 'settings/optin')
    343       .will(returnValue(Promise.resolve({
    344         status: 200,
    345         responseText: '{"value": true}'})));
    346 
    347   this.mockApis.expects(once())
    348       .chrome_storage_local_set(eqJSON({googleNowEnabled: true}));
    349 
    350   var thenCalled = false;
    351   var catchCalled = false;
    352   requestAndUpdateOptedIn().then(function(optedIn) {
    353     thenCalled = true;
    354     assertTrue(optedIn);
    355   }).catch(function() {
    356     catchCalled = true;
    357   });
    358   assertTrue(thenCalled);
    359   assertFalse(catchCalled);
    360 });
    361 
    362 TEST_F(TEST_NAME, 'RequestAndUpdateOptInOptedOut', function() {
    363   this.makeAndRegisterMockApis([
    364     'chrome.storage.local.set',
    365     'requestFromServer'
    366   ]);
    367 
    368   this.mockApis.expects(once()).requestFromServer('GET', 'settings/optin')
    369       .will(returnValue(Promise.resolve({
    370         status: 200,
    371         responseText: '{"value": false}'})));
    372 
    373   this.mockApis.expects(once())
    374       .chrome_storage_local_set(eqJSON({googleNowEnabled: false}));
    375 
    376   var thenCalled = false;
    377   var catchCalled = false;
    378   requestAndUpdateOptedIn().then(function(optedIn) {
    379     thenCalled = true;
    380     assertFalse(optedIn);
    381   }).catch(function() {
    382     catchCalled = true;
    383   });
    384   assertTrue(thenCalled);
    385   assertFalse(catchCalled);
    386 });
    387 
    388 TEST_F(TEST_NAME, 'RequestAndUpdateOptInFailure', function() {
    389   this.makeAndRegisterMockApis([
    390     'chrome.storage.local.set',
    391     'requestFromServer'
    392   ]);
    393 
    394   this.mockApis.expects(once()).requestFromServer('GET', 'settings/optin')
    395       .will(returnValue(Promise.reject({status: 404})));
    396 
    397   this.mockApis.expects(never()).chrome_storage_local_set();
    398 
    399   var thenCalled = false;
    400   var catchCalled = false;
    401   requestAndUpdateOptedIn().then(function() {
    402     thenCalled = true;
    403   }).catch(function() {
    404     catchCalled = true;
    405   });
    406   assertFalse(thenCalled);
    407   assertTrue(catchCalled);
    408 });
    409 
    410 /**
    411  * pollOptedInNoImmediateRecheck Tests
    412  */
    413 TEST_F(TEST_NAME, 'pollOptedInNoImmediateRecheckOptedIn', function() {
    414   this.makeAndRegisterMockApis([
    415     'requestAndUpdateOptedIn',
    416     'instrumented.metricsPrivate.getVariationParams',
    417     'optInPollAttempts.start'
    418   ]);
    419 
    420   this.mockApis.expects(once()).requestAndUpdateOptedIn()
    421       .will(returnValue(Promise.resolve(true)));
    422 
    423   this.mockApis.expects(never())
    424       .instrumented_metricsPrivate_getVariationParams();
    425 
    426   this.mockApis.expects(never()).optInPollAttempts_start();
    427 
    428   pollOptedInNoImmediateRecheck();
    429 });
    430 
    431 TEST_F(TEST_NAME, 'pollOptedInNoImmediateRecheckOptedOut', function() {
    432   this.makeAndRegisterMockApis([
    433     'requestAndUpdateOptedIn',
    434     'instrumented.metricsPrivate.getVariationParams',
    435     'optInPollAttempts.start'
    436   ]);
    437 
    438   this.mockApis.expects(once()).requestAndUpdateOptedIn()
    439       .will(returnValue(Promise.resolve(false)));
    440 
    441   var getVariationParamsSavedArgs = new SaveMockArguments();
    442   this.mockApis.expects(once())
    443       .instrumented_metricsPrivate_getVariationParams(
    444           getVariationParamsSavedArgs.match(eq('GoogleNow')),
    445           getVariationParamsSavedArgs.match(ANYTHING))
    446       .will(invokeCallback(getVariationParamsSavedArgs, 1, {}));
    447 
    448   this.mockApis.expects(once())
    449       .optInPollAttempts_start(DEFAULT_OPTIN_CHECK_PERIOD_SECONDS);
    450 
    451   pollOptedInNoImmediateRecheck();
    452 });
    453 
    454 TEST_F(TEST_NAME, 'pollOptedInNoImmediateRecheckFailure', function() {
    455   this.makeAndRegisterMockApis([
    456     'requestAndUpdateOptedIn',
    457     'instrumented.metricsPrivate.getVariationParams',
    458     'optInPollAttempts.start'
    459   ]);
    460 
    461   this.mockApis.expects(once()).requestAndUpdateOptedIn()
    462       .will(returnValue(Promise.reject()));
    463 
    464   var getVariationParamsSavedArgs = new SaveMockArguments();
    465   this.mockApis.expects(once())
    466       .instrumented_metricsPrivate_getVariationParams(
    467           getVariationParamsSavedArgs.match(eq('GoogleNow')),
    468           getVariationParamsSavedArgs.match(ANYTHING))
    469       .will(invokeCallback(getVariationParamsSavedArgs, 1, {}));
    470 
    471   this.mockApis.expects(once())
    472       .optInPollAttempts_start(DEFAULT_OPTIN_CHECK_PERIOD_SECONDS);
    473 
    474   pollOptedInNoImmediateRecheck();
    475 });
    476 
    477 /**
    478  * getGroupsToRequest Tests
    479  */
    480 TEST_F(TEST_NAME, 'GetGroupsToRequestNone', function() {
    481   this.makeAndRegisterMockApis([
    482     'fillFromChromeLocalStorage',
    483     'Date.now'
    484   ]);
    485 
    486   this.mockApis.expects(once())
    487       .fillFromChromeLocalStorage(eqJSON({notificationGroups: {}}))
    488       .will(returnValue(Promise.resolve({notificationGroups: {}})));
    489 
    490   this.mockApis.expects(once()).Date_now().will(returnValue(20));
    491 
    492   getGroupsToRequest().then(function(groupsToRequest) {
    493     assertTrue(JSON.stringify(groupsToRequest) === '[]');
    494   });
    495 });
    496 
    497 TEST_F(TEST_NAME, 'GetGroupsToRequestWithGroups', function() {
    498   this.makeAndRegisterMockApis([
    499     'fillFromChromeLocalStorage',
    500     'Date.now'
    501   ]);
    502 
    503   this.mockApis.expects(once())
    504       .fillFromChromeLocalStorage(eqJSON({notificationGroups: {}}))
    505       .will(returnValue(Promise.resolve({notificationGroups: {
    506         TIME18: {nextPollTime: 18},
    507         TIME19: {nextPollTime: 19},
    508         TIME20: {nextPollTime: 20},
    509         TIME21: {nextPollTime: 21},
    510         TIME22: {nextPollTime: 22},
    511         TIMEUNDEF: {}
    512       }})));
    513 
    514   this.mockApis.expects(once()).Date_now().will(returnValue(20));
    515 
    516   getGroupsToRequest().then(function(groupsToRequest) {
    517     assertTrue(groupsToRequest.length == 3);
    518     assertTrue(groupsToRequest.indexOf('TIME18') >= 0);
    519     assertTrue(groupsToRequest.indexOf('TIME19') >= 0);
    520     assertTrue(groupsToRequest.indexOf('TIME20') >= 0);
    521   });
    522 });
    523 
    524 /**
    525  * combineGroup Tests
    526  */
    527 TEST_F(TEST_NAME, 'CombineGroup', function() {
    528   // Tests combineGroup function. Verifies that both notifications with and
    529   // without show time are handled correctly and that cards are correctly
    530   // added to existing cards with same ID or start a new combined card.
    531 
    532   // Setup and expectations.
    533   var combinedCards = {
    534     'EXISTING CARD': [1]
    535   };
    536 
    537   var receivedNotificationNoShowTime = {
    538     chromeNotificationId: 'EXISTING CARD',
    539     trigger: {hideTimeSec: 1}
    540   };
    541   var receivedNotificationWithShowTime = {
    542     chromeNotificationId: 'NEW CARD',
    543     trigger: {showTimeSec: 2, hideTimeSec: 3}
    544   }
    545 
    546   var storedGroup = {
    547     cardsTimestamp: 10000,
    548     cards: [
    549       receivedNotificationNoShowTime,
    550       receivedNotificationWithShowTime
    551     ]
    552   };
    553 
    554   // Invoking the tested function.
    555   combineGroup(combinedCards, storedGroup);
    556 
    557   // Check the output value.
    558   var expectedCombinedCards = {
    559     'EXISTING CARD': [
    560       1,
    561       {
    562         receivedNotification: receivedNotificationNoShowTime,
    563         hideTime: 11000
    564       }
    565     ],
    566     'NEW CARD': [
    567       {
    568         receivedNotification: receivedNotificationWithShowTime,
    569         showTime: 12000,
    570         hideTime: 13000
    571       }
    572     ]
    573   };
    574 
    575   assertEquals(
    576       JSON.stringify(expectedCombinedCards),
    577       JSON.stringify(combinedCards));
    578 });
    579 
    580 /**
    581  * Mocks global functions and APIs that initialize() depends upon.
    582  * @param {Test} fixture Test fixture.
    583  */
    584 function mockInitializeDependencies(fixture) {
    585   fixture.makeAndRegisterMockGlobals([
    586     'pollOptedInNoImmediateRecheck',
    587     'recordEvent',
    588     'removeAllCards',
    589     'setBackgroundEnable',
    590     'startPollingCards',
    591     'stopPollingCards'
    592   ]);
    593   fixture.makeAndRegisterMockApis([
    594     'authenticationManager.isSignedIn',
    595     'chrome.storage.local.remove',
    596     'fillFromChromeLocalStorage',
    597     'instrumented.metricsPrivate.getVariationParams',
    598     'instrumented.notifications.getAll',
    599     'instrumented.notifications.getPermissionLevel',
    600     'instrumented.webstorePrivate.getBrowserLogin',
    601     'optInPollAttempts.isRunning',
    602     'optInPollAttempts.stop',
    603     'tasks.add',
    604     'updateCardsAttempts.isRunning',
    605     'updateCardsAttempts.stop'
    606   ]);
    607 }
    608 
    609 /**
    610  * Sets up the test to expect the state machine calls and send
    611  * the specified state machine state. Currently used to test initialize().
    612  * Note that this CAN NOT be used if any of the methods below are called
    613  * outside of this context with the same argument matchers.
    614  * expects() calls cannot be chained with the same argument matchers.
    615  * @param {object} fixture Test fixture.
    616  * @param {string} testIdentityToken getAuthToken callback token.
    617  * @param {object} testExperimentVariationParams Response of
    618  *     metricsPrivate.getVariationParams.
    619  * @param {string} testExperimentVariationParams Response of
    620  *     notifications.getPermissionLevel.
    621  * @param {boolean} testGoogleNowEnabled True if the user is opted in to Google
    622  *     Now.
    623  */
    624 function expectStateMachineCalls(
    625     fixture,
    626     testIdentityToken,
    627     testExperimentVariationParams,
    628     testNotificationPermissionLevel,
    629     testGoogleNowEnabled) {
    630   fixture.mockApis.expects(once()).
    631       authenticationManager_isSignedIn().
    632       will(returnValue(new Promise(function(resolve) {
    633         resolve(!!testIdentityToken);
    634       })));
    635 
    636   var getVariationParamsSavedArgs = new SaveMockArguments();
    637   fixture.mockApis.expects(once()).
    638       instrumented_metricsPrivate_getVariationParams(
    639           getVariationParamsSavedArgs.match(ANYTHING),
    640           getVariationParamsSavedArgs.match(ANYTHING)).
    641       will(invokeCallback(
    642           getVariationParamsSavedArgs, 1, testExperimentVariationParams));
    643 
    644   var notificationGetPermissionLevelSavedArgs = new SaveMockArguments();
    645   fixture.mockApis.expects(once()).
    646       instrumented_notifications_getPermissionLevel(
    647           notificationGetPermissionLevelSavedArgs.match(ANYTHING)).
    648       will(invokeCallback(
    649           notificationGetPermissionLevelSavedArgs,
    650           0,
    651           testNotificationPermissionLevel))
    652 
    653   expectChromeLocalStorageGet(
    654       fixture,
    655       {googleNowEnabled: false},
    656       {googleNowEnabled: testGoogleNowEnabled});
    657 
    658   var updateCardsAttemptsIsRunningSavedArgs = new SaveMockArguments();
    659   fixture.mockApis.expects(once()).
    660       updateCardsAttempts_isRunning(
    661           updateCardsAttemptsIsRunningSavedArgs.match(ANYTHING)).
    662       will(
    663           invokeCallback(
    664               updateCardsAttemptsIsRunningSavedArgs, 0, undefined));
    665 
    666   var optInPollAttemptsIsRunningSavedArgs = new SaveMockArguments();
    667   fixture.mockApis.expects(once()).
    668       optInPollAttempts_isRunning(
    669           optInPollAttemptsIsRunningSavedArgs.match(ANYTHING)).
    670       will(
    671           invokeCallback(
    672               optInPollAttemptsIsRunningSavedArgs, 0, undefined));
    673 }
    674 
    675 /**
    676  * Sets up the test to expect the initialization calls that
    677  * initialize() invokes.
    678  * Note that this CAN NOT be used if any of the methods below are called
    679  * outside of this context with the same argument matchers.
    680  * expects() calls cannot be chained with the same argument matchers.
    681  */
    682 function expectInitialization(fixture) {
    683   var tasksAddSavedArgs = new SaveMockArguments();
    684   fixture.mockApis.expects(once()).
    685       tasks_add(
    686           tasksAddSavedArgs.match(ANYTHING),
    687           tasksAddSavedArgs.match(ANYTHING)).
    688       will(invokeCallback(tasksAddSavedArgs, 1, function() {}));
    689 
    690   // The ordering here between stubs and expects is important.
    691   // We only care about the EXTENSION_START event. The other events are covered
    692   // by the NoCards tests below. Reversing the calls will cause all recordEvent
    693   // calls to be unexpected.
    694   fixture.mockGlobals.stubs().recordEvent(ANYTHING);
    695   fixture.mockGlobals.
    696       expects(once()).recordEvent(GoogleNowEvent.EXTENSION_START);
    697 }
    698 
    699 TEST_F(TEST_NAME,'Initialize_SignedOut', function() {
    700   // Tests the case when getAuthToken fails most likely because the user is
    701   // not signed in. In this case, the function should quietly exit after
    702   // finding out that getAuthToken fails.
    703 
    704   // Setup and expectations.
    705   var testIdentityToken = undefined;
    706   var testExperimentVariationParams = {};
    707   var testNotificationPermissionLevel = 'denied';
    708   var testGoogleNowEnabled = undefined;
    709 
    710   mockInitializeDependencies(this);
    711 
    712   expectInitialization(this);
    713 
    714   expectStateMachineCalls(
    715       this,
    716       testIdentityToken,
    717       testExperimentVariationParams,
    718       testNotificationPermissionLevel,
    719       testGoogleNowEnabled);
    720 
    721   this.mockGlobals.expects(once()).setBackgroundEnable(false);
    722   this.mockGlobals.expects(never()).startPollingCards();
    723   this.mockGlobals.expects(once()).stopPollingCards();
    724   this.mockGlobals.expects(once()).removeAllCards();
    725   this.mockGlobals.expects(never()).pollOptedInNoImmediateRecheck();
    726   this.mockApis.expects(once()).optInPollAttempts_stop();
    727 
    728   // Invoking the tested function.
    729   initialize();
    730 });
    731 
    732 TEST_F(TEST_NAME,'Initialize_NotificationDisabled', function() {
    733   // Tests the case when Google Now is disabled in the notifications center.
    734 
    735   // Setup and expectations.
    736   var testIdentityToken = 'some identity token';
    737   var testExperimentVariationParams = {};
    738   var testNotificationPermissionLevel = 'denied';
    739   var testGoogleNowEnabled = undefined;
    740 
    741   mockInitializeDependencies(this);
    742 
    743   expectInitialization(this);
    744 
    745   expectStateMachineCalls(
    746       this,
    747       testIdentityToken,
    748       testExperimentVariationParams,
    749       testNotificationPermissionLevel,
    750       testGoogleNowEnabled);
    751 
    752   this.mockGlobals.expects(once()).setBackgroundEnable(false);
    753   this.mockGlobals.expects(never()).startPollingCards();
    754   this.mockGlobals.expects(once()).stopPollingCards();
    755   this.mockGlobals.expects(once()).removeAllCards();
    756   this.mockGlobals.expects(never()).pollOptedInNoImmediateRecheck();
    757   this.mockApis.expects(once()).optInPollAttempts_stop();
    758 
    759   // Invoking the tested function.
    760   initialize();
    761 });
    762 
    763 TEST_F(TEST_NAME, 'Initialize_NoBackground', function() {
    764   // Tests when the no background variation is received.
    765 
    766   // Setup and expectations.
    767   var testIdentityToken = 'some identity token';
    768   var testExperimentVariationParams = {canEnableBackground: 'false'};
    769   var testNotificationPermissionLevel = 'granted';
    770   var testGoogleNowEnabled = true;
    771 
    772   mockInitializeDependencies(this);
    773 
    774   expectInitialization(this);
    775 
    776   expectStateMachineCalls(
    777       this,
    778       testIdentityToken,
    779       testExperimentVariationParams,
    780       testNotificationPermissionLevel,
    781       testGoogleNowEnabled);
    782 
    783   this.mockGlobals.expects(once()).setBackgroundEnable(false);
    784   this.mockGlobals.expects(once()).startPollingCards();
    785   this.mockGlobals.expects(never()).stopPollingCards();
    786   this.mockGlobals.expects(never()).removeAllCards();
    787   this.mockGlobals.expects(never()).pollOptedInNoImmediateRecheck();
    788   this.mockApis.expects(once()).optInPollAttempts_stop();
    789 
    790   // Invoking the tested function.
    791   initialize();
    792 });
    793 
    794 TEST_F(TEST_NAME, 'Initialize_GoogleNowDisabled', function() {
    795   // Tests when the user has Google Now disabled.
    796 
    797   // Setup and expectations.
    798   var testIdentityToken = 'some identity token';
    799   var testExperimentVariationParams = {};
    800   var testNotificationPermissionLevel = 'granted';
    801   var testGoogleNowEnabled = false;
    802 
    803   mockInitializeDependencies(this);
    804 
    805   expectInitialization(this);
    806 
    807   expectStateMachineCalls(
    808       this,
    809       testIdentityToken,
    810       testExperimentVariationParams,
    811       testNotificationPermissionLevel,
    812       testGoogleNowEnabled);
    813 
    814   this.mockGlobals.expects(once()).setBackgroundEnable(false);
    815   this.mockGlobals.expects(never()).startPollingCards();
    816   this.mockGlobals.expects(once()).stopPollingCards();
    817   this.mockGlobals.expects(once()).removeAllCards();
    818   this.mockGlobals.expects(once()).pollOptedInNoImmediateRecheck();
    819   this.mockApis.expects(never()).optInPollAttempts_stop();
    820 
    821   // Invoking the tested function.
    822   initialize();
    823 });
    824 
    825 TEST_F(TEST_NAME, 'Initialize_RunGoogleNow', function() {
    826   // Tests if Google Now will invoke startPollingCards when all
    827   // of the required state is fulfilled.
    828 
    829   // Setup and expectations.
    830   var testIdentityToken = 'some identity token';
    831   var testExperimentVariationParams = {};
    832   var testNotificationPermissionLevel = 'granted';
    833   var testGoogleNowEnabled = true;
    834 
    835   mockInitializeDependencies(this);
    836 
    837   expectInitialization(this);
    838 
    839   expectStateMachineCalls(
    840       this,
    841       testIdentityToken,
    842       testExperimentVariationParams,
    843       testNotificationPermissionLevel,
    844       testGoogleNowEnabled);
    845 
    846   this.mockGlobals.expects(once()).setBackgroundEnable(true);
    847   this.mockGlobals.expects(once()).startPollingCards();
    848   this.mockGlobals.expects(never()).stopPollingCards();
    849   this.mockGlobals.expects(never()).removeAllCards();
    850   this.mockGlobals.expects(never()).pollOptedInNoImmediateRecheck();
    851   this.mockApis.expects(once()).optInPollAttempts_stop();
    852 
    853   // Invoking the tested function.
    854   initialize();
    855 });
    856 
    857 /**
    858  * No Cards Event Recording Tests
    859  */
    860 TEST_F(TEST_NAME, 'NoCardsSignedOut', function() {
    861   var signedIn = false;
    862   var notificationEnabled = false;
    863   var googleNowEnabled = false;
    864   this.makeAndRegisterMockGlobals([
    865       'recordEvent',
    866       'removeAllCards',
    867       'setBackgroundEnable',
    868       'setShouldPollCards',
    869       'setShouldPollOptInStatus']);
    870 
    871   this.mockGlobals.stubs().removeAllCards();
    872   this.mockGlobals.stubs().setBackgroundEnable(ANYTHING);
    873   this.mockGlobals.stubs().setShouldPollCards(ANYTHING);
    874   this.mockGlobals.stubs().setShouldPollOptInStatus(ANYTHING);
    875 
    876   this.mockGlobals.expects(once()).recordEvent(
    877       GoogleNowEvent.STOPPED);
    878   this.mockGlobals.expects(once()).recordEvent(
    879       GoogleNowEvent.SIGNED_OUT);
    880   this.mockGlobals.expects(never()).recordEvent(
    881       GoogleNowEvent.NOTIFICATION_DISABLED);
    882   this.mockGlobals.expects(never()).recordEvent(
    883       GoogleNowEvent.GOOGLE_NOW_DISABLED);
    884   updateRunningState(signedIn, true, notificationEnabled, googleNowEnabled);
    885 });
    886 
    887 TEST_F(TEST_NAME, 'NoCardsNotificationsDisabled', function() {
    888   var signedIn = true;
    889   var notificationEnabled = false;
    890   var googleNowEnabled = false;
    891   this.makeAndRegisterMockGlobals([
    892       'recordEvent',
    893       'removeAllCards',
    894       'setBackgroundEnable',
    895       'setShouldPollCards',
    896       'setShouldPollOptInStatus']);
    897 
    898   this.mockGlobals.stubs().removeAllCards();
    899   this.mockGlobals.stubs().setBackgroundEnable(ANYTHING);
    900   this.mockGlobals.stubs().setShouldPollCards(ANYTHING);
    901   this.mockGlobals.stubs().setShouldPollOptInStatus(ANYTHING);
    902 
    903   this.mockGlobals.expects(once()).recordEvent(
    904       GoogleNowEvent.STOPPED);
    905   this.mockGlobals.expects(never()).recordEvent(
    906       GoogleNowEvent.SIGNED_OUT);
    907   this.mockGlobals.expects(once()).recordEvent(
    908       GoogleNowEvent.NOTIFICATION_DISABLED);
    909   this.mockGlobals.expects(never()).recordEvent(
    910       GoogleNowEvent.GOOGLE_NOW_DISABLED);
    911   updateRunningState(signedIn, true, notificationEnabled, googleNowEnabled);
    912 });
    913 
    914 TEST_F(TEST_NAME, 'NoCardsGoogleNowDisabled', function() {
    915   var signedIn = true;
    916   var notificationEnabled = true;
    917   var googleNowEnabled = false;
    918   this.makeAndRegisterMockGlobals([
    919       'recordEvent',
    920       'removeAllCards',
    921       'setBackgroundEnable',
    922       'setShouldPollCards',
    923       'setShouldPollOptInStatus']);
    924 
    925   this.mockGlobals.stubs().removeAllCards();
    926   this.mockGlobals.stubs().setBackgroundEnable(ANYTHING);
    927   this.mockGlobals.stubs().setShouldPollCards(ANYTHING);
    928   this.mockGlobals.stubs().setShouldPollOptInStatus(ANYTHING);
    929 
    930   this.mockGlobals.expects(never()).recordEvent(
    931       GoogleNowEvent.STOPPED);
    932   this.mockGlobals.expects(never()).recordEvent(
    933       GoogleNowEvent.SIGNED_OUT);
    934   this.mockGlobals.expects(never()).recordEvent(
    935       GoogleNowEvent.NOTIFICATION_DISABLED);
    936   this.mockGlobals.expects(once()).recordEvent(
    937       GoogleNowEvent.GOOGLE_NOW_DISABLED);
    938   updateRunningState(signedIn, true, notificationEnabled, googleNowEnabled);
    939 });
    940 
    941 TEST_F(TEST_NAME, 'NoCardsEverythingEnabled', function() {
    942   var signedIn = true;
    943   var notificationEnabled = true;
    944   var googleNowEnabled = true;
    945   this.makeAndRegisterMockGlobals([
    946       'recordEvent',
    947       'removeAllCards',
    948       'setBackgroundEnable',
    949       'setShouldPollCards',
    950       'setShouldPollOptInStatus']);
    951 
    952   this.mockGlobals.stubs().removeAllCards();
    953   this.mockGlobals.stubs().setBackgroundEnable(ANYTHING);
    954   this.mockGlobals.stubs().setShouldPollCards(ANYTHING);
    955   this.mockGlobals.stubs().setShouldPollOptInStatus(ANYTHING);
    956 
    957   this.mockGlobals.expects(never()).recordEvent(
    958       GoogleNowEvent.STOPPED);
    959   this.mockGlobals.expects(never()).recordEvent(
    960       GoogleNowEvent.SIGNED_OUT);
    961   this.mockGlobals.expects(never()).recordEvent(
    962       GoogleNowEvent.NOTIFICATION_DISABLED);
    963   this.mockGlobals.expects(never()).recordEvent(
    964       GoogleNowEvent.GOOGLE_NOW_DISABLED);
    965   updateRunningState(signedIn, true, notificationEnabled, googleNowEnabled);
    966 });
    967 
    968 /**
    969  * Mocks global functions and APIs that onNotificationClicked() depends upon.
    970  * @param {Test} fixture Test fixture.
    971  */
    972 function mockOnNotificationClickedDependencies(fixture) {
    973   fixture.makeAndRegisterMockApis([
    974     'chrome.windows.create',
    975     'chrome.windows.update',
    976     'fillFromChromeLocalStorage',
    977     'instrumented.tabs.create']);
    978 }
    979 
    980 TEST_F(TEST_NAME, 'OnNotificationClicked_NoData', function() {
    981   // Tests the case when there is no data associated with notification id.
    982   // In this case, the function should do nothing.
    983 
    984   // Setup and expectations.
    985   var testNotificationId = 'TEST_ID';
    986   var testNotificationDataRequest = {notificationsData: {}};
    987   var testNotificationData = {notificationsData: {}};
    988 
    989   mockOnNotificationClickedDependencies(this);
    990   this.makeMockLocalFunctions(['selector']);
    991 
    992   expectChromeLocalStorageGet(
    993       this, testNotificationDataRequest, testNotificationData);
    994 
    995   // Invoking the tested function.
    996   onNotificationClicked(
    997       testNotificationId, this.mockLocalFunctions.functions().selector);
    998 });
    999 
   1000 TEST_F(TEST_NAME, 'OnNotificationClicked_ActionUrlsUndefined', function() {
   1001   // Tests the case when the data associated with notification id is
   1002   // 'undefined'.
   1003   // In this case, the function should do nothing.
   1004 
   1005   // Setup and expectations.
   1006   var testActionUrls = undefined;
   1007   var testNotificationId = 'TEST_ID';
   1008   var testNotificationDataRequest = {notificationsData: {}};
   1009   var testNotificationData = {
   1010       notificationsData: {'TEST_ID': {actionUrls: testActionUrls}}
   1011   };
   1012 
   1013   mockOnNotificationClickedDependencies(this);
   1014   this.makeMockLocalFunctions(['selector']);
   1015 
   1016   expectChromeLocalStorageGet(
   1017       this, testNotificationDataRequest, testNotificationData);
   1018 
   1019   this.mockLocalFunctions.expects(once())
   1020       .selector(eqJSON(
   1021           testNotificationData.notificationsData[testNotificationId]))
   1022       .will(returnValue(undefined));
   1023 
   1024   // Invoking the tested function.
   1025   onNotificationClicked(
   1026       testNotificationId, this.mockLocalFunctions.functions().selector);
   1027 });
   1028 
   1029 TEST_F(TEST_NAME, 'OnNotificationClicked_TabCreateSuccess', function() {
   1030   // Tests the selected URL is OK and crome.tabs.create suceeds.
   1031 
   1032   // Setup and expectations.
   1033   var testActionUrls = {testField: 'TEST VALUE'};
   1034   var testNotificationId = 'TEST_ID';
   1035   var testNotificationDataRequest = {notificationsData: {}};
   1036   var testNotificationData = {
   1037       notificationsData: {'TEST_ID': {actionUrls: testActionUrls}}
   1038   };
   1039   var testActionUrl = 'http://testurl.com';
   1040   var testCreatedTab = {windowId: 239};
   1041 
   1042   mockOnNotificationClickedDependencies(this);
   1043   this.makeMockLocalFunctions(['selector']);
   1044 
   1045   expectChromeLocalStorageGet(
   1046       this, testNotificationDataRequest, testNotificationData);
   1047   this.mockLocalFunctions.expects(once())
   1048       .selector(eqJSON(
   1049           testNotificationData.notificationsData[testNotificationId]))
   1050       .will(returnValue(testActionUrl));
   1051   var chromeTabsCreateSavedArgs = new SaveMockArguments();
   1052   this.mockApis.expects(once()).
   1053       instrumented_tabs_create(
   1054           chromeTabsCreateSavedArgs.match(eqJSON({url: testActionUrl})),
   1055           chromeTabsCreateSavedArgs.match(ANYTHING)).
   1056       will(invokeCallback(chromeTabsCreateSavedArgs, 1, testCreatedTab));
   1057   this.mockApis.expects(once()).chrome_windows_update(
   1058       testCreatedTab.windowId,
   1059       eqJSON({focused: true}));
   1060 
   1061   // Invoking the tested function.
   1062   onNotificationClicked(
   1063       testNotificationId, this.mockLocalFunctions.functions().selector);
   1064 });
   1065 
   1066 TEST_F(TEST_NAME, 'OnNotificationClicked_TabCreateFail', function() {
   1067   // Tests the selected URL is OK and crome.tabs.create fails.
   1068   // In this case, the function should invoke chrome.windows.create as a
   1069   // second attempt.
   1070 
   1071   // Setup and expectations.
   1072   var testActionUrls = {testField: 'TEST VALUE'};
   1073   var testNotificationId = 'TEST_ID';
   1074   var testNotificationDataRequest = {notificationsData: {}};
   1075   var testNotificationData = {
   1076     notificationsData: {'TEST_ID': {actionUrls: testActionUrls}}
   1077   };
   1078   var testActionUrl = 'http://testurl.com';
   1079   var testCreatedTab = undefined; // chrome.tabs.create fails
   1080 
   1081   mockOnNotificationClickedDependencies(this);
   1082   this.makeMockLocalFunctions(['selector']);
   1083 
   1084   expectChromeLocalStorageGet(
   1085       this, testNotificationDataRequest, testNotificationData);
   1086   this.mockLocalFunctions.expects(once())
   1087       .selector(eqJSON(
   1088           testNotificationData.notificationsData[testNotificationId]))
   1089       .will(returnValue(testActionUrl));
   1090   var chromeTabsCreateSavedArgs = new SaveMockArguments();
   1091   this.mockApis.expects(once()).
   1092       instrumented_tabs_create(
   1093           chromeTabsCreateSavedArgs.match(eqJSON({url: testActionUrl})),
   1094           chromeTabsCreateSavedArgs.match(ANYTHING)).
   1095       will(invokeCallback(chromeTabsCreateSavedArgs, 1, testCreatedTab));
   1096   this.mockApis.expects(once()).chrome_windows_create(
   1097       eqJSON({url: testActionUrl, focused: true}));
   1098 
   1099   // Invoking the tested function.
   1100   onNotificationClicked(
   1101       testNotificationId, this.mockLocalFunctions.functions().selector);
   1102 });
   1103 
   1104 TEST_F(TEST_NAME, 'ShowNotificationGroups', function() {
   1105   // Tests showNotificationGroups function. Checks that the function properly
   1106   // deletes the card that didn't get an update, updates existing card and
   1107   // creates a new card that previously didn't exist.
   1108 
   1109   // Setup and expectations.
   1110   var existingNotifications = {
   1111     'SHOULD BE DELETED': 'SOMETHING',
   1112     'SHOULD BE KEPT': 'SOMETHING'
   1113   };
   1114 
   1115   var keptCard = {
   1116     chromeNotificationId: 'SHOULD BE KEPT',
   1117     trigger: {showTimeSec: 0, hideTimeSec: 0}
   1118   };
   1119 
   1120   var keptNotification = {
   1121     receivedNotification: keptCard,
   1122     showTime: 0,
   1123     hideTime: 0
   1124   };
   1125 
   1126   var newCard = {
   1127     chromeNotificationId: 'NEW CARD',
   1128     trigger: {showTimeSec: 0, hideTimeSec: 0}
   1129   };
   1130 
   1131   var newNotification = {
   1132     receivedNotification: newCard,
   1133     showTime: 0,
   1134     hideTime: 0
   1135   };
   1136 
   1137   var notificationGroups = {
   1138     'TEST GROUP 1': {cards: [keptCard], cardsTimestamp: 0},
   1139     'TEST GROUP 2': {cards: [newCard], cardsTimestamp: 0}
   1140   };
   1141 
   1142   var fakeOnCardShownFunction = 'FAKE ON CARD SHOWN FUNCTION';
   1143 
   1144   var expectedUpdatedNotifications = {
   1145     'SHOULD BE KEPT': 'KEPT CARD NOTIFICATION DATA',
   1146     'NEW CARD': 'NEW CARD NOTIFICATION DATA'
   1147   };
   1148 
   1149   this.makeAndRegisterMockApis([
   1150     'cardSet.update',
   1151     'chrome.storage.local.set',
   1152     'instrumented.notifications.getAll'
   1153   ]);
   1154   this.makeMockLocalFunctions([
   1155     'onSuccess'
   1156   ]);
   1157 
   1158   var notificationsGetAllSavedArgs = new SaveMockArguments();
   1159   this.mockApis.expects(once()).
   1160       instrumented_notifications_getAll(
   1161           notificationsGetAllSavedArgs.match(ANYTHING)).
   1162       will(invokeCallback(
   1163           notificationsGetAllSavedArgs, 0, existingNotifications));
   1164 
   1165   this.mockApis.expects(once()).
   1166       cardSet_update(
   1167           'SHOULD BE KEPT',
   1168           eqJSON([keptNotification]),
   1169           eqJSON(notificationGroups),
   1170           fakeOnCardShownFunction).
   1171       will(returnValue('KEPT CARD NOTIFICATION DATA'));
   1172   this.mockApis.expects(once()).
   1173       cardSet_update(
   1174           'NEW CARD',
   1175           eqJSON([newNotification]),
   1176           eqJSON(notificationGroups),
   1177           fakeOnCardShownFunction).
   1178       will(returnValue('NEW CARD NOTIFICATION DATA'));
   1179   this.mockApis.expects(once()).
   1180       cardSet_update(
   1181           'SHOULD BE DELETED',
   1182           [],
   1183           eqJSON(notificationGroups),
   1184           fakeOnCardShownFunction).
   1185       will(returnValue(undefined));
   1186 
   1187   this.mockApis.expects(once()).
   1188       chrome_storage_local_set(
   1189           eqJSON({notificationsData: expectedUpdatedNotifications}));
   1190 
   1191   this.mockLocalFunctions.expects(once()).
   1192       onSuccess(undefined);
   1193 
   1194   // Invoking the tested function.
   1195   showNotificationGroups(notificationGroups, fakeOnCardShownFunction)
   1196       .then(this.mockLocalFunctions.functions().onSuccess);
   1197 });
   1198 
   1199 TEST_F(TEST_NAME, 'ProcessServerResponse', function() {
   1200   // Tests processServerResponse function.
   1201 
   1202   // Setup and expectations.
   1203   Date.now = function() { return 3000000; };
   1204 
   1205   // GROUP1 was requested and contains cards c4 and c5. For c5, there is a
   1206   // non-expired dismissal, so it will be ignored.
   1207   // GROUP2 was not requested, but is contained in server response to
   1208   // indicate that the group still exists. Stored group GROUP2 won't change.
   1209   // GROUP3 is stored, but is not present in server's response, which means
   1210   // it doesn't exist anymore. This group will be deleted.
   1211   // GROUP4 doesn't contain cards, but it was requested. This is treated as
   1212   // if it had an empty array of cards. Cards in the stored group will be
   1213   // replaced with an empty array.
   1214   // GROUP5 doesn't have next poll time, and it will be stored without next
   1215   // poll time.
   1216   var serverResponse = {
   1217     groups: {
   1218       GROUP1: {requested: true, nextPollSeconds: 46},
   1219       GROUP2: {requested: false},
   1220       GROUP4: {requested: true, nextPollSeconds: 45},
   1221       GROUP5: {requested: true}
   1222     },
   1223     notifications: [
   1224       {notificationId: 'c4', groupName: 'GROUP1'},
   1225       {notificationId: 'c5', groupName: 'GROUP1'}
   1226     ]
   1227   };
   1228 
   1229   var recentDismissals = {
   1230     c4: 1800000, // expired dismissal
   1231     c5: 1800001  // non-expired dismissal
   1232   };
   1233 
   1234   var storedGroups = {
   1235     GROUP2: {
   1236       cards: [{notificationId: 'c2'}],
   1237       cardsTimestamp: 239,
   1238       nextPollTime: 10000
   1239     },
   1240     GROUP3: {
   1241       cards: [{notificationId: 'c3'}],
   1242       cardsTimestamp: 240,
   1243       nextPollTime: 10001
   1244     },
   1245     GROUP4: {
   1246       cards: [{notificationId: 'c6'}],
   1247       cardsTimestamp: 241,
   1248       nextPollTime: 10002
   1249     }
   1250   };
   1251 
   1252   var expectedUpdatedGroups = {
   1253     GROUP1: {
   1254       cards: [{notificationId: 'c4', groupName: 'GROUP1'}],
   1255       cardsTimestamp: 3000000,
   1256       nextPollTime: 3046000
   1257     },
   1258     GROUP2: {
   1259       cards: [{notificationId: 'c2'}],
   1260       cardsTimestamp: 239,
   1261       nextPollTime: 10000
   1262     },
   1263     GROUP4: {
   1264       cards: [],
   1265       cardsTimestamp: 3000000,
   1266       nextPollTime: 3045000
   1267     },
   1268     GROUP5: {
   1269       cards: [],
   1270       cardsTimestamp: 3000000
   1271     }
   1272   };
   1273 
   1274   var expectedUpdatedRecentDismissals = {
   1275     c5: 1800001
   1276   };
   1277 
   1278   this.makeAndRegisterMockGlobals([
   1279     'scheduleNextCardsPoll'
   1280   ]);
   1281 
   1282   this.makeAndRegisterMockApis([
   1283     'fillFromChromeLocalStorage',
   1284   ]);
   1285 
   1286   expectChromeLocalStorageGet(
   1287       this,
   1288       {
   1289         notificationGroups: {},
   1290         recentDismissals: {}
   1291       },
   1292       {
   1293         notificationGroups: storedGroups,
   1294         recentDismissals: recentDismissals
   1295       });
   1296 
   1297   this.mockGlobals.expects(once())
   1298       .scheduleNextCardsPoll(eqJSON(expectedUpdatedGroups));
   1299 
   1300   // Invoking the tested function.
   1301   processServerResponse(serverResponse);
   1302 });
   1303 
   1304 TEST_F(TEST_NAME, 'ProcessServerResponseGoogleNowDisabled', function() {
   1305   // Tests processServerResponse function for the case when the response
   1306   // indicates that Google Now is disabled.
   1307 
   1308   // Setup and expectations.
   1309   var serverResponse = {
   1310     googleNowDisabled: true,
   1311     groups: {}
   1312   };
   1313 
   1314   this.makeAndRegisterMockGlobals([
   1315     'scheduleNextCardsPoll'
   1316   ]);
   1317 
   1318   this.makeAndRegisterMockApis([
   1319     'chrome.storage.local.set',
   1320     'fillFromChromeLocalStorage'
   1321   ]);
   1322 
   1323   this.mockApis.expects(once()).
   1324       chrome_storage_local_set(eqJSON({googleNowEnabled: false}));
   1325 
   1326   this.mockGlobals.expects(never()).scheduleNextCardsPoll();
   1327 
   1328   // Invoking the tested function.
   1329   processServerResponse(serverResponse);
   1330 });
   1331 
   1332