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  * shouldScheduleRetryFromGroupList Tests
    335  */
    336 TEST_F(TEST_NAME, 'ShouldScheduleRetryEmptyGroupList', function() {
    337   assertTrue(shouldScheduleRetryFromGroupList([]));
    338 });
    339 
    340 TEST_F(TEST_NAME, 'ShouldScheduleRetryNorOnlyGroupList', function() {
    341   assertFalse(shouldScheduleRetryFromGroupList(['NOR']));
    342 });
    343 
    344 TEST_F(TEST_NAME, 'ShouldScheduleRetryNotOnlyGroupList', function() {
    345   assertTrue(shouldScheduleRetryFromGroupList(['NOT']));
    346 });
    347 
    348 TEST_F(TEST_NAME, 'ShouldScheduleRetryMultipleGroupsList', function() {
    349   assertTrue(shouldScheduleRetryFromGroupList(['NOR', 'NOT']));
    350 });
    351 
    352 /**
    353  * requestAndUpdateOptIn Tests
    354  */
    355 TEST_F(TEST_NAME, 'RequestAndUpdateOptInOptedIn', function() {
    356   this.makeAndRegisterMockApis([
    357     'chrome.storage.local.set',
    358     'requestFromServer'
    359   ]);
    360 
    361   this.mockApis.expects(once()).requestFromServer('GET', 'settings/optin')
    362       .will(returnValue(Promise.resolve({
    363         status: 200,
    364         responseText: '{"value": true}'})));
    365 
    366   this.mockApis.expects(once())
    367       .chrome_storage_local_set(eqJSON({googleNowEnabled: true}));
    368 
    369   var thenCalled = false;
    370   var catchCalled = false;
    371   requestAndUpdateOptedIn().then(function(optedIn) {
    372     thenCalled = true;
    373     assertTrue(optedIn);
    374   }).catch(function() {
    375     catchCalled = true;
    376   });
    377   assertTrue(thenCalled);
    378   assertFalse(catchCalled);
    379 });
    380 
    381 TEST_F(TEST_NAME, 'RequestAndUpdateOptInOptedOut', function() {
    382   this.makeAndRegisterMockApis([
    383     'chrome.storage.local.set',
    384     'requestFromServer'
    385   ]);
    386 
    387   this.mockApis.expects(once()).requestFromServer('GET', 'settings/optin')
    388       .will(returnValue(Promise.resolve({
    389         status: 200,
    390         responseText: '{"value": false}'})));
    391 
    392   this.mockApis.expects(once())
    393       .chrome_storage_local_set(eqJSON({googleNowEnabled: false}));
    394 
    395   var thenCalled = false;
    396   var catchCalled = false;
    397   requestAndUpdateOptedIn().then(function(optedIn) {
    398     thenCalled = true;
    399     assertFalse(optedIn);
    400   }).catch(function() {
    401     catchCalled = true;
    402   });
    403   assertTrue(thenCalled);
    404   assertFalse(catchCalled);
    405 });
    406 
    407 TEST_F(TEST_NAME, 'RequestAndUpdateOptInFailure', function() {
    408   this.makeAndRegisterMockApis([
    409     'chrome.storage.local.set',
    410     'requestFromServer'
    411   ]);
    412 
    413   this.mockApis.expects(once()).requestFromServer('GET', 'settings/optin')
    414       .will(returnValue(Promise.reject({status: 404})));
    415 
    416   this.mockApis.expects(never()).chrome_storage_local_set();
    417 
    418   var thenCalled = false;
    419   var catchCalled = false;
    420   requestAndUpdateOptedIn().then(function() {
    421     thenCalled = true;
    422   }).catch(function() {
    423     catchCalled = true;
    424   });
    425   assertFalse(thenCalled);
    426   assertTrue(catchCalled);
    427 });
    428 
    429 /**
    430  * pollOptedInNoImmediateRecheck Tests
    431  */
    432 TEST_F(TEST_NAME, 'pollOptedInNoImmediateRecheckOptedIn', function() {
    433   this.makeAndRegisterMockApis([
    434     'requestAndUpdateOptedIn',
    435     'instrumented.metricsPrivate.getVariationParams',
    436     'optInPollAttempts.start'
    437   ]);
    438 
    439   this.mockApis.expects(once()).requestAndUpdateOptedIn()
    440       .will(returnValue(Promise.resolve(true)));
    441 
    442   this.mockApis.expects(never())
    443       .instrumented_metricsPrivate_getVariationParams();
    444 
    445   this.mockApis.expects(never()).optInPollAttempts_start();
    446 
    447   pollOptedInNoImmediateRecheck();
    448 });
    449 
    450 TEST_F(TEST_NAME, 'pollOptedInNoImmediateRecheckOptedOut', function() {
    451   this.makeAndRegisterMockApis([
    452     'requestAndUpdateOptedIn',
    453     'instrumented.metricsPrivate.getVariationParams',
    454     'optInPollAttempts.start'
    455   ]);
    456 
    457   this.mockApis.expects(once()).requestAndUpdateOptedIn()
    458       .will(returnValue(Promise.resolve(false)));
    459 
    460   var getVariationParamsSavedArgs = new SaveMockArguments();
    461   this.mockApis.expects(once())
    462       .instrumented_metricsPrivate_getVariationParams(
    463           getVariationParamsSavedArgs.match(eq('GoogleNow')),
    464           getVariationParamsSavedArgs.match(ANYTHING))
    465       .will(invokeCallback(getVariationParamsSavedArgs, 1, {}));
    466 
    467   this.mockApis.expects(once())
    468       .optInPollAttempts_start(DEFAULT_OPTIN_CHECK_PERIOD_SECONDS);
    469 
    470   pollOptedInNoImmediateRecheck();
    471 });
    472 
    473 TEST_F(TEST_NAME, 'pollOptedInNoImmediateRecheckFailure', function() {
    474   this.makeAndRegisterMockApis([
    475     'requestAndUpdateOptedIn',
    476     'instrumented.metricsPrivate.getVariationParams',
    477     'optInPollAttempts.start'
    478   ]);
    479 
    480   this.mockApis.expects(once()).requestAndUpdateOptedIn()
    481       .will(returnValue(Promise.reject()));
    482 
    483   var getVariationParamsSavedArgs = new SaveMockArguments();
    484   this.mockApis.expects(once())
    485       .instrumented_metricsPrivate_getVariationParams(
    486           getVariationParamsSavedArgs.match(eq('GoogleNow')),
    487           getVariationParamsSavedArgs.match(ANYTHING))
    488       .will(invokeCallback(getVariationParamsSavedArgs, 1, {}));
    489 
    490   this.mockApis.expects(once())
    491       .optInPollAttempts_start(DEFAULT_OPTIN_CHECK_PERIOD_SECONDS);
    492 
    493   pollOptedInNoImmediateRecheck();
    494 });
    495 
    496 /**
    497  * getGroupsToRequest Tests
    498  */
    499 TEST_F(TEST_NAME, 'GetGroupsToRequestNone', function() {
    500   this.makeAndRegisterMockApis([
    501     'fillFromChromeLocalStorage',
    502     'Date.now'
    503   ]);
    504 
    505   this.mockApis.expects(once())
    506       .fillFromChromeLocalStorage(eqJSON({notificationGroups: {}}))
    507       .will(returnValue(Promise.resolve({notificationGroups: {}})));
    508 
    509   this.mockApis.expects(once()).Date_now().will(returnValue(20));
    510 
    511   getGroupsToRequest().then(function(groupsToRequest) {
    512     assertTrue(JSON.stringify(groupsToRequest) === '[]');
    513   });
    514 });
    515 
    516 TEST_F(TEST_NAME, 'GetGroupsToRequestWithGroups', function() {
    517   this.makeAndRegisterMockApis([
    518     'fillFromChromeLocalStorage',
    519     'Date.now'
    520   ]);
    521 
    522   this.mockApis.expects(once())
    523       .fillFromChromeLocalStorage(eqJSON({notificationGroups: {}}))
    524       .will(returnValue(Promise.resolve({notificationGroups: {
    525         TIME18: {nextPollTime: 18},
    526         TIME19: {nextPollTime: 19},
    527         TIME20: {nextPollTime: 20},
    528         TIME21: {nextPollTime: 21},
    529         TIME22: {nextPollTime: 22},
    530         TIMEUNDEF: {}
    531       }})));
    532 
    533   this.mockApis.expects(once()).Date_now().will(returnValue(20));
    534 
    535   getGroupsToRequest().then(function(groupsToRequest) {
    536     assertTrue(groupsToRequest.length == 3);
    537     assertTrue(groupsToRequest.indexOf('TIME18') >= 0);
    538     assertTrue(groupsToRequest.indexOf('TIME19') >= 0);
    539     assertTrue(groupsToRequest.indexOf('TIME20') >= 0);
    540   });
    541 });
    542 
    543 /**
    544  * combineGroup Tests
    545  */
    546 TEST_F(TEST_NAME, 'CombineGroup', function() {
    547   // Tests combineGroup function. Verifies that both notifications with and
    548   // without show time are handled correctly and that cards are correctly
    549   // added to existing cards with same ID or start a new combined card.
    550 
    551   // Setup and expectations.
    552   var combinedCards = {
    553     'EXISTING CARD': [1]
    554   };
    555 
    556   var receivedNotificationNoShowTime = {
    557     chromeNotificationId: 'EXISTING CARD',
    558     trigger: {hideTimeSec: 1}
    559   };
    560   var receivedNotificationWithShowTime = {
    561     chromeNotificationId: 'NEW CARD',
    562     trigger: {showTimeSec: 2, hideTimeSec: 3}
    563   }
    564 
    565   var storedGroup = {
    566     cardsTimestamp: 10000,
    567     cards: [
    568       receivedNotificationNoShowTime,
    569       receivedNotificationWithShowTime
    570     ]
    571   };
    572 
    573   // Invoking the tested function.
    574   combineGroup(combinedCards, storedGroup);
    575 
    576   // Check the output value.
    577   var expectedCombinedCards = {
    578     'EXISTING CARD': [
    579       1,
    580       {
    581         receivedNotification: receivedNotificationNoShowTime,
    582         hideTime: 11000
    583       }
    584     ],
    585     'NEW CARD': [
    586       {
    587         receivedNotification: receivedNotificationWithShowTime,
    588         showTime: 12000,
    589         hideTime: 13000
    590       }
    591     ]
    592   };
    593 
    594   assertEquals(
    595       JSON.stringify(expectedCombinedCards),
    596       JSON.stringify(combinedCards));
    597 });
    598 
    599 /**
    600  * Mocks global functions and APIs that initialize() depends upon.
    601  * @param {Test} fixture Test fixture.
    602  */
    603 function mockInitializeDependencies(fixture) {
    604   fixture.makeAndRegisterMockGlobals([
    605     'pollOptedInNoImmediateRecheck',
    606     'recordEvent',
    607     'removeAllCards',
    608     'setBackgroundEnable',
    609     'startPollingCards',
    610     'stopPollingCards'
    611   ]);
    612   fixture.makeAndRegisterMockApis([
    613     'authenticationManager.isSignedIn',
    614     'chrome.storage.local.remove',
    615     'fillFromChromeLocalStorage',
    616     'instrumented.metricsPrivate.getVariationParams',
    617     'instrumented.notifications.getAll',
    618     'instrumented.notifications.getPermissionLevel',
    619     'instrumented.webstorePrivate.getBrowserLogin',
    620     'optInPollAttempts.isRunning',
    621     'optInPollAttempts.stop',
    622     'tasks.add',
    623     'updateCardsAttempts.isRunning',
    624     'updateCardsAttempts.stop'
    625   ]);
    626 }
    627 
    628 /**
    629  * Sets up the test to expect the state machine calls and send
    630  * the specified state machine state. Currently used to test initialize().
    631  * Note that this CAN NOT be used if any of the methods below are called
    632  * outside of this context with the same argument matchers.
    633  * expects() calls cannot be chained with the same argument matchers.
    634  * @param {object} fixture Test fixture.
    635  * @param {string} testIdentityToken getAuthToken callback token.
    636  * @param {object} testExperimentVariationParams Response of
    637  *     metricsPrivate.getVariationParams.
    638  * @param {string} testExperimentVariationParams Response of
    639  *     notifications.getPermissionLevel.
    640  * @param {boolean} testGoogleNowEnabled True if the user is opted in to Google
    641  *     Now.
    642  */
    643 function expectStateMachineCalls(
    644     fixture,
    645     testIdentityToken,
    646     testExperimentVariationParams,
    647     testNotificationPermissionLevel,
    648     testGoogleNowEnabled) {
    649   fixture.mockApis.expects(once()).
    650       authenticationManager_isSignedIn().
    651       will(returnValue(new Promise(function(resolve) {
    652         resolve(!!testIdentityToken);
    653       })));
    654 
    655   var getVariationParamsSavedArgs = new SaveMockArguments();
    656   fixture.mockApis.expects(once()).
    657       instrumented_metricsPrivate_getVariationParams(
    658           getVariationParamsSavedArgs.match(ANYTHING),
    659           getVariationParamsSavedArgs.match(ANYTHING)).
    660       will(invokeCallback(
    661           getVariationParamsSavedArgs, 1, testExperimentVariationParams));
    662 
    663   var notificationGetPermissionLevelSavedArgs = new SaveMockArguments();
    664   fixture.mockApis.expects(once()).
    665       instrumented_notifications_getPermissionLevel(
    666           notificationGetPermissionLevelSavedArgs.match(ANYTHING)).
    667       will(invokeCallback(
    668           notificationGetPermissionLevelSavedArgs,
    669           0,
    670           testNotificationPermissionLevel))
    671 
    672   expectChromeLocalStorageGet(
    673       fixture,
    674       {googleNowEnabled: false},
    675       {googleNowEnabled: testGoogleNowEnabled});
    676 
    677   var updateCardsAttemptsIsRunningSavedArgs = new SaveMockArguments();
    678   fixture.mockApis.expects(once()).
    679       updateCardsAttempts_isRunning(
    680           updateCardsAttemptsIsRunningSavedArgs.match(ANYTHING)).
    681       will(
    682           invokeCallback(
    683               updateCardsAttemptsIsRunningSavedArgs, 0, undefined));
    684 
    685   var optInPollAttemptsIsRunningSavedArgs = new SaveMockArguments();
    686   fixture.mockApis.expects(once()).
    687       optInPollAttempts_isRunning(
    688           optInPollAttemptsIsRunningSavedArgs.match(ANYTHING)).
    689       will(
    690           invokeCallback(
    691               optInPollAttemptsIsRunningSavedArgs, 0, undefined));
    692 }
    693 
    694 /**
    695  * Sets up the test to expect the initialization calls that
    696  * initialize() invokes.
    697  * Note that this CAN NOT be used if any of the methods below are called
    698  * outside of this context with the same argument matchers.
    699  * expects() calls cannot be chained with the same argument matchers.
    700  */
    701 function expectInitialization(fixture) {
    702   var tasksAddSavedArgs = new SaveMockArguments();
    703   fixture.mockApis.expects(once()).
    704       tasks_add(
    705           tasksAddSavedArgs.match(ANYTHING),
    706           tasksAddSavedArgs.match(ANYTHING)).
    707       will(invokeCallback(tasksAddSavedArgs, 1, function() {}));
    708 
    709   // The ordering here between stubs and expects is important.
    710   // We only care about the EXTENSION_START event. The other events are covered
    711   // by the NoCards tests below. Reversing the calls will cause all recordEvent
    712   // calls to be unexpected.
    713   fixture.mockGlobals.stubs().recordEvent(ANYTHING);
    714   fixture.mockGlobals.
    715       expects(once()).recordEvent(GoogleNowEvent.EXTENSION_START);
    716 }
    717 
    718 TEST_F(TEST_NAME,'Initialize_SignedOut', function() {
    719   // Tests the case when getAuthToken fails most likely because the user is
    720   // not signed in. In this case, the function should quietly exit after
    721   // finding out that getAuthToken fails.
    722 
    723   // Setup and expectations.
    724   var testIdentityToken = undefined;
    725   var testExperimentVariationParams = {};
    726   var testNotificationPermissionLevel = 'denied';
    727   var testGoogleNowEnabled = undefined;
    728 
    729   mockInitializeDependencies(this);
    730 
    731   expectInitialization(this);
    732 
    733   expectStateMachineCalls(
    734       this,
    735       testIdentityToken,
    736       testExperimentVariationParams,
    737       testNotificationPermissionLevel,
    738       testGoogleNowEnabled);
    739 
    740   this.mockGlobals.expects(once()).setBackgroundEnable(false);
    741   this.mockGlobals.expects(never()).startPollingCards();
    742   this.mockGlobals.expects(once()).stopPollingCards();
    743   this.mockGlobals.expects(once()).removeAllCards();
    744   this.mockGlobals.expects(never()).pollOptedInNoImmediateRecheck();
    745   this.mockApis.expects(once()).optInPollAttempts_stop();
    746 
    747   // Invoking the tested function.
    748   initialize();
    749 });
    750 
    751 TEST_F(TEST_NAME,'Initialize_NotificationDisabled', function() {
    752   // Tests the case when Google Now is disabled in the notifications center.
    753 
    754   // Setup and expectations.
    755   var testIdentityToken = 'some identity token';
    756   var testExperimentVariationParams = {};
    757   var testNotificationPermissionLevel = 'denied';
    758   var testGoogleNowEnabled = undefined;
    759 
    760   mockInitializeDependencies(this);
    761 
    762   expectInitialization(this);
    763 
    764   expectStateMachineCalls(
    765       this,
    766       testIdentityToken,
    767       testExperimentVariationParams,
    768       testNotificationPermissionLevel,
    769       testGoogleNowEnabled);
    770 
    771   this.mockGlobals.expects(once()).setBackgroundEnable(false);
    772   this.mockGlobals.expects(never()).startPollingCards();
    773   this.mockGlobals.expects(once()).stopPollingCards();
    774   this.mockGlobals.expects(once()).removeAllCards();
    775   this.mockGlobals.expects(never()).pollOptedInNoImmediateRecheck();
    776   this.mockApis.expects(once()).optInPollAttempts_stop();
    777 
    778   // Invoking the tested function.
    779   initialize();
    780 });
    781 
    782 TEST_F(TEST_NAME, 'Initialize_NoBackground', function() {
    783   // Tests when the no background variation is received.
    784 
    785   // Setup and expectations.
    786   var testIdentityToken = 'some identity token';
    787   var testExperimentVariationParams = {canEnableBackground: 'false'};
    788   var testNotificationPermissionLevel = 'granted';
    789   var testGoogleNowEnabled = true;
    790 
    791   mockInitializeDependencies(this);
    792 
    793   expectInitialization(this);
    794 
    795   expectStateMachineCalls(
    796       this,
    797       testIdentityToken,
    798       testExperimentVariationParams,
    799       testNotificationPermissionLevel,
    800       testGoogleNowEnabled);
    801 
    802   this.mockGlobals.expects(once()).setBackgroundEnable(false);
    803   this.mockGlobals.expects(once()).startPollingCards();
    804   this.mockGlobals.expects(never()).stopPollingCards();
    805   this.mockGlobals.expects(never()).removeAllCards();
    806   this.mockGlobals.expects(never()).pollOptedInNoImmediateRecheck();
    807   this.mockApis.expects(once()).optInPollAttempts_stop();
    808 
    809   // Invoking the tested function.
    810   initialize();
    811 });
    812 
    813 TEST_F(TEST_NAME, 'Initialize_GoogleNowDisabled', function() {
    814   // Tests when the user has Google Now disabled.
    815 
    816   // Setup and expectations.
    817   var testIdentityToken = 'some identity token';
    818   var testExperimentVariationParams = {};
    819   var testNotificationPermissionLevel = 'granted';
    820   var testGoogleNowEnabled = false;
    821 
    822   mockInitializeDependencies(this);
    823 
    824   expectInitialization(this);
    825 
    826   expectStateMachineCalls(
    827       this,
    828       testIdentityToken,
    829       testExperimentVariationParams,
    830       testNotificationPermissionLevel,
    831       testGoogleNowEnabled);
    832 
    833   this.mockGlobals.expects(once()).setBackgroundEnable(false);
    834   this.mockGlobals.expects(never()).startPollingCards();
    835   this.mockGlobals.expects(once()).stopPollingCards();
    836   this.mockGlobals.expects(once()).removeAllCards();
    837   this.mockGlobals.expects(once()).pollOptedInNoImmediateRecheck();
    838   this.mockApis.expects(never()).optInPollAttempts_stop();
    839 
    840   // Invoking the tested function.
    841   initialize();
    842 });
    843 
    844 TEST_F(TEST_NAME, 'Initialize_RunGoogleNow', function() {
    845   // Tests if Google Now will invoke startPollingCards when all
    846   // of the required state is fulfilled.
    847 
    848   // Setup and expectations.
    849   var testIdentityToken = 'some identity token';
    850   var testExperimentVariationParams = {};
    851   var testNotificationPermissionLevel = 'granted';
    852   var testGoogleNowEnabled = true;
    853 
    854   mockInitializeDependencies(this);
    855 
    856   expectInitialization(this);
    857 
    858   expectStateMachineCalls(
    859       this,
    860       testIdentityToken,
    861       testExperimentVariationParams,
    862       testNotificationPermissionLevel,
    863       testGoogleNowEnabled);
    864 
    865   this.mockGlobals.expects(once()).setBackgroundEnable(true);
    866   this.mockGlobals.expects(once()).startPollingCards();
    867   this.mockGlobals.expects(never()).stopPollingCards();
    868   this.mockGlobals.expects(never()).removeAllCards();
    869   this.mockGlobals.expects(never()).pollOptedInNoImmediateRecheck();
    870   this.mockApis.expects(once()).optInPollAttempts_stop();
    871 
    872   // Invoking the tested function.
    873   initialize();
    874 });
    875 
    876 /**
    877  * No Cards Event Recording Tests
    878  */
    879 TEST_F(TEST_NAME, 'NoCardsSignedOut', function() {
    880   var signedIn = false;
    881   var notificationEnabled = false;
    882   var googleNowEnabled = false;
    883   this.makeAndRegisterMockGlobals([
    884       'recordEvent',
    885       'removeAllCards',
    886       'setBackgroundEnable',
    887       'setShouldPollCards',
    888       'setShouldPollOptInStatus']);
    889 
    890   this.mockGlobals.stubs().removeAllCards();
    891   this.mockGlobals.stubs().setBackgroundEnable(ANYTHING);
    892   this.mockGlobals.stubs().setShouldPollCards(ANYTHING);
    893   this.mockGlobals.stubs().setShouldPollOptInStatus(ANYTHING);
    894 
    895   this.mockGlobals.expects(once()).recordEvent(
    896       GoogleNowEvent.STOPPED);
    897   this.mockGlobals.expects(once()).recordEvent(
    898       GoogleNowEvent.SIGNED_OUT);
    899   this.mockGlobals.expects(never()).recordEvent(
    900       GoogleNowEvent.NOTIFICATION_DISABLED);
    901   this.mockGlobals.expects(never()).recordEvent(
    902       GoogleNowEvent.GOOGLE_NOW_DISABLED);
    903   updateRunningState(signedIn, true, notificationEnabled, googleNowEnabled);
    904 });
    905 
    906 TEST_F(TEST_NAME, 'NoCardsNotificationsDisabled', function() {
    907   var signedIn = true;
    908   var notificationEnabled = false;
    909   var googleNowEnabled = false;
    910   this.makeAndRegisterMockGlobals([
    911       'recordEvent',
    912       'removeAllCards',
    913       'setBackgroundEnable',
    914       'setShouldPollCards',
    915       'setShouldPollOptInStatus']);
    916 
    917   this.mockGlobals.stubs().removeAllCards();
    918   this.mockGlobals.stubs().setBackgroundEnable(ANYTHING);
    919   this.mockGlobals.stubs().setShouldPollCards(ANYTHING);
    920   this.mockGlobals.stubs().setShouldPollOptInStatus(ANYTHING);
    921 
    922   this.mockGlobals.expects(once()).recordEvent(
    923       GoogleNowEvent.STOPPED);
    924   this.mockGlobals.expects(never()).recordEvent(
    925       GoogleNowEvent.SIGNED_OUT);
    926   this.mockGlobals.expects(once()).recordEvent(
    927       GoogleNowEvent.NOTIFICATION_DISABLED);
    928   this.mockGlobals.expects(never()).recordEvent(
    929       GoogleNowEvent.GOOGLE_NOW_DISABLED);
    930   updateRunningState(signedIn, true, notificationEnabled, googleNowEnabled);
    931 });
    932 
    933 TEST_F(TEST_NAME, 'NoCardsGoogleNowDisabled', function() {
    934   var signedIn = true;
    935   var notificationEnabled = true;
    936   var googleNowEnabled = false;
    937   this.makeAndRegisterMockGlobals([
    938       'recordEvent',
    939       'removeAllCards',
    940       'setBackgroundEnable',
    941       'setShouldPollCards',
    942       'setShouldPollOptInStatus']);
    943 
    944   this.mockGlobals.stubs().removeAllCards();
    945   this.mockGlobals.stubs().setBackgroundEnable(ANYTHING);
    946   this.mockGlobals.stubs().setShouldPollCards(ANYTHING);
    947   this.mockGlobals.stubs().setShouldPollOptInStatus(ANYTHING);
    948 
    949   this.mockGlobals.expects(never()).recordEvent(
    950       GoogleNowEvent.STOPPED);
    951   this.mockGlobals.expects(never()).recordEvent(
    952       GoogleNowEvent.SIGNED_OUT);
    953   this.mockGlobals.expects(never()).recordEvent(
    954       GoogleNowEvent.NOTIFICATION_DISABLED);
    955   this.mockGlobals.expects(once()).recordEvent(
    956       GoogleNowEvent.GOOGLE_NOW_DISABLED);
    957   updateRunningState(signedIn, true, notificationEnabled, googleNowEnabled);
    958 });
    959 
    960 TEST_F(TEST_NAME, 'NoCardsEverythingEnabled', function() {
    961   var signedIn = true;
    962   var notificationEnabled = true;
    963   var googleNowEnabled = true;
    964   this.makeAndRegisterMockGlobals([
    965       'recordEvent',
    966       'removeAllCards',
    967       'setBackgroundEnable',
    968       'setShouldPollCards',
    969       'setShouldPollOptInStatus']);
    970 
    971   this.mockGlobals.stubs().removeAllCards();
    972   this.mockGlobals.stubs().setBackgroundEnable(ANYTHING);
    973   this.mockGlobals.stubs().setShouldPollCards(ANYTHING);
    974   this.mockGlobals.stubs().setShouldPollOptInStatus(ANYTHING);
    975 
    976   this.mockGlobals.expects(never()).recordEvent(
    977       GoogleNowEvent.STOPPED);
    978   this.mockGlobals.expects(never()).recordEvent(
    979       GoogleNowEvent.SIGNED_OUT);
    980   this.mockGlobals.expects(never()).recordEvent(
    981       GoogleNowEvent.NOTIFICATION_DISABLED);
    982   this.mockGlobals.expects(never()).recordEvent(
    983       GoogleNowEvent.GOOGLE_NOW_DISABLED);
    984   updateRunningState(signedIn, true, notificationEnabled, googleNowEnabled);
    985 });
    986 
    987 /**
    988  * Mocks global functions and APIs that onNotificationClicked() depends upon.
    989  * @param {Test} fixture Test fixture.
    990  */
    991 function mockOnNotificationClickedDependencies(fixture) {
    992   fixture.makeAndRegisterMockApis([
    993     'chrome.windows.create',
    994     'chrome.windows.update',
    995     'fillFromChromeLocalStorage',
    996     'instrumented.tabs.create']);
    997 }
    998 
    999 TEST_F(TEST_NAME, 'OnNotificationClicked_NoData', function() {
   1000   // Tests the case when there is no data associated with notification id.
   1001   // In this case, the function should do nothing.
   1002 
   1003   // Setup and expectations.
   1004   var testNotificationId = 'TEST_ID';
   1005   var testNotificationDataRequest = {notificationsData: {}};
   1006   var testNotificationData = {notificationsData: {}};
   1007 
   1008   mockOnNotificationClickedDependencies(this);
   1009   this.makeMockLocalFunctions(['selector']);
   1010 
   1011   expectChromeLocalStorageGet(
   1012       this, testNotificationDataRequest, testNotificationData);
   1013 
   1014   // Invoking the tested function.
   1015   onNotificationClicked(
   1016       testNotificationId, this.mockLocalFunctions.functions().selector);
   1017 });
   1018 
   1019 TEST_F(TEST_NAME, 'OnNotificationClicked_ActionUrlsUndefined', function() {
   1020   // Tests the case when the data associated with notification id is
   1021   // 'undefined'.
   1022   // In this case, the function should do nothing.
   1023 
   1024   // Setup and expectations.
   1025   var testActionUrls = undefined;
   1026   var testNotificationId = 'TEST_ID';
   1027   var testNotificationDataRequest = {notificationsData: {}};
   1028   var testNotificationData = {
   1029       notificationsData: {'TEST_ID': {actionUrls: testActionUrls}}
   1030   };
   1031 
   1032   mockOnNotificationClickedDependencies(this);
   1033   this.makeMockLocalFunctions(['selector']);
   1034 
   1035   expectChromeLocalStorageGet(
   1036       this, testNotificationDataRequest, testNotificationData);
   1037 
   1038   this.mockLocalFunctions.expects(once())
   1039       .selector(eqJSON(
   1040           testNotificationData.notificationsData[testNotificationId]))
   1041       .will(returnValue(undefined));
   1042 
   1043   // Invoking the tested function.
   1044   onNotificationClicked(
   1045       testNotificationId, this.mockLocalFunctions.functions().selector);
   1046 });
   1047 
   1048 TEST_F(TEST_NAME, 'OnNotificationClicked_TabCreateSuccess', function() {
   1049   // Tests the selected URL is OK and crome.tabs.create suceeds.
   1050 
   1051   // Setup and expectations.
   1052   var testActionUrls = {testField: 'TEST VALUE'};
   1053   var testNotificationId = 'TEST_ID';
   1054   var testNotificationDataRequest = {notificationsData: {}};
   1055   var testNotificationData = {
   1056       notificationsData: {'TEST_ID': {actionUrls: testActionUrls}}
   1057   };
   1058   var testActionUrl = 'http://testurl.com';
   1059   var testCreatedTab = {windowId: 239};
   1060 
   1061   mockOnNotificationClickedDependencies(this);
   1062   this.makeMockLocalFunctions(['selector']);
   1063 
   1064   expectChromeLocalStorageGet(
   1065       this, testNotificationDataRequest, testNotificationData);
   1066   this.mockLocalFunctions.expects(once())
   1067       .selector(eqJSON(
   1068           testNotificationData.notificationsData[testNotificationId]))
   1069       .will(returnValue(testActionUrl));
   1070   var chromeTabsCreateSavedArgs = new SaveMockArguments();
   1071   this.mockApis.expects(once()).
   1072       instrumented_tabs_create(
   1073           chromeTabsCreateSavedArgs.match(eqJSON({url: testActionUrl})),
   1074           chromeTabsCreateSavedArgs.match(ANYTHING)).
   1075       will(invokeCallback(chromeTabsCreateSavedArgs, 1, testCreatedTab));
   1076   this.mockApis.expects(once()).chrome_windows_update(
   1077       testCreatedTab.windowId,
   1078       eqJSON({focused: true}));
   1079 
   1080   // Invoking the tested function.
   1081   onNotificationClicked(
   1082       testNotificationId, this.mockLocalFunctions.functions().selector);
   1083 });
   1084 
   1085 TEST_F(TEST_NAME, 'OnNotificationClicked_TabCreateFail', function() {
   1086   // Tests the selected URL is OK and crome.tabs.create fails.
   1087   // In this case, the function should invoke chrome.windows.create as a
   1088   // second attempt.
   1089 
   1090   // Setup and expectations.
   1091   var testActionUrls = {testField: 'TEST VALUE'};
   1092   var testNotificationId = 'TEST_ID';
   1093   var testNotificationDataRequest = {notificationsData: {}};
   1094   var testNotificationData = {
   1095     notificationsData: {'TEST_ID': {actionUrls: testActionUrls}}
   1096   };
   1097   var testActionUrl = 'http://testurl.com';
   1098   var testCreatedTab = undefined; // chrome.tabs.create fails
   1099 
   1100   mockOnNotificationClickedDependencies(this);
   1101   this.makeMockLocalFunctions(['selector']);
   1102 
   1103   expectChromeLocalStorageGet(
   1104       this, testNotificationDataRequest, testNotificationData);
   1105   this.mockLocalFunctions.expects(once())
   1106       .selector(eqJSON(
   1107           testNotificationData.notificationsData[testNotificationId]))
   1108       .will(returnValue(testActionUrl));
   1109   var chromeTabsCreateSavedArgs = new SaveMockArguments();
   1110   this.mockApis.expects(once()).
   1111       instrumented_tabs_create(
   1112           chromeTabsCreateSavedArgs.match(eqJSON({url: testActionUrl})),
   1113           chromeTabsCreateSavedArgs.match(ANYTHING)).
   1114       will(invokeCallback(chromeTabsCreateSavedArgs, 1, testCreatedTab));
   1115   this.mockApis.expects(once()).chrome_windows_create(
   1116       eqJSON({url: testActionUrl, focused: true}));
   1117 
   1118   // Invoking the tested function.
   1119   onNotificationClicked(
   1120       testNotificationId, this.mockLocalFunctions.functions().selector);
   1121 });
   1122 
   1123 TEST_F(TEST_NAME, 'ShowNotificationGroups', function() {
   1124   // Tests showNotificationGroups function. Checks that the function properly
   1125   // deletes the card that didn't get an update, updates existing card and
   1126   // creates a new card that previously didn't exist.
   1127 
   1128   // Setup and expectations.
   1129   var existingNotifications = {
   1130     'SHOULD BE DELETED': 'SOMETHING',
   1131     'SHOULD BE KEPT': 'SOMETHING'
   1132   };
   1133 
   1134   var keptCard = {
   1135     chromeNotificationId: 'SHOULD BE KEPT',
   1136     trigger: {showTimeSec: 0, hideTimeSec: 0}
   1137   };
   1138 
   1139   var keptNotification = {
   1140     receivedNotification: keptCard,
   1141     showTime: 0,
   1142     hideTime: 0
   1143   };
   1144 
   1145   var newCard = {
   1146     chromeNotificationId: 'NEW CARD',
   1147     trigger: {showTimeSec: 0, hideTimeSec: 0}
   1148   };
   1149 
   1150   var newNotification = {
   1151     receivedNotification: newCard,
   1152     showTime: 0,
   1153     hideTime: 0
   1154   };
   1155 
   1156   var notificationGroups = {
   1157     'TEST GROUP 1': {cards: [keptCard], cardsTimestamp: 0},
   1158     'TEST GROUP 2': {cards: [newCard], cardsTimestamp: 0}
   1159   };
   1160 
   1161   var fakeOnCardShownFunction = 'FAKE ON CARD SHOWN FUNCTION';
   1162 
   1163   var expectedUpdatedNotifications = {
   1164     'SHOULD BE KEPT': 'KEPT CARD NOTIFICATION DATA',
   1165     'NEW CARD': 'NEW CARD NOTIFICATION DATA'
   1166   };
   1167 
   1168   this.makeAndRegisterMockApis([
   1169     'cardSet.update',
   1170     'chrome.storage.local.set',
   1171     'instrumented.notifications.getAll'
   1172   ]);
   1173   this.makeMockLocalFunctions([
   1174     'onSuccess'
   1175   ]);
   1176 
   1177   var notificationsGetAllSavedArgs = new SaveMockArguments();
   1178   this.mockApis.expects(once()).
   1179       instrumented_notifications_getAll(
   1180           notificationsGetAllSavedArgs.match(ANYTHING)).
   1181       will(invokeCallback(
   1182           notificationsGetAllSavedArgs, 0, existingNotifications));
   1183 
   1184   this.mockApis.expects(once()).
   1185       cardSet_update(
   1186           'SHOULD BE KEPT',
   1187           eqJSON([keptNotification]),
   1188           eqJSON(notificationGroups),
   1189           fakeOnCardShownFunction).
   1190       will(returnValue('KEPT CARD NOTIFICATION DATA'));
   1191   this.mockApis.expects(once()).
   1192       cardSet_update(
   1193           'NEW CARD',
   1194           eqJSON([newNotification]),
   1195           eqJSON(notificationGroups),
   1196           fakeOnCardShownFunction).
   1197       will(returnValue('NEW CARD NOTIFICATION DATA'));
   1198   this.mockApis.expects(once()).
   1199       cardSet_update(
   1200           'SHOULD BE DELETED',
   1201           [],
   1202           eqJSON(notificationGroups),
   1203           fakeOnCardShownFunction).
   1204       will(returnValue(undefined));
   1205 
   1206   this.mockApis.expects(once()).
   1207       chrome_storage_local_set(
   1208           eqJSON({notificationsData: expectedUpdatedNotifications}));
   1209 
   1210   this.mockLocalFunctions.expects(once()).
   1211       onSuccess(undefined);
   1212 
   1213   // Invoking the tested function.
   1214   showNotificationGroups(notificationGroups, fakeOnCardShownFunction)
   1215       .then(this.mockLocalFunctions.functions().onSuccess);
   1216 });
   1217 
   1218 TEST_F(TEST_NAME, 'ProcessServerResponse', function() {
   1219   // Tests processServerResponse function.
   1220 
   1221   // Setup and expectations.
   1222   Date.now = function() { return 3000000; };
   1223 
   1224   // GROUP1 was requested and contains cards c4 and c5. For c5, there is a
   1225   // non-expired dismissal, so it will be ignored.
   1226   // GROUP2 was not requested, but is contained in server response to
   1227   // indicate that the group still exists. Stored group GROUP2 won't change.
   1228   // GROUP3 is stored, but is not present in server's response, which means
   1229   // it doesn't exist anymore. This group will be deleted.
   1230   // GROUP4 doesn't contain cards, but it was requested. This is treated as
   1231   // if it had an empty array of cards. Cards in the stored group will be
   1232   // replaced with an empty array.
   1233   // GROUP5 doesn't have next poll time, and it will be stored without next
   1234   // poll time.
   1235   var serverResponse = {
   1236     groups: {
   1237       GROUP1: {requested: true, nextPollSeconds: 46},
   1238       GROUP2: {requested: false},
   1239       GROUP4: {requested: true, nextPollSeconds: 45},
   1240       GROUP5: {requested: true}
   1241     },
   1242     notifications: [
   1243       {notificationId: 'c4', groupName: 'GROUP1'},
   1244       {notificationId: 'c5', groupName: 'GROUP1'}
   1245     ]
   1246   };
   1247 
   1248   var recentDismissals = {
   1249     c4: 1800000, // expired dismissal
   1250     c5: 1800001  // non-expired dismissal
   1251   };
   1252 
   1253   var storedGroups = {
   1254     GROUP2: {
   1255       cards: [{notificationId: 'c2'}],
   1256       cardsTimestamp: 239,
   1257       nextPollTime: 10000
   1258     },
   1259     GROUP3: {
   1260       cards: [{notificationId: 'c3'}],
   1261       cardsTimestamp: 240,
   1262       nextPollTime: 10001
   1263     },
   1264     GROUP4: {
   1265       cards: [{notificationId: 'c6'}],
   1266       cardsTimestamp: 241,
   1267       nextPollTime: 10002
   1268     }
   1269   };
   1270 
   1271   var expectedUpdatedGroups = {
   1272     GROUP1: {
   1273       cards: [{notificationId: 'c4', groupName: 'GROUP1'}],
   1274       cardsTimestamp: 3000000,
   1275       nextPollTime: 3046000
   1276     },
   1277     GROUP2: {
   1278       cards: [{notificationId: 'c2'}],
   1279       cardsTimestamp: 239,
   1280       nextPollTime: 10000
   1281     },
   1282     GROUP4: {
   1283       cards: [],
   1284       cardsTimestamp: 3000000,
   1285       nextPollTime: 3045000
   1286     },
   1287     GROUP5: {
   1288       cards: [],
   1289       cardsTimestamp: 3000000
   1290     }
   1291   };
   1292 
   1293   var expectedUpdatedRecentDismissals = {
   1294     c5: 1800001
   1295   };
   1296 
   1297   this.makeAndRegisterMockGlobals([
   1298     'scheduleNextCardsPoll'
   1299   ]);
   1300 
   1301   this.makeAndRegisterMockApis([
   1302     'fillFromChromeLocalStorage',
   1303   ]);
   1304 
   1305   expectChromeLocalStorageGet(
   1306       this,
   1307       {
   1308         notificationGroups: {},
   1309         recentDismissals: {}
   1310       },
   1311       {
   1312         notificationGroups: storedGroups,
   1313         recentDismissals: recentDismissals
   1314       });
   1315 
   1316   this.mockGlobals.expects(once())
   1317       .scheduleNextCardsPoll(eqJSON(expectedUpdatedGroups));
   1318 
   1319   // Invoking the tested function.
   1320   processServerResponse(serverResponse);
   1321 });
   1322 
   1323 TEST_F(TEST_NAME, 'ProcessServerResponseGoogleNowDisabled', function() {
   1324   // Tests processServerResponse function for the case when the response
   1325   // indicates that Google Now is disabled.
   1326 
   1327   // Setup and expectations.
   1328   var serverResponse = {
   1329     googleNowDisabled: true,
   1330     groups: {}
   1331   };
   1332 
   1333   this.makeAndRegisterMockGlobals([
   1334     'scheduleNextCardsPoll'
   1335   ]);
   1336 
   1337   this.makeAndRegisterMockApis([
   1338     'chrome.storage.local.set',
   1339     'fillFromChromeLocalStorage'
   1340   ]);
   1341 
   1342   this.mockApis.expects(once()).
   1343       chrome_storage_local_set(eqJSON({googleNowEnabled: false}));
   1344 
   1345   this.mockGlobals.expects(never()).scheduleNextCardsPoll();
   1346 
   1347   // Invoking the tested function.
   1348   processServerResponse(serverResponse);
   1349 });
   1350 
   1351