toolkit/components/places/tests/unit/test_async_history_api.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* Any copyright is dedicated to the Public Domain.
michael@0 2 http://creativecommons.org/publicdomain/zero/1.0/ */
michael@0 3
michael@0 4 /**
michael@0 5 * This file tests the async history API exposed by mozIAsyncHistory.
michael@0 6 */
michael@0 7
michael@0 8 ////////////////////////////////////////////////////////////////////////////////
michael@0 9 //// Globals
michael@0 10
michael@0 11 const TEST_DOMAIN = "http://mozilla.org/";
michael@0 12 const URI_VISIT_SAVED = "uri-visit-saved";
michael@0 13 const RECENT_EVENT_THRESHOLD = 15 * 60 * 1000000;
michael@0 14
michael@0 15 ////////////////////////////////////////////////////////////////////////////////
michael@0 16 //// Helpers
michael@0 17 /**
michael@0 18 * Object that represents a mozIVisitInfo object.
michael@0 19 *
michael@0 20 * @param [optional] aTransitionType
michael@0 21 * The transition type of the visit. Defaults to TRANSITION_LINK if not
michael@0 22 * provided.
michael@0 23 * @param [optional] aVisitTime
michael@0 24 * The time of the visit. Defaults to now if not provided.
michael@0 25 */
michael@0 26 function VisitInfo(aTransitionType,
michael@0 27 aVisitTime)
michael@0 28 {
michael@0 29 this.transitionType =
michael@0 30 aTransitionType === undefined ? TRANSITION_LINK : aTransitionType;
michael@0 31 this.visitDate = aVisitTime || Date.now() * 1000;
michael@0 32 }
michael@0 33
michael@0 34 function promiseUpdatePlaces(aPlaces) {
michael@0 35 let deferred = Promise.defer();
michael@0 36 PlacesUtils.asyncHistory.updatePlaces(aPlaces, {
michael@0 37 _errors: [],
michael@0 38 _results: [],
michael@0 39 handleError: function handleError(aResultCode, aPlace) {
michael@0 40 this._errors.push({ resultCode: aResultCode, info: aPlace});
michael@0 41 },
michael@0 42 handleResult: function handleResult(aPlace) {
michael@0 43 this._results.push(aPlace);
michael@0 44 },
michael@0 45 handleCompletion: function handleCompletion() {
michael@0 46 deferred.resolve({ errors: this._errors, results: this._results });
michael@0 47 }
michael@0 48 });
michael@0 49
michael@0 50 return deferred.promise;
michael@0 51 }
michael@0 52
michael@0 53 /**
michael@0 54 * Listens for a title change notification, and calls aCallback when it gets it.
michael@0 55 *
michael@0 56 * @param aURI
michael@0 57 * The URI of the page we expect a notification for.
michael@0 58 * @param aExpectedTitle
michael@0 59 * The expected title of the URI we expect a notification for.
michael@0 60 * @param aCallback
michael@0 61 * The method to call when we have gotten the proper notification about
michael@0 62 * the title changing.
michael@0 63 */
michael@0 64 function TitleChangedObserver(aURI,
michael@0 65 aExpectedTitle,
michael@0 66 aCallback)
michael@0 67 {
michael@0 68 this.uri = aURI;
michael@0 69 this.expectedTitle = aExpectedTitle;
michael@0 70 this.callback = aCallback;
michael@0 71 }
michael@0 72 TitleChangedObserver.prototype = {
michael@0 73 __proto__: NavHistoryObserver.prototype,
michael@0 74 onTitleChanged: function(aURI,
michael@0 75 aTitle,
michael@0 76 aGUID)
michael@0 77 {
michael@0 78 do_log_info("onTitleChanged(" + aURI.spec + ", " + aTitle + ", " + aGUID + ")");
michael@0 79 if (!this.uri.equals(aURI)) {
michael@0 80 return;
michael@0 81 }
michael@0 82 do_check_eq(aTitle, this.expectedTitle);
michael@0 83 do_check_guid_for_uri(aURI, aGUID);
michael@0 84 this.callback();
michael@0 85 },
michael@0 86 };
michael@0 87
michael@0 88 /**
michael@0 89 * Listens for a visit notification, and calls aCallback when it gets it.
michael@0 90 *
michael@0 91 * @param aURI
michael@0 92 * The URI of the page we expect a notification for.
michael@0 93 * @param aCallback
michael@0 94 * The method to call when we have gotten the proper notification about
michael@0 95 * being visited.
michael@0 96 */
michael@0 97 function VisitObserver(aURI,
michael@0 98 aGUID,
michael@0 99 aCallback)
michael@0 100 {
michael@0 101 this.uri = aURI;
michael@0 102 this.guid = aGUID;
michael@0 103 this.callback = aCallback;
michael@0 104 }
michael@0 105 VisitObserver.prototype = {
michael@0 106 __proto__: NavHistoryObserver.prototype,
michael@0 107 onVisit: function(aURI,
michael@0 108 aVisitId,
michael@0 109 aTime,
michael@0 110 aSessionId,
michael@0 111 aReferringId,
michael@0 112 aTransitionType,
michael@0 113 aGUID)
michael@0 114 {
michael@0 115 do_log_info("onVisit(" + aURI.spec + ", " + aVisitId + ", " + aTime +
michael@0 116 ", " + aSessionId + ", " + aReferringId + ", " +
michael@0 117 aTransitionType + ", " + aGUID + ")");
michael@0 118 if (!this.uri.equals(aURI) || this.guid != aGUID) {
michael@0 119 return;
michael@0 120 }
michael@0 121 this.callback(aTime, aTransitionType);
michael@0 122 },
michael@0 123 };
michael@0 124
michael@0 125 /**
michael@0 126 * Tests that a title was set properly in the database.
michael@0 127 *
michael@0 128 * @param aURI
michael@0 129 * The uri to check.
michael@0 130 * @param aTitle
michael@0 131 * The expected title in the database.
michael@0 132 */
michael@0 133 function do_check_title_for_uri(aURI,
michael@0 134 aTitle)
michael@0 135 {
michael@0 136 let stack = Components.stack.caller;
michael@0 137 let stmt = DBConn().createStatement(
michael@0 138 "SELECT title " +
michael@0 139 "FROM moz_places " +
michael@0 140 "WHERE url = :url "
michael@0 141 );
michael@0 142 stmt.params.url = aURI.spec;
michael@0 143 do_check_true(stmt.executeStep(), stack);
michael@0 144 do_check_eq(stmt.row.title, aTitle, stack);
michael@0 145 stmt.finalize();
michael@0 146 }
michael@0 147
michael@0 148 ////////////////////////////////////////////////////////////////////////////////
michael@0 149 //// Test Functions
michael@0 150
michael@0 151 function test_interface_exists()
michael@0 152 {
michael@0 153 let history = Cc["@mozilla.org/browser/history;1"].getService(Ci.nsISupports);
michael@0 154 do_check_true(history instanceof Ci.mozIAsyncHistory);
michael@0 155 }
michael@0 156
michael@0 157 function test_invalid_uri_throws()
michael@0 158 {
michael@0 159 // First, test passing in nothing.
michael@0 160 let place = {
michael@0 161 visits: [
michael@0 162 new VisitInfo(),
michael@0 163 ],
michael@0 164 };
michael@0 165 try {
michael@0 166 yield promiseUpdatePlaces(place);
michael@0 167 do_throw("Should have thrown!");
michael@0 168 }
michael@0 169 catch (e) {
michael@0 170 do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
michael@0 171 }
michael@0 172
michael@0 173 // Now, test other bogus things.
michael@0 174 const TEST_VALUES = [
michael@0 175 null,
michael@0 176 undefined,
michael@0 177 {},
michael@0 178 [],
michael@0 179 TEST_DOMAIN + "test_invalid_id_throws",
michael@0 180 ];
michael@0 181 for (let i = 0; i < TEST_VALUES.length; i++) {
michael@0 182 place.uri = TEST_VALUES[i];
michael@0 183 try {
michael@0 184 yield promiseUpdatePlaces(place);
michael@0 185 do_throw("Should have thrown!");
michael@0 186 }
michael@0 187 catch (e) {
michael@0 188 do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
michael@0 189 }
michael@0 190 }
michael@0 191 }
michael@0 192
michael@0 193 function test_invalid_places_throws()
michael@0 194 {
michael@0 195 // First, test passing in nothing.
michael@0 196 try {
michael@0 197 PlacesUtils.asyncHistory.updatePlaces();
michael@0 198 do_throw("Should have thrown!");
michael@0 199 }
michael@0 200 catch (e) {
michael@0 201 do_check_eq(e.result, Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS);
michael@0 202 }
michael@0 203
michael@0 204 // Now, test other bogus things.
michael@0 205 const TEST_VALUES = [
michael@0 206 null,
michael@0 207 undefined,
michael@0 208 {},
michael@0 209 [],
michael@0 210 "",
michael@0 211 ];
michael@0 212 for (let i = 0; i < TEST_VALUES.length; i++) {
michael@0 213 let value = TEST_VALUES[i];
michael@0 214 try {
michael@0 215 yield promiseUpdatePlaces(value);
michael@0 216 do_throw("Should have thrown!");
michael@0 217 }
michael@0 218 catch (e) {
michael@0 219 do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
michael@0 220 }
michael@0 221 }
michael@0 222 }
michael@0 223
michael@0 224 function test_invalid_guid_throws()
michael@0 225 {
michael@0 226 // First check invalid length guid.
michael@0 227 let place = {
michael@0 228 guid: "BAD_GUID",
michael@0 229 uri: NetUtil.newURI(TEST_DOMAIN + "test_invalid_guid_throws"),
michael@0 230 visits: [
michael@0 231 new VisitInfo(),
michael@0 232 ],
michael@0 233 };
michael@0 234 try {
michael@0 235 yield promiseUpdatePlaces(place);
michael@0 236 do_throw("Should have thrown!");
michael@0 237 }
michael@0 238 catch (e) {
michael@0 239 do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
michael@0 240 }
michael@0 241
michael@0 242 // Now check invalid character guid.
michael@0 243 place.guid = "__BADGUID+__";
michael@0 244 do_check_eq(place.guid.length, 12);
michael@0 245 try {
michael@0 246 yield promiseUpdatePlaces(place);
michael@0 247 do_throw("Should have thrown!");
michael@0 248 }
michael@0 249 catch (e) {
michael@0 250 do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
michael@0 251 }
michael@0 252 }
michael@0 253
michael@0 254 function test_no_visits_throws()
michael@0 255 {
michael@0 256 const TEST_URI =
michael@0 257 NetUtil.newURI(TEST_DOMAIN + "test_no_id_or_guid_no_visits_throws");
michael@0 258 const TEST_GUID = "_RANDOMGUID_";
michael@0 259 const TEST_PLACEID = 2;
michael@0 260
michael@0 261 let log_test_conditions = function(aPlace) {
michael@0 262 let str = "Testing place with " +
michael@0 263 (aPlace.uri ? "uri" : "no uri") + ", " +
michael@0 264 (aPlace.guid ? "guid" : "no guid") + ", " +
michael@0 265 (aPlace.visits ? "visits array" : "no visits array");
michael@0 266 do_log_info(str);
michael@0 267 };
michael@0 268
michael@0 269 // Loop through every possible case. Note that we don't actually care about
michael@0 270 // the case where we have no uri, place id, or guid (covered by another test),
michael@0 271 // but it is easier to just make sure it too throws than to exclude it.
michael@0 272 let place = { };
michael@0 273 for (let uri = 1; uri >= 0; uri--) {
michael@0 274 place.uri = uri ? TEST_URI : undefined;
michael@0 275
michael@0 276 for (let guid = 1; guid >= 0; guid--) {
michael@0 277 place.guid = guid ? TEST_GUID : undefined;
michael@0 278
michael@0 279 for (let visits = 1; visits >= 0; visits--) {
michael@0 280 place.visits = visits ? [] : undefined;
michael@0 281
michael@0 282 log_test_conditions(place);
michael@0 283 try {
michael@0 284 yield promiseUpdatePlaces(place);
michael@0 285 do_throw("Should have thrown!");
michael@0 286 }
michael@0 287 catch (e) {
michael@0 288 do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
michael@0 289 }
michael@0 290 }
michael@0 291 }
michael@0 292 }
michael@0 293 }
michael@0 294
michael@0 295 function test_add_visit_no_date_throws()
michael@0 296 {
michael@0 297 let place = {
michael@0 298 uri: NetUtil.newURI(TEST_DOMAIN + "test_add_visit_no_date_throws"),
michael@0 299 visits: [
michael@0 300 new VisitInfo(),
michael@0 301 ],
michael@0 302 };
michael@0 303 delete place.visits[0].visitDate;
michael@0 304 try {
michael@0 305 yield promiseUpdatePlaces(place);
michael@0 306 do_throw("Should have thrown!");
michael@0 307 }
michael@0 308 catch (e) {
michael@0 309 do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
michael@0 310 }
michael@0 311 }
michael@0 312
michael@0 313 function test_add_visit_no_transitionType_throws()
michael@0 314 {
michael@0 315 let place = {
michael@0 316 uri: NetUtil.newURI(TEST_DOMAIN + "test_add_visit_no_transitionType_throws"),
michael@0 317 visits: [
michael@0 318 new VisitInfo(),
michael@0 319 ],
michael@0 320 };
michael@0 321 delete place.visits[0].transitionType;
michael@0 322 try {
michael@0 323 yield promiseUpdatePlaces(place);
michael@0 324 do_throw("Should have thrown!");
michael@0 325 }
michael@0 326 catch (e) {
michael@0 327 do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
michael@0 328 }
michael@0 329 }
michael@0 330
michael@0 331 function test_add_visit_invalid_transitionType_throws()
michael@0 332 {
michael@0 333 // First, test something that has a transition type lower than the first one.
michael@0 334 let place = {
michael@0 335 uri: NetUtil.newURI(TEST_DOMAIN +
michael@0 336 "test_add_visit_invalid_transitionType_throws"),
michael@0 337 visits: [
michael@0 338 new VisitInfo(TRANSITION_LINK - 1),
michael@0 339 ],
michael@0 340 };
michael@0 341 try {
michael@0 342 yield promiseUpdatePlaces(place);
michael@0 343 do_throw("Should have thrown!");
michael@0 344 }
michael@0 345 catch (e) {
michael@0 346 do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
michael@0 347 }
michael@0 348
michael@0 349 // Now, test something that has a transition type greater than the last one.
michael@0 350 place.visits[0] = new VisitInfo(TRANSITION_FRAMED_LINK + 1);
michael@0 351 try {
michael@0 352 yield promiseUpdatePlaces(place);
michael@0 353 do_throw("Should have thrown!");
michael@0 354 }
michael@0 355 catch (e) {
michael@0 356 do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
michael@0 357 }
michael@0 358 }
michael@0 359
michael@0 360 function test_non_addable_uri_errors()
michael@0 361 {
michael@0 362 // Array of protocols that nsINavHistoryService::canAddURI returns false for.
michael@0 363 const URLS = [
michael@0 364 "about:config",
michael@0 365 "imap://cyrus.andrew.cmu.edu/archive.imap",
michael@0 366 "news://new.mozilla.org/mozilla.dev.apps.firefox",
michael@0 367 "mailbox:Inbox",
michael@0 368 "moz-anno:favicon:http://mozilla.org/made-up-favicon",
michael@0 369 "view-source:http://mozilla.org",
michael@0 370 "chrome://browser/content/browser.xul",
michael@0 371 "resource://gre-resources/hiddenWindow.html",
michael@0 372 "data:,Hello%2C%20World!",
michael@0 373 "wyciwyg:/0/http://mozilla.org",
michael@0 374 "javascript:alert('hello wolrd!');",
michael@0 375 "blob:foo",
michael@0 376 ];
michael@0 377 let places = [];
michael@0 378 URLS.forEach(function(url) {
michael@0 379 try {
michael@0 380 let place = {
michael@0 381 uri: NetUtil.newURI(url),
michael@0 382 title: "test for " + url,
michael@0 383 visits: [
michael@0 384 new VisitInfo(),
michael@0 385 ],
michael@0 386 };
michael@0 387 places.push(place);
michael@0 388 }
michael@0 389 catch (e if e.result === Cr.NS_ERROR_FAILURE) {
michael@0 390 // NetUtil.newURI() can throw if e.g. our app knows about imap://
michael@0 391 // but the account is not set up and so the URL is invalid for us.
michael@0 392 // Note this in the log but ignore as it's not the subject of this test.
michael@0 393 do_log_info("Could not construct URI for '" + url + "'; ignoring");
michael@0 394 }
michael@0 395 });
michael@0 396
michael@0 397 let placesResult = yield promiseUpdatePlaces(places);
michael@0 398 if (placesResult.results.length > 0) {
michael@0 399 do_throw("Unexpected success.");
michael@0 400 }
michael@0 401 for (let place of placesResult.errors) {
michael@0 402 do_log_info("Checking '" + place.info.uri.spec + "'");
michael@0 403 do_check_eq(place.resultCode, Cr.NS_ERROR_INVALID_ARG);
michael@0 404 do_check_false(yield promiseIsURIVisited(place.info.uri));
michael@0 405 }
michael@0 406 yield promiseAsyncUpdates();
michael@0 407 }
michael@0 408
michael@0 409 function test_duplicate_guid_errors()
michael@0 410 {
michael@0 411 // This test ensures that trying to add a visit, with a guid already found in
michael@0 412 // another visit, fails.
michael@0 413 let place = {
michael@0 414 uri: NetUtil.newURI(TEST_DOMAIN + "test_duplicate_guid_fails_first"),
michael@0 415 visits: [
michael@0 416 new VisitInfo(),
michael@0 417 ],
michael@0 418 };
michael@0 419
michael@0 420 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 421 let placesResult = yield promiseUpdatePlaces(place);
michael@0 422 if (placesResult.errors.length > 0) {
michael@0 423 do_throw("Unexpected error.");
michael@0 424 }
michael@0 425 let placeInfo = placesResult.results[0];
michael@0 426 do_check_true(yield promiseIsURIVisited(placeInfo.uri));
michael@0 427
michael@0 428 let badPlace = {
michael@0 429 uri: NetUtil.newURI(TEST_DOMAIN + "test_duplicate_guid_fails_second"),
michael@0 430 visits: [
michael@0 431 new VisitInfo(),
michael@0 432 ],
michael@0 433 guid: placeInfo.guid,
michael@0 434 };
michael@0 435
michael@0 436 do_check_false(yield promiseIsURIVisited(badPlace.uri));
michael@0 437 placesResult = yield promiseUpdatePlaces(badPlace);
michael@0 438 if (placesResult.results.length > 0) {
michael@0 439 do_throw("Unexpected success.");
michael@0 440 }
michael@0 441 let badPlaceInfo = placesResult.errors[0];
michael@0 442 do_check_eq(badPlaceInfo.resultCode, Cr.NS_ERROR_STORAGE_CONSTRAINT);
michael@0 443 do_check_false(yield promiseIsURIVisited(badPlaceInfo.info.uri));
michael@0 444
michael@0 445 yield promiseAsyncUpdates();
michael@0 446 }
michael@0 447
michael@0 448 function test_invalid_referrerURI_ignored()
michael@0 449 {
michael@0 450 let place = {
michael@0 451 uri: NetUtil.newURI(TEST_DOMAIN +
michael@0 452 "test_invalid_referrerURI_ignored"),
michael@0 453 visits: [
michael@0 454 new VisitInfo(),
michael@0 455 ],
michael@0 456 };
michael@0 457 place.visits[0].referrerURI = NetUtil.newURI(place.uri.spec + "_unvisistedURI");
michael@0 458 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 459 do_check_false(yield promiseIsURIVisited(place.visits[0].referrerURI));
michael@0 460
michael@0 461 let placesResult = yield promiseUpdatePlaces(place);
michael@0 462 if (placesResult.errors.length > 0) {
michael@0 463 do_throw("Unexpected error.");
michael@0 464 }
michael@0 465 let placeInfo = placesResult.results[0];
michael@0 466 do_check_true(yield promiseIsURIVisited(placeInfo.uri));
michael@0 467
michael@0 468 // Check to make sure we do not visit the invalid referrer.
michael@0 469 do_check_false(yield promiseIsURIVisited(place.visits[0].referrerURI));
michael@0 470
michael@0 471 // Check to make sure from_visit is zero in database.
michael@0 472 let stmt = DBConn().createStatement(
michael@0 473 "SELECT from_visit " +
michael@0 474 "FROM moz_historyvisits " +
michael@0 475 "WHERE id = :visit_id"
michael@0 476 );
michael@0 477 stmt.params.visit_id = placeInfo.visits[0].visitId;
michael@0 478 do_check_true(stmt.executeStep());
michael@0 479 do_check_eq(stmt.row.from_visit, 0);
michael@0 480 stmt.finalize();
michael@0 481
michael@0 482 yield promiseAsyncUpdates();
michael@0 483 }
michael@0 484
michael@0 485 function test_nonnsIURI_referrerURI_ignored()
michael@0 486 {
michael@0 487 let place = {
michael@0 488 uri: NetUtil.newURI(TEST_DOMAIN +
michael@0 489 "test_nonnsIURI_referrerURI_ignored"),
michael@0 490 visits: [
michael@0 491 new VisitInfo(),
michael@0 492 ],
michael@0 493 };
michael@0 494 place.visits[0].referrerURI = place.uri.spec + "_nonnsIURI";
michael@0 495 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 496
michael@0 497 let placesResult = yield promiseUpdatePlaces(place);
michael@0 498 if (placesResult.errors.length > 0) {
michael@0 499 do_throw("Unexpected error.");
michael@0 500 }
michael@0 501 let placeInfo = placesResult.results[0];
michael@0 502 do_check_true(yield promiseIsURIVisited(placeInfo.uri));
michael@0 503
michael@0 504 // Check to make sure from_visit is zero in database.
michael@0 505 let stmt = DBConn().createStatement(
michael@0 506 "SELECT from_visit " +
michael@0 507 "FROM moz_historyvisits " +
michael@0 508 "WHERE id = :visit_id"
michael@0 509 );
michael@0 510 stmt.params.visit_id = placeInfo.visits[0].visitId;
michael@0 511 do_check_true(stmt.executeStep());
michael@0 512 do_check_eq(stmt.row.from_visit, 0);
michael@0 513 stmt.finalize();
michael@0 514
michael@0 515 yield promiseAsyncUpdates();
michael@0 516 }
michael@0 517
michael@0 518 function test_old_referrer_ignored()
michael@0 519 {
michael@0 520 // This tests that a referrer for a visit which is not recent (specifically,
michael@0 521 // older than 15 minutes as per RECENT_EVENT_THRESHOLD) is not saved by
michael@0 522 // updatePlaces.
michael@0 523 let oldTime = (Date.now() * 1000) - (RECENT_EVENT_THRESHOLD + 1);
michael@0 524 let referrerPlace = {
michael@0 525 uri: NetUtil.newURI(TEST_DOMAIN + "test_old_referrer_ignored_referrer"),
michael@0 526 visits: [
michael@0 527 new VisitInfo(TRANSITION_LINK, oldTime),
michael@0 528 ],
michael@0 529 };
michael@0 530
michael@0 531 // First we must add our referrer to the history so that it is not ignored
michael@0 532 // as being invalid.
michael@0 533 do_check_false(yield promiseIsURIVisited(referrerPlace.uri));
michael@0 534 let placesResult = yield promiseUpdatePlaces(referrerPlace);
michael@0 535 if (placesResult.errors.length > 0) {
michael@0 536 do_throw("Unexpected error.");
michael@0 537 }
michael@0 538
michael@0 539 // Now that the referrer is added, we can add a page with a valid
michael@0 540 // referrer to determine if the recency of the referrer is taken into
michael@0 541 // account.
michael@0 542 do_check_true(yield promiseIsURIVisited(referrerPlace.uri));
michael@0 543
michael@0 544 let visitInfo = new VisitInfo();
michael@0 545 visitInfo.referrerURI = referrerPlace.uri;
michael@0 546 let place = {
michael@0 547 uri: NetUtil.newURI(TEST_DOMAIN + "test_old_referrer_ignored_page"),
michael@0 548 visits: [
michael@0 549 visitInfo,
michael@0 550 ],
michael@0 551 };
michael@0 552
michael@0 553 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 554 placesResult = yield promiseUpdatePlaces(place);
michael@0 555 if (placesResult.errors.length > 0) {
michael@0 556 do_throw("Unexpected error.");
michael@0 557 }
michael@0 558 let placeInfo = placesResult.results[0];
michael@0 559 do_check_true(yield promiseIsURIVisited(place.uri));
michael@0 560
michael@0 561 // Though the visit will not contain the referrer, we must examine the
michael@0 562 // database to be sure.
michael@0 563 do_check_eq(placeInfo.visits[0].referrerURI, null);
michael@0 564 let stmt = DBConn().createStatement(
michael@0 565 "SELECT COUNT(1) AS count " +
michael@0 566 "FROM moz_historyvisits " +
michael@0 567 "WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) " +
michael@0 568 "AND from_visit = 0 "
michael@0 569 );
michael@0 570 stmt.params.page_url = place.uri.spec;
michael@0 571 do_check_true(stmt.executeStep());
michael@0 572 do_check_eq(stmt.row.count, 1);
michael@0 573 stmt.finalize();
michael@0 574
michael@0 575 yield promiseAsyncUpdates();
michael@0 576 }
michael@0 577
michael@0 578 function test_place_id_ignored()
michael@0 579 {
michael@0 580 let place = {
michael@0 581 uri: NetUtil.newURI(TEST_DOMAIN + "test_place_id_ignored_first"),
michael@0 582 visits: [
michael@0 583 new VisitInfo(),
michael@0 584 ],
michael@0 585 };
michael@0 586
michael@0 587 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 588 let placesResult = yield promiseUpdatePlaces(place);
michael@0 589 if (placesResult.errors.length > 0) {
michael@0 590 do_throw("Unexpected error.");
michael@0 591 }
michael@0 592 let placeInfo = placesResult.results[0];
michael@0 593 do_check_true(yield promiseIsURIVisited(place.uri));
michael@0 594
michael@0 595 let placeId = placeInfo.placeId;
michael@0 596 do_check_neq(placeId, 0);
michael@0 597
michael@0 598 let badPlace = {
michael@0 599 uri: NetUtil.newURI(TEST_DOMAIN + "test_place_id_ignored_second"),
michael@0 600 visits: [
michael@0 601 new VisitInfo(),
michael@0 602 ],
michael@0 603 placeId: placeId,
michael@0 604 };
michael@0 605
michael@0 606 do_check_false(yield promiseIsURIVisited(badPlace.uri));
michael@0 607 placesResult = yield promiseUpdatePlaces(badPlace);
michael@0 608 if (placesResult.errors.length > 0) {
michael@0 609 do_throw("Unexpected error.");
michael@0 610 }
michael@0 611 placeInfo = placesResult.results[0];
michael@0 612
michael@0 613 do_check_neq(placeInfo.placeId, placeId);
michael@0 614 do_check_true(yield promiseIsURIVisited(badPlace.uri));
michael@0 615
michael@0 616 yield promiseAsyncUpdates();
michael@0 617 }
michael@0 618
michael@0 619 function test_handleCompletion_called_when_complete()
michael@0 620 {
michael@0 621 // We test a normal visit, and embeded visit, and a uri that would fail
michael@0 622 // the canAddURI test to make sure that the notification happens after *all*
michael@0 623 // of them have had a callback.
michael@0 624 let places = [
michael@0 625 { uri: NetUtil.newURI(TEST_DOMAIN +
michael@0 626 "test_handleCompletion_called_when_complete"),
michael@0 627 visits: [
michael@0 628 new VisitInfo(),
michael@0 629 new VisitInfo(TRANSITION_EMBED),
michael@0 630 ],
michael@0 631 },
michael@0 632 { uri: NetUtil.newURI("data:,Hello%2C%20World!"),
michael@0 633 visits: [
michael@0 634 new VisitInfo(),
michael@0 635 ],
michael@0 636 },
michael@0 637 ];
michael@0 638 do_check_false(yield promiseIsURIVisited(places[0].uri));
michael@0 639 do_check_false(yield promiseIsURIVisited(places[1].uri));
michael@0 640
michael@0 641 const EXPECTED_COUNT_SUCCESS = 2;
michael@0 642 const EXPECTED_COUNT_FAILURE = 1;
michael@0 643 let callbackCountSuccess = 0;
michael@0 644 let callbackCountFailure = 0;
michael@0 645
michael@0 646 let placesResult = yield promiseUpdatePlaces(places);
michael@0 647 for (let place of placesResult.results) {
michael@0 648 let checker = PlacesUtils.history.canAddURI(place.uri) ?
michael@0 649 do_check_true : do_check_false;
michael@0 650 callbackCountSuccess++;
michael@0 651 }
michael@0 652 for (let error of placesResult.errors) {
michael@0 653 callbackCountFailure++;
michael@0 654 }
michael@0 655
michael@0 656 do_check_eq(callbackCountSuccess, EXPECTED_COUNT_SUCCESS);
michael@0 657 do_check_eq(callbackCountFailure, EXPECTED_COUNT_FAILURE);
michael@0 658 yield promiseAsyncUpdates();
michael@0 659 }
michael@0 660
michael@0 661 function test_add_visit()
michael@0 662 {
michael@0 663 const VISIT_TIME = Date.now() * 1000;
michael@0 664 let place = {
michael@0 665 uri: NetUtil.newURI(TEST_DOMAIN + "test_add_visit"),
michael@0 666 title: "test_add_visit title",
michael@0 667 visits: [],
michael@0 668 };
michael@0 669 for (let transitionType = TRANSITION_LINK;
michael@0 670 transitionType <= TRANSITION_FRAMED_LINK;
michael@0 671 transitionType++) {
michael@0 672 place.visits.push(new VisitInfo(transitionType, VISIT_TIME));
michael@0 673 }
michael@0 674 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 675
michael@0 676 let callbackCount = 0;
michael@0 677 let placesResult = yield promiseUpdatePlaces(place);
michael@0 678 if (placesResult.errors.length > 0) {
michael@0 679 do_throw("Unexpected error.");
michael@0 680 }
michael@0 681 for (let placeInfo of placesResult.results) {
michael@0 682 do_check_true(yield promiseIsURIVisited(place.uri));
michael@0 683
michael@0 684 // Check mozIPlaceInfo properties.
michael@0 685 do_check_true(place.uri.equals(placeInfo.uri));
michael@0 686 do_check_eq(placeInfo.frecency, -1); // We don't pass frecency here!
michael@0 687 do_check_eq(placeInfo.title, place.title);
michael@0 688
michael@0 689 // Check mozIVisitInfo properties.
michael@0 690 let visits = placeInfo.visits;
michael@0 691 do_check_eq(visits.length, 1);
michael@0 692 let visit = visits[0];
michael@0 693 do_check_eq(visit.visitDate, VISIT_TIME);
michael@0 694 do_check_true(visit.transitionType >= TRANSITION_LINK &&
michael@0 695 visit.transitionType <= TRANSITION_FRAMED_LINK);
michael@0 696 do_check_true(visit.referrerURI === null);
michael@0 697
michael@0 698 // For TRANSITION_EMBED visits, many properties will always be zero or
michael@0 699 // undefined.
michael@0 700 if (visit.transitionType == TRANSITION_EMBED) {
michael@0 701 // Check mozIPlaceInfo properties.
michael@0 702 do_check_eq(placeInfo.placeId, 0, '//');
michael@0 703 do_check_eq(placeInfo.guid, null);
michael@0 704
michael@0 705 // Check mozIVisitInfo properties.
michael@0 706 do_check_eq(visit.visitId, 0);
michael@0 707 }
michael@0 708 // But they should be valid for non-embed visits.
michael@0 709 else {
michael@0 710 // Check mozIPlaceInfo properties.
michael@0 711 do_check_true(placeInfo.placeId > 0);
michael@0 712 do_check_valid_places_guid(placeInfo.guid);
michael@0 713
michael@0 714 // Check mozIVisitInfo properties.
michael@0 715 do_check_true(visit.visitId > 0);
michael@0 716 }
michael@0 717
michael@0 718 // If we have had all of our callbacks, continue running tests.
michael@0 719 if (++callbackCount == place.visits.length) {
michael@0 720 yield promiseAsyncUpdates();
michael@0 721 }
michael@0 722 }
michael@0 723 }
michael@0 724
michael@0 725 function test_properties_saved()
michael@0 726 {
michael@0 727 // Check each transition type to make sure it is saved properly.
michael@0 728 let places = [];
michael@0 729 for (let transitionType = TRANSITION_LINK;
michael@0 730 transitionType <= TRANSITION_FRAMED_LINK;
michael@0 731 transitionType++) {
michael@0 732 let place = {
michael@0 733 uri: NetUtil.newURI(TEST_DOMAIN + "test_properties_saved/" +
michael@0 734 transitionType),
michael@0 735 title: "test_properties_saved test",
michael@0 736 visits: [
michael@0 737 new VisitInfo(transitionType),
michael@0 738 ],
michael@0 739 };
michael@0 740 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 741 places.push(place);
michael@0 742 }
michael@0 743
michael@0 744 let callbackCount = 0;
michael@0 745 let placesResult = yield promiseUpdatePlaces(places);
michael@0 746 if (placesResult.errors.length > 0) {
michael@0 747 do_throw("Unexpected error.");
michael@0 748 }
michael@0 749 for (let placeInfo of placesResult.results) {
michael@0 750 let uri = placeInfo.uri;
michael@0 751 do_check_true(yield promiseIsURIVisited(uri));
michael@0 752 let visit = placeInfo.visits[0];
michael@0 753 print("TEST-INFO | test_properties_saved | updatePlaces callback for " +
michael@0 754 "transition type " + visit.transitionType);
michael@0 755
michael@0 756 // Note that TRANSITION_EMBED should not be in the database.
michael@0 757 const EXPECTED_COUNT = visit.transitionType == TRANSITION_EMBED ? 0 : 1;
michael@0 758
michael@0 759 // mozIVisitInfo::date
michael@0 760 let stmt = DBConn().createStatement(
michael@0 761 "SELECT COUNT(1) AS count " +
michael@0 762 "FROM moz_places h " +
michael@0 763 "JOIN moz_historyvisits v " +
michael@0 764 "ON h.id = v.place_id " +
michael@0 765 "WHERE h.url = :page_url " +
michael@0 766 "AND v.visit_date = :visit_date "
michael@0 767 );
michael@0 768 stmt.params.page_url = uri.spec;
michael@0 769 stmt.params.visit_date = visit.visitDate;
michael@0 770 do_check_true(stmt.executeStep());
michael@0 771 do_check_eq(stmt.row.count, EXPECTED_COUNT);
michael@0 772 stmt.finalize();
michael@0 773
michael@0 774 // mozIVisitInfo::transitionType
michael@0 775 stmt = DBConn().createStatement(
michael@0 776 "SELECT COUNT(1) AS count " +
michael@0 777 "FROM moz_places h " +
michael@0 778 "JOIN moz_historyvisits v " +
michael@0 779 "ON h.id = v.place_id " +
michael@0 780 "WHERE h.url = :page_url " +
michael@0 781 "AND v.visit_type = :transition_type "
michael@0 782 );
michael@0 783 stmt.params.page_url = uri.spec;
michael@0 784 stmt.params.transition_type = visit.transitionType;
michael@0 785 do_check_true(stmt.executeStep());
michael@0 786 do_check_eq(stmt.row.count, EXPECTED_COUNT);
michael@0 787 stmt.finalize();
michael@0 788
michael@0 789 // mozIPlaceInfo::title
michael@0 790 stmt = DBConn().createStatement(
michael@0 791 "SELECT COUNT(1) AS count " +
michael@0 792 "FROM moz_places h " +
michael@0 793 "WHERE h.url = :page_url " +
michael@0 794 "AND h.title = :title "
michael@0 795 );
michael@0 796 stmt.params.page_url = uri.spec;
michael@0 797 stmt.params.title = placeInfo.title;
michael@0 798 do_check_true(stmt.executeStep());
michael@0 799 do_check_eq(stmt.row.count, EXPECTED_COUNT);
michael@0 800 stmt.finalize();
michael@0 801
michael@0 802 // If we have had all of our callbacks, continue running tests.
michael@0 803 if (++callbackCount == places.length) {
michael@0 804 yield promiseAsyncUpdates();
michael@0 805 }
michael@0 806 }
michael@0 807 }
michael@0 808
michael@0 809 function test_guid_saved()
michael@0 810 {
michael@0 811 let place = {
michael@0 812 uri: NetUtil.newURI(TEST_DOMAIN + "test_guid_saved"),
michael@0 813 guid: "__TESTGUID__",
michael@0 814 visits: [
michael@0 815 new VisitInfo(),
michael@0 816 ],
michael@0 817 };
michael@0 818 do_check_valid_places_guid(place.guid);
michael@0 819 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 820
michael@0 821 let placesResult = yield promiseUpdatePlaces(place);
michael@0 822 if (placesResult.errors.length > 0) {
michael@0 823 do_throw("Unexpected error.");
michael@0 824 }
michael@0 825 let placeInfo = placesResult.results[0];
michael@0 826 let uri = placeInfo.uri;
michael@0 827 do_check_true(yield promiseIsURIVisited(uri));
michael@0 828 do_check_eq(placeInfo.guid, place.guid);
michael@0 829 do_check_guid_for_uri(uri, place.guid);
michael@0 830 yield promiseAsyncUpdates();
michael@0 831 }
michael@0 832
michael@0 833 function test_referrer_saved()
michael@0 834 {
michael@0 835 let places = [
michael@0 836 { uri: NetUtil.newURI(TEST_DOMAIN + "test_referrer_saved/referrer"),
michael@0 837 visits: [
michael@0 838 new VisitInfo(),
michael@0 839 ],
michael@0 840 },
michael@0 841 { uri: NetUtil.newURI(TEST_DOMAIN + "test_referrer_saved/test"),
michael@0 842 visits: [
michael@0 843 new VisitInfo(),
michael@0 844 ],
michael@0 845 },
michael@0 846 ];
michael@0 847 places[1].visits[0].referrerURI = places[0].uri;
michael@0 848 do_check_false(yield promiseIsURIVisited(places[0].uri));
michael@0 849 do_check_false(yield promiseIsURIVisited(places[1].uri));
michael@0 850
michael@0 851 let resultCount = 0;
michael@0 852 let placesResult = yield promiseUpdatePlaces(places);
michael@0 853 if (placesResult.errors.length > 0) {
michael@0 854 do_throw("Unexpected error.");
michael@0 855 }
michael@0 856 for (let placeInfo of placesResult.results) {
michael@0 857 let uri = placeInfo.uri;
michael@0 858 do_check_true(yield promiseIsURIVisited(uri));
michael@0 859 let visit = placeInfo.visits[0];
michael@0 860
michael@0 861 // We need to insert all of our visits before we can test conditions.
michael@0 862 if (++resultCount == places.length) {
michael@0 863 do_check_true(places[0].uri.equals(visit.referrerURI));
michael@0 864
michael@0 865 let stmt = DBConn().createStatement(
michael@0 866 "SELECT COUNT(1) AS count " +
michael@0 867 "FROM moz_historyvisits " +
michael@0 868 "WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) " +
michael@0 869 "AND from_visit = ( " +
michael@0 870 "SELECT id " +
michael@0 871 "FROM moz_historyvisits " +
michael@0 872 "WHERE place_id = (SELECT id FROM moz_places WHERE url = :referrer) " +
michael@0 873 ") "
michael@0 874 );
michael@0 875 stmt.params.page_url = uri.spec;
michael@0 876 stmt.params.referrer = visit.referrerURI.spec;
michael@0 877 do_check_true(stmt.executeStep());
michael@0 878 do_check_eq(stmt.row.count, 1);
michael@0 879 stmt.finalize();
michael@0 880
michael@0 881 yield promiseAsyncUpdates();
michael@0 882 }
michael@0 883 }
michael@0 884 }
michael@0 885
michael@0 886 function test_guid_change_saved()
michael@0 887 {
michael@0 888 // First, add a visit for it.
michael@0 889 let place = {
michael@0 890 uri: NetUtil.newURI(TEST_DOMAIN + "test_guid_change_saved"),
michael@0 891 visits: [
michael@0 892 new VisitInfo(),
michael@0 893 ],
michael@0 894 };
michael@0 895 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 896
michael@0 897 let placesResult = yield promiseUpdatePlaces(place);
michael@0 898 if (placesResult.errors.length > 0) {
michael@0 899 do_throw("Unexpected error.");
michael@0 900 }
michael@0 901 // Then, change the guid with visits.
michael@0 902 place.guid = "_GUIDCHANGE_";
michael@0 903 place.visits = [new VisitInfo()];
michael@0 904 placesResult = yield promiseUpdatePlaces(place);
michael@0 905 if (placesResult.errors.length > 0) {
michael@0 906 do_throw("Unexpected error.");
michael@0 907 }
michael@0 908 do_check_guid_for_uri(place.uri, place.guid);
michael@0 909
michael@0 910 yield promiseAsyncUpdates();
michael@0 911 }
michael@0 912
michael@0 913 function test_title_change_saved()
michael@0 914 {
michael@0 915 // First, add a visit for it.
michael@0 916 let place = {
michael@0 917 uri: NetUtil.newURI(TEST_DOMAIN + "test_title_change_saved"),
michael@0 918 title: "original title",
michael@0 919 visits: [
michael@0 920 new VisitInfo(),
michael@0 921 ],
michael@0 922 };
michael@0 923 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 924
michael@0 925 let placesResult = yield promiseUpdatePlaces(place);
michael@0 926 if (placesResult.errors.length > 0) {
michael@0 927 do_throw("Unexpected error.");
michael@0 928 }
michael@0 929
michael@0 930 // Now, make sure the empty string clears the title.
michael@0 931 place.title = "";
michael@0 932 place.visits = [new VisitInfo()];
michael@0 933 placesResult = yield promiseUpdatePlaces(place);
michael@0 934 if (placesResult.errors.length > 0) {
michael@0 935 do_throw("Unexpected error.");
michael@0 936 }
michael@0 937 do_check_title_for_uri(place.uri, null);
michael@0 938
michael@0 939 // Then, change the title with visits.
michael@0 940 place.title = "title change";
michael@0 941 place.visits = [new VisitInfo()];
michael@0 942 placesResult = yield promiseUpdatePlaces(place);
michael@0 943 if (placesResult.errors.length > 0) {
michael@0 944 do_throw("Unexpected error.");
michael@0 945 }
michael@0 946 do_check_title_for_uri(place.uri, place.title);
michael@0 947
michael@0 948 // Lastly, check that the title is cleared if we set it to null.
michael@0 949 place.title = null;
michael@0 950 place.visits = [new VisitInfo()];
michael@0 951 placesResult = yield promiseUpdatePlaces(place);
michael@0 952 if (placesResult.errors.length > 0) {
michael@0 953 do_throw("Unexpected error.");
michael@0 954 }
michael@0 955 do_check_title_for_uri(place.uri, place.title);
michael@0 956
michael@0 957 yield promiseAsyncUpdates();
michael@0 958 }
michael@0 959
michael@0 960 function test_no_title_does_not_clear_title()
michael@0 961 {
michael@0 962 const TITLE = "test title";
michael@0 963 // First, add a visit for it.
michael@0 964 let place = {
michael@0 965 uri: NetUtil.newURI(TEST_DOMAIN + "test_no_title_does_not_clear_title"),
michael@0 966 title: TITLE,
michael@0 967 visits: [
michael@0 968 new VisitInfo(),
michael@0 969 ],
michael@0 970 };
michael@0 971 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 972
michael@0 973 let placesResult = yield promiseUpdatePlaces(place);
michael@0 974 if (placesResult.errors.length > 0) {
michael@0 975 do_throw("Unexpected error.");
michael@0 976 }
michael@0 977 // Now, make sure that not specifying a title does not clear it.
michael@0 978 delete place.title;
michael@0 979 place.visits = [new VisitInfo()];
michael@0 980 placesResult = yield promiseUpdatePlaces(place);
michael@0 981 if (placesResult.errors.length > 0) {
michael@0 982 do_throw("Unexpected error.");
michael@0 983 }
michael@0 984 do_check_title_for_uri(place.uri, TITLE);
michael@0 985
michael@0 986 yield promiseAsyncUpdates();
michael@0 987 }
michael@0 988
michael@0 989 function test_title_change_notifies()
michael@0 990 {
michael@0 991 // There are three cases to test. The first case is to make sure we do not
michael@0 992 // get notified if we do not specify a title.
michael@0 993 let place = {
michael@0 994 uri: NetUtil.newURI(TEST_DOMAIN + "test_title_change_notifies"),
michael@0 995 visits: [
michael@0 996 new VisitInfo(),
michael@0 997 ],
michael@0 998 };
michael@0 999 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 1000
michael@0 1001 let silentObserver =
michael@0 1002 new TitleChangedObserver(place.uri, "DO NOT WANT", function() {
michael@0 1003 do_throw("unexpected callback!");
michael@0 1004 });
michael@0 1005
michael@0 1006 PlacesUtils.history.addObserver(silentObserver, false);
michael@0 1007 let placesResult = yield promiseUpdatePlaces(place);
michael@0 1008 if (placesResult.errors.length > 0) {
michael@0 1009 do_throw("Unexpected error.");
michael@0 1010 }
michael@0 1011
michael@0 1012 // The second case to test is that we get the notification when we add
michael@0 1013 // it for the first time. The first case will fail before our callback if it
michael@0 1014 // is busted, so we can do this now.
michael@0 1015 place.uri = NetUtil.newURI(place.uri.spec + "/new-visit-with-title");
michael@0 1016 place.title = "title 1";
michael@0 1017 function promiseTitleChangedObserver(aPlace) {
michael@0 1018 let deferred = Promise.defer();
michael@0 1019 let callbackCount = 0;
michael@0 1020 let observer = new TitleChangedObserver(aPlace.uri, aPlace.title, function() {
michael@0 1021 switch (++callbackCount) {
michael@0 1022 case 1:
michael@0 1023 // The third case to test is to make sure we get a notification when
michael@0 1024 // we change an existing place.
michael@0 1025 observer.expectedTitle = place.title = "title 2";
michael@0 1026 place.visits = [new VisitInfo()];
michael@0 1027 PlacesUtils.asyncHistory.updatePlaces(place);
michael@0 1028 break;
michael@0 1029 case 2:
michael@0 1030 PlacesUtils.history.removeObserver(silentObserver);
michael@0 1031 PlacesUtils.history.removeObserver(observer);
michael@0 1032 deferred.resolve();
michael@0 1033 break;
michael@0 1034 };
michael@0 1035 });
michael@0 1036
michael@0 1037 PlacesUtils.history.addObserver(observer, false);
michael@0 1038 PlacesUtils.asyncHistory.updatePlaces(aPlace);
michael@0 1039 return deferred.promise;
michael@0 1040 }
michael@0 1041
michael@0 1042 yield promiseTitleChangedObserver(place);
michael@0 1043 yield promiseAsyncUpdates();
michael@0 1044 }
michael@0 1045
michael@0 1046 function test_visit_notifies()
michael@0 1047 {
michael@0 1048 // There are two observers we need to see for each visit. One is an
michael@0 1049 // nsINavHistoryObserver and the other is the uri-visit-saved observer topic.
michael@0 1050 let place = {
michael@0 1051 guid: "abcdefghijkl",
michael@0 1052 uri: NetUtil.newURI(TEST_DOMAIN + "test_visit_notifies"),
michael@0 1053 visits: [
michael@0 1054 new VisitInfo(),
michael@0 1055 ],
michael@0 1056 };
michael@0 1057 do_check_false(yield promiseIsURIVisited(place.uri));
michael@0 1058
michael@0 1059 function promiseVisitObserver(aPlace) {
michael@0 1060 let deferred = Promise.defer();
michael@0 1061 let callbackCount = 0;
michael@0 1062 let finisher = function() {
michael@0 1063 if (++callbackCount == 2) {
michael@0 1064 deferred.resolve();
michael@0 1065 }
michael@0 1066 }
michael@0 1067 let visitObserver = new VisitObserver(place.uri, place.guid,
michael@0 1068 function(aVisitDate,
michael@0 1069 aTransitionType) {
michael@0 1070 let visit = place.visits[0];
michael@0 1071 do_check_eq(visit.visitDate, aVisitDate);
michael@0 1072 do_check_eq(visit.transitionType, aTransitionType);
michael@0 1073
michael@0 1074 PlacesUtils.history.removeObserver(visitObserver);
michael@0 1075 finisher();
michael@0 1076 });
michael@0 1077 PlacesUtils.history.addObserver(visitObserver, false);
michael@0 1078 let observer = function(aSubject, aTopic, aData) {
michael@0 1079 do_log_info("observe(" + aSubject + ", " + aTopic + ", " + aData + ")");
michael@0 1080 do_check_true(aSubject instanceof Ci.nsIURI);
michael@0 1081 do_check_true(aSubject.equals(place.uri));
michael@0 1082
michael@0 1083 Services.obs.removeObserver(observer, URI_VISIT_SAVED);
michael@0 1084 finisher();
michael@0 1085 };
michael@0 1086 Services.obs.addObserver(observer, URI_VISIT_SAVED, false);
michael@0 1087 PlacesUtils.asyncHistory.updatePlaces(place);
michael@0 1088 return deferred.promise;
michael@0 1089 }
michael@0 1090
michael@0 1091 yield promiseVisitObserver(place);
michael@0 1092 yield promiseAsyncUpdates();
michael@0 1093 }
michael@0 1094
michael@0 1095 // test with empty mozIVisitInfoCallback object
michael@0 1096 function test_callbacks_not_supplied()
michael@0 1097 {
michael@0 1098 const URLS = [
michael@0 1099 "imap://cyrus.andrew.cmu.edu/archive.imap", // bad URI
michael@0 1100 "http://mozilla.org/" // valid URI
michael@0 1101 ];
michael@0 1102 let places = [];
michael@0 1103 URLS.forEach(function(url) {
michael@0 1104 try {
michael@0 1105 let place = {
michael@0 1106 uri: NetUtil.newURI(url),
michael@0 1107 title: "test for " + url,
michael@0 1108 visits: [
michael@0 1109 new VisitInfo(),
michael@0 1110 ],
michael@0 1111 };
michael@0 1112 places.push(place);
michael@0 1113 }
michael@0 1114 catch (e if e.result === Cr.NS_ERROR_FAILURE) {
michael@0 1115 // NetUtil.newURI() can throw if e.g. our app knows about imap://
michael@0 1116 // but the account is not set up and so the URL is invalid for us.
michael@0 1117 // Note this in the log but ignore as it's not the subject of this test.
michael@0 1118 do_log_info("Could not construct URI for '" + url + "'; ignoring");
michael@0 1119 }
michael@0 1120 });
michael@0 1121
michael@0 1122 PlacesUtils.asyncHistory.updatePlaces(places, {});
michael@0 1123 yield promiseAsyncUpdates();
michael@0 1124 }
michael@0 1125
michael@0 1126 ////////////////////////////////////////////////////////////////////////////////
michael@0 1127 //// Test Runner
michael@0 1128
michael@0 1129 [
michael@0 1130 test_interface_exists,
michael@0 1131 test_invalid_uri_throws,
michael@0 1132 test_invalid_places_throws,
michael@0 1133 test_invalid_guid_throws,
michael@0 1134 test_no_visits_throws,
michael@0 1135 test_add_visit_no_date_throws,
michael@0 1136 test_add_visit_no_transitionType_throws,
michael@0 1137 test_add_visit_invalid_transitionType_throws,
michael@0 1138 // Note: all asynchronous tests (every test below this point) should wait for
michael@0 1139 // async updates before calling run_next_test.
michael@0 1140 test_non_addable_uri_errors,
michael@0 1141 test_duplicate_guid_errors,
michael@0 1142 test_invalid_referrerURI_ignored,
michael@0 1143 test_nonnsIURI_referrerURI_ignored,
michael@0 1144 test_old_referrer_ignored,
michael@0 1145 test_place_id_ignored,
michael@0 1146 test_handleCompletion_called_when_complete,
michael@0 1147 test_add_visit,
michael@0 1148 test_properties_saved,
michael@0 1149 test_guid_saved,
michael@0 1150 test_referrer_saved,
michael@0 1151 test_guid_change_saved,
michael@0 1152 test_title_change_saved,
michael@0 1153 test_no_title_does_not_clear_title,
michael@0 1154 test_title_change_notifies,
michael@0 1155 test_visit_notifies,
michael@0 1156 test_callbacks_not_supplied,
michael@0 1157 ].forEach(add_task);
michael@0 1158
michael@0 1159 function run_test()
michael@0 1160 {
michael@0 1161 run_next_test();
michael@0 1162 }

mercurial