OLD | NEW |
(Empty) | |
| 1 // History api test for Chrome. |
| 2 // browser_tests.exe --gtest_filter=ExtensionApiTest.History |
| 3 |
| 4 var pass = chrome.test.callbackPass; |
| 5 var fail = chrome.test.callbackFail; |
| 6 var assertEq = chrome.test.assertEq; |
| 7 |
| 8 var GOOGLE_URL = 'http://www.google.com/'; |
| 9 var PICASA_URL = 'http://www.picasa.com/'; |
| 10 var A_RELATIVE_URL = |
| 11 'http://www.a.com:1337/files/extensions/api_test/history/a.html'; |
| 12 var B_RELATIVE_URL = |
| 13 'http://www.b.com:1337/files/extensions/api_test/history/b.html'; |
| 14 |
| 15 /** |
| 16 * A helper function to flip the setTimeout arguments and make the code more |
| 17 * readable. |
| 18 * @param {number} seconds The number of seconds to wait. |
| 19 * @param {function} callback Closure. |
| 20 */ |
| 21 function waitAFewSeconds(seconds, callback) { |
| 22 setTimeout(callback, seconds * 1000); |
| 23 }; |
| 24 |
| 25 /** |
| 26 * Object used for listening to the chrome.history.onVisited events. The global |
| 27 * object 'itemVisited' stores the last item received. |
| 28 */ |
| 29 var itemVisitedCallback = null; |
| 30 function itemVisitedListener(visited) { |
| 31 if (null != itemVisitedCallback) { |
| 32 itemVisitedCallback(visited); |
| 33 }; |
| 34 }; |
| 35 |
| 36 function removeItemVisitedListener() { |
| 37 chrome.experimental.history.onVisited.removeListener(itemVisitedListener); |
| 38 itemVisitedCallback = null; |
| 39 } |
| 40 |
| 41 function setItemVisitedListener(callback) { |
| 42 chrome.experimental.history.onVisited.addListener(itemVisitedListener); |
| 43 itemVisitedCallback = callback; |
| 44 } |
| 45 |
| 46 function setNextItemVisitedListener(callback) { |
| 47 itemVisitedCallback = callback; |
| 48 } |
| 49 |
| 50 /** |
| 51 * An object used for listening to the chrome.history.onVisitRemoved events. The |
| 52 * global object 'itemRemovedInfo' stores the information from the last callback. |
| 53 */ |
| 54 var itemRemovedCallback = null; |
| 55 function itemRemovedListener(removed) { |
| 56 if (null != itemRemovedCallback) { |
| 57 itemRemovedCallback(removed); |
| 58 }; |
| 59 }; |
| 60 |
| 61 function removeItemRemovedListener() { |
| 62 chrome.experimental.history.onVisited.removeListener(itemRemovedListener); |
| 63 itemRemovedCallback = null; |
| 64 } |
| 65 |
| 66 function setItemRemovedListener(callback) { |
| 67 chrome.experimental.history.onVisitRemoved.addListener(itemRemovedListener); |
| 68 itemRemovedCallback = callback; |
| 69 } |
| 70 |
| 71 function setNextItemRemovedListener(callback) { |
| 72 itemRemovedCallback = callback; |
| 73 } |
| 74 |
| 75 /** |
| 76 * An object used for listening to the chrome.history.onVisitRemoved events. Set |
| 77 * 'tabCompleteCallback' to a function to add extra processing to the callback. |
| 78 * The global object 'tabsCompleteData' contains a list of the last known state |
| 79 * of every tab. |
| 80 */ |
| 81 var tabCompleteCallback = null; |
| 82 var tabsCompleteData = {}; |
| 83 function tabsCompleteListener(tabId, changeInfo) { |
| 84 if (changeInfo && changeInfo.status) { |
| 85 tabsCompleteData[tabId] = changeInfo.status; |
| 86 }; |
| 87 if (null != tabCompleteCallback) { |
| 88 tabCompleteCallback(); |
| 89 }; |
| 90 }; |
| 91 |
| 92 /** |
| 93 * Queries the entire history for items, calling the closure with an argument |
| 94 * specifying the the number of items in the query. |
| 95 * @param {function(number)} callback The closure. |
| 96 */ |
| 97 function countItemsInHistory(callback) { |
| 98 var query = {'search': ''}; |
| 99 chrome.experimental.history.search(query, function(results) { |
| 100 callback(results.length); |
| 101 }); |
| 102 }; |
| 103 |
| 104 /** |
| 105 * Populates the history by calling addUrl for each url in the array urls. |
| 106 * @param {Array.<string>} urls The array of urls to populate the history. |
| 107 * @param {function} callback Closure. |
| 108 */ |
| 109 function populateHistory(urls, callback) { |
| 110 urls.forEach(function(url) { |
| 111 chrome.experimental.history.addUrl({ 'url': url }); |
| 112 }); |
| 113 callback(); |
| 114 }; |
| 115 |
| 116 chrome.test.runTests([ |
| 117 // All the tests require a blank state to start from. This test is run |
| 118 // first to insure that state can be acheived. |
| 119 function clearHistory() { |
| 120 chrome.experimental.history.deleteAll(pass(function() { |
| 121 countItemsInHistory(pass(function(count) { |
| 122 assertEq(0, count); |
| 123 })); |
| 124 })); |
| 125 }, |
| 126 |
| 127 function basicSearch() { |
| 128 // basicSearch callback. |
| 129 function basicSearchTestVerification() { |
| 130 removeItemVisitedListener(); |
| 131 var query = { 'search': '' }; |
| 132 chrome.experimental.history.search(query, function(results) { |
| 133 assertEq(1, results.length); |
| 134 assertEq(GOOGLE_URL, results[0].url); |
| 135 |
| 136 // The test has succeeded. |
| 137 chrome.test.succeed(); |
| 138 }); |
| 139 }; |
| 140 |
| 141 // basicSearch entry point. |
| 142 chrome.experimental.history.deleteAll(function() { |
| 143 setItemVisitedListener(basicSearchTestVerification); |
| 144 populateHistory([GOOGLE_URL], function() { }); |
| 145 }); |
| 146 }, |
| 147 |
| 148 function timeScopedSearch() { |
| 149 var startDate = {}; |
| 150 var endDate = {}; |
| 151 |
| 152 function timeScopedSearchTestVerification() { |
| 153 removeItemVisitedListener(); |
| 154 |
| 155 var query = { 'search': '', |
| 156 'startTime': startDate.getTime(), |
| 157 'endTime': endDate.getTime() }; |
| 158 chrome.experimental.history.search(query, function(results) { |
| 159 assertEq(1, results.length); |
| 160 assertEq(GOOGLE_URL, results[0].url); |
| 161 |
| 162 // The test has succeeded. |
| 163 chrome.test.succeed(); |
| 164 }); |
| 165 }; |
| 166 |
| 167 function onAddedItem() { |
| 168 // Set the next test callback. |
| 169 setNextItemVisitedListener(timeScopedSearchTestVerification); |
| 170 |
| 171 // Chrome has seconds resolution, so we must wait in order to search |
| 172 // a range. |
| 173 waitAFewSeconds(2, function() { |
| 174 endDate = new Date(); |
| 175 endDate.setTime(endDate.getTime() - 500); |
| 176 populateHistory([PICASA_URL], function() { }); |
| 177 }); |
| 178 }; |
| 179 |
| 180 // timeScopedSearch entry point. |
| 181 chrome.experimental.history.deleteAll(function() { |
| 182 // Set the test callback. |
| 183 setItemVisitedListener(onAddedItem); |
| 184 // Set the start time a few seconds in the past. |
| 185 startDate = new Date(); |
| 186 startDate.setTime(startDate.getTime() - 1000); |
| 187 populateHistory([GOOGLE_URL], function() { }); |
| 188 }); |
| 189 }, |
| 190 |
| 191 // Give time epochs x,y,z and history events A,B which occur in the sequence |
| 192 // x A y B z, this test scopes the search to the interval [y,z] to test that |
| 193 // [x,y) is excluded. The previous test scoped to the interval [x,y]. |
| 194 function timeScopedSearch2() { |
| 195 var startDate = {}; |
| 196 var endDate = {}; |
| 197 |
| 198 function timeScopedSearch2TestVerification() { |
| 199 removeItemVisitedListener(); |
| 200 |
| 201 endDate = new Date(); |
| 202 endDate.setTime(endDate.getTime() + 1000); |
| 203 var query = { 'search': '', |
| 204 'startTime': startDate.getTime(), |
| 205 'endTime': endDate.getTime() }; |
| 206 chrome.experimental.history.search(query, function(results) { |
| 207 assertEq(1, results.length); |
| 208 assertEq(PICASA_URL, results[0].url); |
| 209 |
| 210 // The test has succeeded. |
| 211 chrome.test.succeed(); |
| 212 }); |
| 213 }; |
| 214 |
| 215 function onAddedItem() { |
| 216 // Set the next test callback. |
| 217 setNextItemVisitedListener(timeScopedSearch2TestVerification); |
| 218 |
| 219 // Chrome has seconds resolution, so we must wait in order to search |
| 220 // a range. |
| 221 waitAFewSeconds(2, function() { |
| 222 startDate = new Date(); |
| 223 startDate.setTime(startDate.getTime() - 1000); |
| 224 populateHistory([PICASA_URL], function() { }); |
| 225 }); |
| 226 }; |
| 227 |
| 228 // timeScopedSearch entry point. |
| 229 chrome.experimental.history.deleteAll(function() { |
| 230 // Set the test callback. |
| 231 setItemVisitedListener(onAddedItem); |
| 232 populateHistory([GOOGLE_URL], function() { }); |
| 233 }); |
| 234 }, |
| 235 |
| 236 function lengthScopedSearch() { |
| 237 var urls = [GOOGLE_URL, PICASA_URL]; |
| 238 var urlsAdded = 0; |
| 239 |
| 240 function lengthScopedSearchTestVerification() { |
| 241 // Ensure all urls have been added. |
| 242 urlsAdded += 1; |
| 243 if (urlsAdded < urls.length) |
| 244 return; |
| 245 |
| 246 removeItemVisitedListener(); |
| 247 |
| 248 var query = { 'search': '', 'maxResults': 1 }; |
| 249 chrome.experimental.history.search(query, function(results) { |
| 250 assertEq(1, results.length); |
| 251 assertEq(PICASA_URL, results[0].url); |
| 252 |
| 253 // The test has succeeded. |
| 254 chrome.test.succeed(); |
| 255 }); |
| 256 }; |
| 257 |
| 258 // lengthScopedSearch entry point. |
| 259 chrome.experimental.history.deleteAll(function() { |
| 260 setItemVisitedListener(lengthScopedSearchTestVerification); |
| 261 populateHistory(urls, function() { }); |
| 262 }); |
| 263 }, |
| 264 |
| 265 function fullTextSearch() { |
| 266 chrome.experimental.history.deleteAll(function() { |
| 267 // The continuation of the test after the windows have been opened. |
| 268 var validateTest = function() { |
| 269 // Continue with the test. |
| 270 // A title search for www.a.com should find a. |
| 271 var query = { 'search': 'www.a.com' }; |
| 272 chrome.experimental.history.search(query, function(results) { |
| 273 assertEq(1, results.length); |
| 274 assertEq(A_RELATIVE_URL, results[0].url); |
| 275 |
| 276 // Text in the body of b.html. |
| 277 query = { 'search': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }; |
| 278 chrome.experimental.history.search(query, function(results) { |
| 279 assertEq(1, results.length); |
| 280 assertEq(B_RELATIVE_URL, results[0].url); |
| 281 |
| 282 // The test has succeeded. |
| 283 chrome.test.succeed(); |
| 284 }); |
| 285 }); |
| 286 }; |
| 287 |
| 288 // Setup a callback object for tab events. |
| 289 var urls = [A_RELATIVE_URL, B_RELATIVE_URL]; |
| 290 var tabIds = []; |
| 291 |
| 292 function listenerCallback() { |
| 293 if (tabIds.length < urls.length) { |
| 294 return; |
| 295 }; |
| 296 |
| 297 // Ensure both tabs have completed loading. |
| 298 for (var index = 0, id; id = tabIds[index]; index++) { |
| 299 if (!tabsCompleteData[id] || |
| 300 tabsCompleteData[id] != 'complete') { |
| 301 return; |
| 302 }; |
| 303 }; |
| 304 |
| 305 // Unhook callbacks. |
| 306 tabCompleteCallback = null; |
| 307 chrome.tabs.onUpdated.removeListener(tabsCompleteListener); |
| 308 |
| 309 // Allow indexing to occur. |
| 310 waitAFewSeconds(2, function() { |
| 311 validateTest(); |
| 312 }); |
| 313 }; |
| 314 |
| 315 tabCompleteCallback = listenerCallback; |
| 316 chrome.tabs.onUpdated.addListener(tabsCompleteListener); |
| 317 |
| 318 // Navigate to a few pages. |
| 319 urls.forEach(function(url) { |
| 320 chrome.tabs.create({ 'url': url }, function(tab) { |
| 321 tabIds.push(tab.id); |
| 322 }); |
| 323 }); |
| 324 }); |
| 325 }, |
| 326 |
| 327 function getVisits() { |
| 328 // getVisits callback. |
| 329 function getVisitsTestVerification() { |
| 330 removeItemVisitedListener(); |
| 331 |
| 332 // Verify that we received the url. |
| 333 var query = { 'search': '' }; |
| 334 chrome.experimental.history.search(query, function(results) { |
| 335 assertEq(1, results.length); |
| 336 assertEq(GOOGLE_URL, results[0].url); |
| 337 |
| 338 var id = results[0].id; |
| 339 chrome.experimental.history.getVisits({ 'url': GOOGLE_URL }, function(re
sults) { |
| 340 assertEq(1, results.length); |
| 341 assertEq(id, results[0].id); |
| 342 |
| 343 // The test has succeeded. |
| 344 chrome.test.succeed(); |
| 345 }); |
| 346 }); |
| 347 }; |
| 348 |
| 349 // getVisits entry point. |
| 350 chrome.experimental.history.deleteAll(function() { |
| 351 setItemVisitedListener(getVisitsTestVerification); |
| 352 populateHistory([GOOGLE_URL], function() { }); |
| 353 }); |
| 354 }, |
| 355 |
| 356 function deleteUrl() { |
| 357 function deleteUrlTestVerification() { |
| 358 removeItemRemovedListener(); |
| 359 |
| 360 var query = { 'search': '' }; |
| 361 chrome.experimental.history.search(query, function(results) { |
| 362 assertEq(0, results.length); |
| 363 |
| 364 // The test has succeeded. |
| 365 chrome.test.succeed(); |
| 366 }); |
| 367 }; |
| 368 |
| 369 function onAddedItem() { |
| 370 removeItemVisitedListener(); |
| 371 |
| 372 var query = { 'search': '' }; |
| 373 chrome.experimental.history.search(query, function(results) { |
| 374 assertEq(1, results.length); |
| 375 assertEq(GOOGLE_URL, results[0].url); |
| 376 |
| 377 chrome.experimental.history.deleteUrl({ 'url': GOOGLE_URL }); |
| 378 }); |
| 379 }; |
| 380 |
| 381 // deleteUrl entry point. |
| 382 chrome.experimental.history.deleteAll(function() { |
| 383 setItemVisitedListener(onAddedItem); |
| 384 setItemRemovedListener(deleteUrlTestVerification); |
| 385 populateHistory([GOOGLE_URL], function() { }); |
| 386 }); |
| 387 }, |
| 388 |
| 389 function deleteRange() { |
| 390 var urls = [GOOGLE_URL, PICASA_URL]; |
| 391 var startDate = {}; |
| 392 var endDate = {}; |
| 393 var itemsAdded = 0; |
| 394 |
| 395 function deleteRangeTestVerification() { |
| 396 removeItemRemovedListener(); |
| 397 |
| 398 var query = { 'search': '' }; |
| 399 chrome.experimental.history.search(query, function(results) { |
| 400 assertEq(1, results.length); |
| 401 assertEq(PICASA_URL, results[0].url); |
| 402 |
| 403 // The test has succeeded. |
| 404 chrome.test.succeed(); |
| 405 }); |
| 406 }; |
| 407 |
| 408 function onAddedItem() { |
| 409 itemsAdded += 1; |
| 410 |
| 411 if (itemsAdded < urls.length) { |
| 412 // Chrome has seconds resolution, so we must wait to search a range. |
| 413 waitAFewSeconds(2, function() { |
| 414 endDate = new Date(); |
| 415 endDate.setTime(endDate.getTime() - 1000); |
| 416 populateHistory([urls[itemsAdded]], function() { }); |
| 417 }); |
| 418 return; |
| 419 }; |
| 420 |
| 421 removeItemVisitedListener(); |
| 422 chrome.experimental.history.deleteRange({ 'startTime': startDate.getTime()
, |
| 423 'endTime': endDate.getTime() }, |
| 424 function() { }); |
| 425 }; |
| 426 |
| 427 // deletRange entry point. |
| 428 chrome.experimental.history.deleteAll(function() { |
| 429 setItemVisitedListener(onAddedItem); |
| 430 setItemRemovedListener(deleteRangeTestVerification); |
| 431 |
| 432 startDate = new Date(); |
| 433 startDate.setTime(startDate.getTime() - 1000); |
| 434 |
| 435 populateHistory([urls[itemsAdded]], function() { }); |
| 436 }); |
| 437 }, |
| 438 |
| 439 // Suppose we have time epochs x,y,z and history events A,B which occur in the |
| 440 // sequence x A y B z. The previous deleteRange test deleted the range [x,y], |
| 441 // this test deletes the range [y,z]. |
| 442 function deleteRange2() { |
| 443 var urls = [GOOGLE_URL, PICASA_URL]; |
| 444 var startDate = {}; |
| 445 var endDate = {}; |
| 446 var itemsAdded = 0; |
| 447 |
| 448 function deleteRange2TestVerification() { |
| 449 removeItemRemovedListener(); |
| 450 |
| 451 var query = { 'search': '' }; |
| 452 chrome.experimental.history.search(query, function(results) { |
| 453 assertEq(1, results.length); |
| 454 assertEq(GOOGLE_URL, results[0].url); |
| 455 |
| 456 // The test has succeeded. |
| 457 chrome.test.succeed(); |
| 458 }); |
| 459 }; |
| 460 |
| 461 function onAddedItem() { |
| 462 itemsAdded += 1; |
| 463 |
| 464 if (itemsAdded < urls.length) { |
| 465 // Chrome has seconds resolution, so we must wait to search a range. |
| 466 waitAFewSeconds(2, function() { |
| 467 startDate = new Date(); |
| 468 startDate.setTime(startDate.getTime() - 1000); |
| 469 populateHistory([urls[itemsAdded]], function() { }); |
| 470 }); |
| 471 return; |
| 472 }; |
| 473 |
| 474 removeItemVisitedListener(); |
| 475 |
| 476 endDate = new Date(); |
| 477 endDate.setTime(endDate.getTime() + 1000); |
| 478 chrome.experimental.history.deleteRange({ 'startTime': startDate.getTime()
, |
| 479 'endTime': endDate.getTime() }, |
| 480 function() { }); |
| 481 }; |
| 482 |
| 483 // deletRange entry point. |
| 484 chrome.experimental.history.deleteAll(function() { |
| 485 setItemVisitedListener(onAddedItem); |
| 486 setItemRemovedListener(deleteRange2TestVerification); |
| 487 populateHistory([urls[itemsAdded]], function() { }); |
| 488 }); |
| 489 }, |
| 490 |
| 491 // This test has the following format. From the main entry point we |
| 492 // attach a listener and give that listener a state to callback on. After |
| 493 // receipt of the first message we validate it is as excepted in |
| 494 // 'firsPageVisit.' We update the callback to 'secondPageVisit' and modify |
| 495 //the history. |
| 496 function listenForAdd() { |
| 497 function secondPageVisit(visited) { |
| 498 // Update state machine. |
| 499 itemVisitedCallback = null; |
| 500 chrome.experimental.history.onVisited.removeListener(itemVisitedListener); |
| 501 |
| 502 // Validate the state. |
| 503 countItemsInHistory(function(count) { |
| 504 assertEq(2, count); |
| 505 assertEq(PICASA_URL, visited.url); |
| 506 |
| 507 // The test has succeeded. |
| 508 chrome.test.succeed(); |
| 509 }); |
| 510 }; |
| 511 |
| 512 function firstPageVisit(visited) { |
| 513 // Update state machine. |
| 514 itemVisitedCallback = secondPageVisit; |
| 515 |
| 516 // Validate the state. |
| 517 countItemsInHistory(function(count) { |
| 518 assertEq(1, count); |
| 519 assertEq(GOOGLE_URL, visited.url); |
| 520 |
| 521 // Transistion to next state. |
| 522 populateHistory([PICASA_URL], function() { |
| 523 }); |
| 524 }); |
| 525 }; |
| 526 |
| 527 // Populate the history one item at a time. |
| 528 itemVisitedCallback = firstPageVisit; |
| 529 chrome.experimental.history.onVisited.addListener(itemVisitedListener); |
| 530 |
| 531 chrome.experimental.history.deleteAll(function() { |
| 532 populateHistory([GOOGLE_URL], function() { |
| 533 }); |
| 534 }); |
| 535 }, |
| 536 |
| 537 function listenForDelete() { |
| 538 var urls = [GOOGLE_URL, PICASA_URL]; |
| 539 var visited_urls = {}; |
| 540 |
| 541 // Callback for deletions. |
| 542 function deleteRangeCallback(removed) { |
| 543 // We will no longer need to listen to deletions. |
| 544 itemRemovedCallback = null; |
| 545 chrome.experimental.history.onVisitRemoved.removeListener( |
| 546 itemRemovedListener); |
| 547 |
| 548 // The two added urls should have been deleted together. |
| 549 assertEq(false, removed.allHistory); |
| 550 assertEq(2, removed.urls.length); |
| 551 |
| 552 // History should be empty. |
| 553 countItemsInHistory(function(count) { |
| 554 assertEq(0, count); |
| 555 |
| 556 // The test has succeeded. |
| 557 chrome.test.succeed(); |
| 558 }); |
| 559 }; |
| 560 |
| 561 // Callback for page visits. |
| 562 function historyPopulatedCallback(visited) { |
| 563 visited_urls[visited_urls.length] = visited.url; |
| 564 |
| 565 if (visited_urls.length < urls.length) { |
| 566 return; |
| 567 } |
| 568 |
| 569 // We will no longer need to listen to visits. |
| 570 itemVisitedCallback = null; |
| 571 chrome.experimental.history.onVisited.removeListener(itemVisitedListener); |
| 572 countItemsInHistory(function(count) { |
| 573 assertEq(2, count); |
| 574 |
| 575 var startDate = new Date(); |
| 576 startDate.setDate(startDate.getDate() - 1); |
| 577 var endDate = new Date(); |
| 578 chrome.experimental.history.deleteRange( |
| 579 { 'startTime': startDate.getTime(), 'endTime': endDate.getTime() }, |
| 580 function() { }); |
| 581 }); |
| 582 }; |
| 583 |
| 584 // We must be called back after the initial |
| 585 itemVisitedCallback = historyPopulatedCallback; |
| 586 chrome.experimental.history.onVisited.addListener(itemVisitedListener); |
| 587 |
| 588 chrome.experimental.history.deleteAll(function() { |
| 589 // Do not add the listener until after we have reset the state of history. |
| 590 itemRemovedCallback = deleteRangeCallback; |
| 591 chrome.experimental.history.onVisitRemoved.addListener( |
| 592 itemRemovedListener); |
| 593 |
| 594 // This call triggers the onVisited event handler. |
| 595 populateHistory(urls, function() { }); |
| 596 }); |
| 597 }, |
| 598 ]); |
OLD | NEW |