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 /** 6 * Test fixture for background.js. 7 * @constructor 8 * @extends {testing.Test} 9 */ 10 function GoogleNowBackgroundUnitTest () { 11 testing.Test.call(this); 12 } 13 14 GoogleNowBackgroundUnitTest.prototype = { 15 __proto__: testing.Test.prototype, 16 17 /** @override */ 18 extraLibraries: [ 19 'background_test_util.js', 20 'background.js' 21 ] 22 }; 23 24 TEST_F('GoogleNowBackgroundUnitTest', 'AreTasksConflicting', function() { 25 function testTaskPair(newTaskName, scheduledTaskName, expected) { 26 assertTrue(areTasksConflicting(newTaskName, scheduledTaskName) == expected, 27 '(' + newTaskName + ', ' + scheduledTaskName + ')'); 28 } 29 30 testTaskPair(UPDATE_CARDS_TASK_NAME, UPDATE_CARDS_TASK_NAME, true); 31 testTaskPair(UPDATE_CARDS_TASK_NAME, DISMISS_CARD_TASK_NAME, false); 32 testTaskPair(UPDATE_CARDS_TASK_NAME, RETRY_DISMISS_TASK_NAME, false); 33 testTaskPair(UPDATE_CARDS_TASK_NAME, STATE_CHANGED_TASK_NAME, false); 34 35 testTaskPair(DISMISS_CARD_TASK_NAME, UPDATE_CARDS_TASK_NAME, false); 36 testTaskPair(DISMISS_CARD_TASK_NAME, DISMISS_CARD_TASK_NAME, false); 37 testTaskPair(DISMISS_CARD_TASK_NAME, RETRY_DISMISS_TASK_NAME, false); 38 testTaskPair(DISMISS_CARD_TASK_NAME, STATE_CHANGED_TASK_NAME, false); 39 40 testTaskPair(RETRY_DISMISS_TASK_NAME, UPDATE_CARDS_TASK_NAME, true); 41 testTaskPair(RETRY_DISMISS_TASK_NAME, DISMISS_CARD_TASK_NAME, true); 42 testTaskPair(RETRY_DISMISS_TASK_NAME, RETRY_DISMISS_TASK_NAME, true); 43 testTaskPair(RETRY_DISMISS_TASK_NAME, STATE_CHANGED_TASK_NAME, false); 44 45 testTaskPair(STATE_CHANGED_TASK_NAME, UPDATE_CARDS_TASK_NAME, false); 46 testTaskPair(STATE_CHANGED_TASK_NAME, DISMISS_CARD_TASK_NAME, false); 47 testTaskPair(STATE_CHANGED_TASK_NAME, RETRY_DISMISS_TASK_NAME, false); 48 testTaskPair(STATE_CHANGED_TASK_NAME, STATE_CHANGED_TASK_NAME, false); 49 }); 50 51 /** 52 * Mocks global functions and APIs that initialize() depends upon. 53 * @param {Test} fixture Test fixture. 54 */ 55 function mockInitializeDependencies(fixture) { 56 fixture.makeAndRegisterMockGlobals([ 57 'recordEvent', 58 'showWelcomeToast', 59 'startPollingCards' 60 ]); 61 fixture.makeAndRegisterMockApis([ 62 'authenticationManager.isSignedIn', 63 'chrome.location.clearWatch', 64 'chrome.storage.local.set', 65 'instrumented.notifications.getAll', 66 'instrumented.preferencesPrivate.googleGeolocationAccessEnabled.get', 67 'instrumented.storage.local.get', 68 'tasks.add', 69 'updateCardsAttempts.isRunning', 70 'updateCardsAttempts.stop' 71 ]); 72 } 73 74 /** 75 * Sets up the test to expect the state machine calls and send 76 * the specified state machine state. Currently used to test initialize(). 77 * Note that this CAN NOT be used if any of the methods below are called 78 * outside of this context with the same argument matchers. 79 * expects() calls cannot be chained with the same argument matchers. 80 * @param {object} mockApisObj Mock APIs Object. 81 * @param {string} testIdentityToken getAuthToken callback token. 82 * @param {boolean} testGeolocationPref Geolocation Preference callback value. 83 * @param {boolean} testUserRespondedToToast User Response to toast 84 & callback value. 85 */ 86 function expectStateMachineCalls( 87 mockApisObj, 88 testIdentityToken, 89 testGeolocationPref, 90 testUserRespondedToToast) { 91 var authenticationManagerIsSignedInSavedArgs = new SaveMockArguments(); 92 mockApisObj.expects(once()). 93 authenticationManager_isSignedIn( 94 authenticationManagerIsSignedInSavedArgs.match(ANYTHING)). 95 will(invokeCallback( 96 authenticationManagerIsSignedInSavedArgs, 97 0, 98 testIdentityToken)); 99 100 var googleGeolocationPrefGetSavedArgs = new SaveMockArguments(); 101 mockApisObj.expects(once()). 102 instrumented_preferencesPrivate_googleGeolocationAccessEnabled_get( 103 googleGeolocationPrefGetSavedArgs.match(eqJSON({})), 104 googleGeolocationPrefGetSavedArgs.match(ANYTHING)). 105 will(invokeCallback( 106 googleGeolocationPrefGetSavedArgs, 1, {value: testGeolocationPref})); 107 108 var storageGetSavedArgs = new SaveMockArguments(); 109 mockApisObj.expects(once()). 110 instrumented_storage_local_get( 111 storageGetSavedArgs.match(eq('userRespondedToToast')), 112 storageGetSavedArgs.match(ANYTHING)). 113 will(invokeCallback(storageGetSavedArgs, 1, testUserRespondedToToast)); 114 } 115 116 /** 117 * Sets up the test to expect the initialization calls that 118 * initialize() invokes. 119 * Note that this CAN NOT be used if any of the methods below are called 120 * outside of this context with the same argument matchers. 121 * expects() calls cannot be chained with the same argument matchers. 122 */ 123 function expectInitialization(mockApisObj) { 124 mockApisObj.expects(once()). 125 chrome_location_clearWatch(ANYTHING); 126 mockApisObj.expects(once()). 127 updateCardsAttempts_stop(); 128 mockApisObj.expects(once()). 129 chrome_storage_local_set(eqJSON({notificationsData: {}})); 130 var tasksAddSavedArgs = new SaveMockArguments(); 131 mockApisObj.expects(once()). 132 tasks_add( 133 tasksAddSavedArgs.match(ANYTHING), 134 tasksAddSavedArgs.match(ANYTHING)). 135 will(invokeCallback(tasksAddSavedArgs, 1, function() {})); 136 var updateCardsAttemptsIsRunningSavedArgs = new SaveMockArguments(); 137 mockApisObj.expects(once()). 138 updateCardsAttempts_isRunning( 139 updateCardsAttemptsIsRunningSavedArgs.match(ANYTHING)). 140 will( 141 invokeCallback( 142 updateCardsAttemptsIsRunningSavedArgs, 0, false)); 143 } 144 145 TEST_F( 146 'GoogleNowBackgroundUnitTest', 147 'Initialize_ToastStateEmpty1', 148 function() { 149 // Tests the case when the user isn't signed in and NOTIFICATION_CARDS_URL 150 // is not set. Since NOTIFICATION_CARDS_URL is empty, 151 // nothing should start. 152 153 // Setup and expectations. 154 NOTIFICATION_CARDS_URL = undefined; 155 var testIdentityToken = undefined; 156 var testGeolocationPref = false; 157 var testUserRespondedToToast = {}; 158 159 mockInitializeDependencies(this); 160 161 this.mockGlobals.expects(once()).recordEvent( 162 GoogleNowEvent.EXTENSION_START); 163 164 this.mockGlobals.expects(once()).recordEvent( 165 GoogleNowEvent.STOPPED); 166 167 expectInitialization(this.mockApis); 168 169 expectStateMachineCalls( 170 this.mockApis, 171 testIdentityToken, 172 testGeolocationPref, 173 testUserRespondedToToast); 174 175 var chromeNotificationGetAllSavedArgs = new SaveMockArguments(); 176 this.mockApis.expects(exactly(2)). 177 instrumented_notifications_getAll( 178 chromeNotificationGetAllSavedArgs.match(ANYTHING)). 179 will( 180 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}), 181 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {})); 182 183 // TODO(robliao,vadimt): Determine the granularity of testing to perform. 184 185 // Invoking the tested function. 186 initialize(); 187 }); 188 189 TEST_F( 190 'GoogleNowBackgroundUnitTest', 191 'Initialize_ToastStateEmpty2', 192 function() { 193 // Tests the case when NOTIFICATION_CARDS_URL is but getAuthToken fails 194 // most likely because the user is not signed in. In this case, the 195 // function should quietly exit after finding out that getAuthToken fails. 196 197 // Setup and expectations. 198 NOTIFICATION_CARDS_URL = 'https://some.server.url.com'; 199 var testIdentityToken = undefined; 200 var testGeolocationPref = false; 201 var testUserRespondedToToast = {}; 202 203 mockInitializeDependencies(this); 204 205 this.mockGlobals.expects(once()).recordEvent( 206 GoogleNowEvent.EXTENSION_START); 207 208 this.mockGlobals.expects(once()).recordEvent( 209 GoogleNowEvent.STOPPED); 210 211 expectInitialization(this.mockApis); 212 213 expectStateMachineCalls( 214 this.mockApis, 215 testIdentityToken, 216 testGeolocationPref, 217 testUserRespondedToToast); 218 219 var chromeNotificationGetAllSavedArgs = new SaveMockArguments(); 220 this.mockApis.expects(exactly(2)). 221 instrumented_notifications_getAll( 222 chromeNotificationGetAllSavedArgs.match(ANYTHING)). 223 will( 224 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}), 225 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {})); 226 227 // Invoking the tested function. 228 initialize(); 229 }); 230 231 TEST_F( 232 'GoogleNowBackgroundUnitTest', 233 'Initialize_ToastStateEmpty3', 234 function() { 235 // Tests the case when NOTIFICATION_CARDS_URL is set, getAuthToken 236 // succeeds, and the user has never responded to the toast. 237 // In this case, the function should invoke showWelcomeToast(). 238 239 // Setup and expectations. 240 NOTIFICATION_CARDS_URL = 'https://some.server.url.com'; 241 var testIdentityToken = 'some identity token'; 242 var testGeolocationPref = false; 243 var testUserRespondedToToast = {}; 244 245 mockInitializeDependencies(this); 246 247 this.mockGlobals.expects(once()).recordEvent( 248 GoogleNowEvent.EXTENSION_START); 249 250 expectInitialization(this.mockApis); 251 252 expectStateMachineCalls( 253 this.mockApis, 254 testIdentityToken, 255 testGeolocationPref, 256 testUserRespondedToToast); 257 258 var chromeNotificationGetAllSavedArgs = new SaveMockArguments(); 259 this.mockApis.expects(exactly(2)). 260 instrumented_notifications_getAll( 261 chromeNotificationGetAllSavedArgs.match(ANYTHING)). 262 will( 263 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}), 264 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {})); 265 266 this.mockGlobals.expects(once()).showWelcomeToast(); 267 268 // Invoking the tested function. 269 initialize(); 270 }); 271 272 TEST_F('GoogleNowBackgroundUnitTest', 'Initialize_RunGoogleNow', function() { 273 // Tests if Google Now will invoke startPollingCards when all 274 // of the required state is fulfilled. 275 276 // Setup and expectations. 277 NOTIFICATION_CARDS_URL = 'https://some.server.url.com'; 278 var testIdentityToken = 'some identity token'; 279 var testGeolocationPref = true; 280 var testUserRespondedToToast = {userRespondedToToast: true}; 281 282 mockInitializeDependencies(this); 283 284 this.mockGlobals.expects(once()).recordEvent( 285 GoogleNowEvent.EXTENSION_START); 286 287 expectInitialization(this.mockApis); 288 289 expectStateMachineCalls( 290 this.mockApis, 291 testIdentityToken, 292 testGeolocationPref, 293 testUserRespondedToToast); 294 295 var chromeNotificationGetAllSavedArgs = new SaveMockArguments(); 296 this.mockApis.expects(exactly(2)). 297 instrumented_notifications_getAll( 298 chromeNotificationGetAllSavedArgs.match(ANYTHING)). 299 will( 300 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}), 301 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {})); 302 303 this.mockGlobals.expects(once()).startPollingCards(); 304 305 // Invoking the tested function. 306 initialize(); 307 }); 308 309 TEST_F('GoogleNowBackgroundUnitTest', 'Initialize_NoGeolocation', function() { 310 // Tests the case where everything is in place except for the 311 // Geolocation Preference after the user responded to the toast. 312 313 // Setup and expectations. 314 NOTIFICATION_CARDS_URL = 'https://some.server.url.com'; 315 var testIdentityToken = 'some identity token'; 316 var testGeolocationPref = false; 317 var testUserRespondedToToast = {userRespondedToToast: true}; 318 319 mockInitializeDependencies(this); 320 321 this.mockGlobals.expects(once()).recordEvent( 322 GoogleNowEvent.EXTENSION_START); 323 324 this.mockGlobals.expects(once()).recordEvent( 325 GoogleNowEvent.USER_SUPPRESSED); 326 327 expectInitialization(this.mockApis); 328 329 expectStateMachineCalls( 330 this.mockApis, 331 testIdentityToken, 332 testGeolocationPref, 333 testUserRespondedToToast); 334 335 var chromeNotificationGetAllSavedArgs = new SaveMockArguments(); 336 this.mockApis.expects(exactly(2)). 337 instrumented_notifications_getAll( 338 chromeNotificationGetAllSavedArgs.match(ANYTHING)). 339 will( 340 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}), 341 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {})); 342 343 // Invoking the tested function. 344 initialize(); 345 }); 346 347 /** 348 * Mocks global functions and APIs that onNotificationClicked() depends upon. 349 * @param {Test} fixture Test fixture. 350 */ 351 function mockOnNotificationClickedDependencies(fixture) { 352 fixture.makeAndRegisterMockApis([ 353 'chrome.windows.create', 354 'instrumented.storage.local.get', 355 'instrumented.tabs.create']); 356 } 357 358 TEST_F( 359 'GoogleNowBackgroundUnitTest', 360 'OnNotificationClicked_NoData', 361 function() { 362 // Tests the case when there is no data associated with notification id. 363 // In this case, the function should do nothing. 364 365 // Setup and expectations. 366 var testNotificationId = 'TEST_ID'; 367 var testNotificationData = {}; 368 369 mockOnNotificationClickedDependencies(this); 370 this.makeMockLocalFunctions(['selector']); 371 372 var storageGetSavedArgs = new SaveMockArguments(); 373 this.mockApis.expects(once()). 374 instrumented_storage_local_get( 375 storageGetSavedArgs.match(eq('notificationsData')), 376 storageGetSavedArgs.match(ANYTHING)). 377 will(invokeCallback(storageGetSavedArgs, 1, testNotificationData)); 378 379 // Invoking the tested function. 380 onNotificationClicked( 381 testNotificationId, this.mockLocalFunctions.functions().selector); 382 }); 383 384 TEST_F( 385 'GoogleNowBackgroundUnitTest', 386 'OnNotificationClicked_ActionUrlsNotObject', 387 function() { 388 // Tests the case when the data associated with notification id is not an 389 // object, probably because of a malformed server response. 390 // In this case, the function should do nothing. 391 392 // Setup and expectations. 393 var testActionUrls = 'string, not object'; 394 var testNotificationId = 'TEST_ID'; 395 var testNotificationData = { 396 notificationsData: {'TEST_ID': {actionUrls: testActionUrls}} 397 }; 398 399 mockOnNotificationClickedDependencies(this); 400 this.makeMockLocalFunctions(['selector']); 401 402 var storageGetSavedArgs = new SaveMockArguments(); 403 this.mockApis.expects(once()). 404 instrumented_storage_local_get( 405 storageGetSavedArgs.match(eq('notificationsData')), 406 storageGetSavedArgs.match(ANYTHING)). 407 will(invokeCallback(storageGetSavedArgs, 1, testNotificationData)); 408 409 // Invoking the tested function. 410 onNotificationClicked( 411 testNotificationId, this.mockLocalFunctions.functions().selector); 412 }); 413 414 TEST_F( 415 'GoogleNowBackgroundUnitTest', 416 'OnNotificationClicked_UrlNotString', 417 function() { 418 // Tests the case when selector returns a non-string, probably because of 419 // a malformed server response. 420 // In this case, the function should do nothing. 421 422 // Setup and expectations. 423 var testActionUrls = {testField: 'TEST VALUE'}; 424 var testNotificationId = 'TEST_ID'; 425 var testNotificationData = { 426 notificationsData: {'TEST_ID': {actionUrls: testActionUrls}} 427 }; 428 var testActionUrl = {}; 429 430 mockOnNotificationClickedDependencies(this); 431 this.makeMockLocalFunctions(['selector']); 432 433 var storageGetSavedArgs = new SaveMockArguments(); 434 this.mockApis.expects(once()). 435 instrumented_storage_local_get( 436 storageGetSavedArgs.match(eq('notificationsData')), 437 storageGetSavedArgs.match(ANYTHING)). 438 will(invokeCallback(storageGetSavedArgs, 1, testNotificationData)); 439 this.mockLocalFunctions.expects(once()).selector(testActionUrls).will( 440 returnValue(testActionUrl)); 441 442 // Invoking the tested function. 443 onNotificationClicked( 444 testNotificationId, this.mockLocalFunctions.functions().selector); 445 }); 446 447 TEST_F( 448 'GoogleNowBackgroundUnitTest', 449 'OnNotificationClicked_TabCreateSuccess', 450 function() { 451 // Tests the selected URL is OK and crome.tabs.create suceeds. 452 453 // Setup and expectations. 454 var testActionUrls = {testField: 'TEST VALUE'}; 455 var testNotificationId = 'TEST_ID'; 456 var testNotificationData = { 457 notificationsData: {'TEST_ID': {actionUrls: testActionUrls}} 458 }; 459 var testActionUrl = 'http://testurl.com'; 460 var testCreatedTab = {}; 461 462 mockOnNotificationClickedDependencies(this); 463 this.makeMockLocalFunctions(['selector']); 464 465 var storageGetSavedArgs = new SaveMockArguments(); 466 this.mockApis.expects(once()). 467 instrumented_storage_local_get( 468 storageGetSavedArgs.match(eq('notificationsData')), 469 storageGetSavedArgs.match(ANYTHING)). 470 will(invokeCallback(storageGetSavedArgs, 1, testNotificationData)); 471 this.mockLocalFunctions.expects(once()).selector(testActionUrls).will( 472 returnValue(testActionUrl)); 473 var chromeTabsCreateSavedArgs = new SaveMockArguments(); 474 this.mockApis.expects(once()). 475 instrumented_tabs_create( 476 chromeTabsCreateSavedArgs.match(eqJSON({url: testActionUrl})), 477 chromeTabsCreateSavedArgs.match(ANYTHING)). 478 will(invokeCallback(chromeTabsCreateSavedArgs, 1, testCreatedTab)); 479 480 // Invoking the tested function. 481 onNotificationClicked( 482 testNotificationId, this.mockLocalFunctions.functions().selector); 483 }); 484 485 TEST_F( 486 'GoogleNowBackgroundUnitTest', 487 'OnNotificationClicked_TabCreateFail', 488 function() { 489 // Tests the selected URL is OK and crome.tabs.create fails. 490 // In this case, the function should invoke chrome.windows.create as a 491 // second attempt. 492 493 // Setup and expectations. 494 var testActionUrls = {testField: 'TEST VALUE'}; 495 var testNotificationId = 'TEST_ID'; 496 var testNotificationData = { 497 notificationsData: {'TEST_ID': {actionUrls: testActionUrls}} 498 }; 499 var testActionUrl = 'http://testurl.com'; 500 var testCreatedTab = undefined; // chrome.tabs.create fails 501 502 mockOnNotificationClickedDependencies(this); 503 this.makeMockLocalFunctions(['selector']); 504 505 var storageGetSavedArgs = new SaveMockArguments(); 506 this.mockApis.expects(once()). 507 instrumented_storage_local_get( 508 storageGetSavedArgs.match(eq('notificationsData')), 509 storageGetSavedArgs.match(ANYTHING)). 510 will(invokeCallback(storageGetSavedArgs, 1, testNotificationData)); 511 this.mockLocalFunctions.expects(once()).selector(testActionUrls).will( 512 returnValue(testActionUrl)); 513 var chromeTabsCreateSavedArgs = new SaveMockArguments(); 514 this.mockApis.expects(once()). 515 instrumented_tabs_create( 516 chromeTabsCreateSavedArgs.match(eqJSON({url: testActionUrl})), 517 chromeTabsCreateSavedArgs.match(ANYTHING)). 518 will(invokeCallback(chromeTabsCreateSavedArgs, 1, testCreatedTab)); 519 this.mockApis.expects(once()).chrome_windows_create( 520 eqJSON({url: testActionUrl})); 521 522 // Invoking the tested function. 523 onNotificationClicked( 524 testNotificationId, this.mockLocalFunctions.functions().selector); 525 }); 526