1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/places/tests/unit/test_async_transactions.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1161 @@ 1.4 +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +const bmsvc = PlacesUtils.bookmarks; 1.11 +const tagssvc = PlacesUtils.tagging; 1.12 +const annosvc = PlacesUtils.annotations; 1.13 +const PT = PlacesTransactions; 1.14 + 1.15 +// Create and add bookmarks observer. 1.16 +let observer = { 1.17 + __proto__: NavBookmarkObserver.prototype, 1.18 + 1.19 + tagRelatedGUIDs: new Set(), 1.20 + 1.21 + reset: function () { 1.22 + this.itemsAdded = new Map(); 1.23 + this.itemsRemoved = new Map(); 1.24 + this.itemsChanged = new Map(); 1.25 + this.itemsMoved = new Map(); 1.26 + this.beginUpdateBatch = false; 1.27 + this.endUpdateBatch = false; 1.28 + }, 1.29 + 1.30 + onBeginUpdateBatch: function () { 1.31 + this.beginUpdateBatch = true; 1.32 + }, 1.33 + 1.34 + onEndUpdateBatch: function () { 1.35 + this.endUpdateBatch = true; 1.36 + }, 1.37 + 1.38 + onItemAdded: 1.39 + function (aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded, 1.40 + aGUID, aParentGUID) { 1.41 + // Ignore tag items. 1.42 + if (aParentId == PlacesUtils.tagsFolderId || 1.43 + (aParentId != PlacesUtils.placesRootId && 1.44 + bmsvc.getFolderIdForItem(aParentId) == PlacesUtils.tagsFolderId)) { 1.45 + this.tagRelatedGUIDs.add(aGUID); 1.46 + return; 1.47 + } 1.48 + 1.49 + this.itemsAdded.set(aGUID, { itemId: aItemId 1.50 + , parentGUID: aParentGUID 1.51 + , index: aIndex 1.52 + , itemType: aItemType 1.53 + , title: aTitle 1.54 + , uri: aURI }); 1.55 + }, 1.56 + 1.57 + onItemRemoved: 1.58 + function (aItemId, aParentId, aIndex, aItemType, aURI, aGUID, aParentGUID) { 1.59 + if (this.tagRelatedGUIDs.has(aGUID)) 1.60 + return; 1.61 + 1.62 + this.itemsRemoved.set(aGUID, { parentGUID: aParentGUID 1.63 + , index: aIndex 1.64 + , itemType: aItemType }); 1.65 + }, 1.66 + 1.67 + onItemChanged: 1.68 + function (aItemId, aProperty, aIsAnnoProperty, aNewValue, aLastModified, 1.69 + aItemType, aParentId, aGUID, aParentGUID) { 1.70 + if (this.tagRelatedGUIDs.has(aGUID)) 1.71 + return; 1.72 + 1.73 + let changesForGUID = this.itemsChanged.get(aGUID); 1.74 + if (changesForGUID === undefined) { 1.75 + changesForGUID = new Map(); 1.76 + this.itemsChanged.set(aGUID, changesForGUID); 1.77 + } 1.78 + 1.79 + let newValue = aNewValue; 1.80 + if (aIsAnnoProperty) { 1.81 + if (annosvc.itemHasAnnotation(aItemId, aProperty)) 1.82 + newValue = annosvc.getItemAnnotation(aItemId, aProperty); 1.83 + else 1.84 + newValue = null; 1.85 + } 1.86 + let change = { isAnnoProperty: aIsAnnoProperty 1.87 + , newValue: newValue 1.88 + , lastModified: aLastModified 1.89 + , itemType: aItemType }; 1.90 + changesForGUID.set(aProperty, change); 1.91 + }, 1.92 + 1.93 + onItemVisited: () => {}, 1.94 + 1.95 + onItemMoved: 1.96 + function (aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex, aItemType, 1.97 + aGUID, aOldParentGUID, aNewParentGUID) { 1.98 + this.itemsMoved.set(aGUID, { oldParentGUID: aOldParentGUID 1.99 + , oldIndex: aOldIndex 1.100 + , newParentGUID: aNewParentGUID 1.101 + , newIndex: aNewIndex 1.102 + , itemType: aItemType }); 1.103 + } 1.104 +}; 1.105 +observer.reset(); 1.106 + 1.107 +// index at which items should begin 1.108 +let bmStartIndex = 0; 1.109 + 1.110 +// get bookmarks root id 1.111 +let root = PlacesUtils.bookmarksMenuFolderId; 1.112 + 1.113 +function run_test() { 1.114 + bmsvc.addObserver(observer, false); 1.115 + do_register_cleanup(function () { 1.116 + bmsvc.removeObserver(observer); 1.117 + }); 1.118 + 1.119 + run_next_test(); 1.120 +} 1.121 + 1.122 +function sanityCheckTransactionHistory() { 1.123 + do_check_true(PT.undoPosition <= PT.length); 1.124 + 1.125 + let check_entry_throws = f => { 1.126 + try { 1.127 + f(); 1.128 + do_throw("PT.entry should throw for invalid input"); 1.129 + } catch(ex) {} 1.130 + }; 1.131 + check_entry_throws( () => PT.entry(-1) ); 1.132 + check_entry_throws( () => PT.entry({}) ); 1.133 + check_entry_throws( () => PT.entry(PT.length) ); 1.134 + 1.135 + if (PT.undoPosition < PT.length) 1.136 + do_check_eq(PT.topUndoEntry, PT.entry(PT.undoPosition)); 1.137 + else 1.138 + do_check_null(PT.topUndoEntry); 1.139 + if (PT.undoPosition > 0) 1.140 + do_check_eq(PT.topRedoEntry, PT.entry(PT.undoPosition - 1)); 1.141 + else 1.142 + do_check_null(PT.topRedoEntry); 1.143 +} 1.144 + 1.145 +function getTransactionsHistoryState() { 1.146 + let history = []; 1.147 + for (let i = 0; i < PT.length; i++) { 1.148 + history.push(PT.entry(i)); 1.149 + } 1.150 + return [history, PT.undoPosition]; 1.151 +} 1.152 + 1.153 +function ensureUndoState(aExpectedEntries = [], aExpectedUndoPosition = 0) { 1.154 + // ensureUndoState is called in various places during this test, so it's 1.155 + // a good places to sanity-check the transaction-history APIs in all 1.156 + // cases. 1.157 + sanityCheckTransactionHistory(); 1.158 + 1.159 + let [actualEntries, actualUndoPosition] = getTransactionsHistoryState(); 1.160 + do_check_eq(actualEntries.length, aExpectedEntries.length); 1.161 + do_check_eq(actualUndoPosition, aExpectedUndoPosition); 1.162 + 1.163 + function checkEqualEntries(aExpectedEntry, aActualEntry) { 1.164 + do_check_eq(aExpectedEntry.length, aActualEntry.length); 1.165 + aExpectedEntry.forEach( (t, i) => do_check_eq(t, aActualEntry[i]) ); 1.166 + } 1.167 + aExpectedEntries.forEach( (e, i) => checkEqualEntries(e, actualEntries[i]) ); 1.168 +} 1.169 + 1.170 +function ensureItemsAdded(...items) { 1.171 + do_check_eq(observer.itemsAdded.size, items.length); 1.172 + for (let item of items) { 1.173 + do_check_true(observer.itemsAdded.has(item.GUID)); 1.174 + let info = observer.itemsAdded.get(item.GUID); 1.175 + do_check_eq(info.parentGUID, item.parentGUID); 1.176 + if ("title" in item) 1.177 + do_check_eq(info.title, item.title); 1.178 + if ("index" in item) 1.179 + do_check_eq(info.index, item.index); 1.180 + if ("itemType" in item) 1.181 + do_check_eq(info.itemType, item.itemType); 1.182 + } 1.183 +} 1.184 + 1.185 +function ensureItemsRemoved(...items) { 1.186 + do_check_eq(observer.itemsRemoved.size, items.length); 1.187 + for (let item of items) { 1.188 + do_check_true(observer.itemsRemoved.has(item.GUID)); 1.189 + let info = observer.itemsRemoved.get(item.GUID); 1.190 + do_check_eq(info.parentGUID, item.parentGUID); 1.191 + if ("index" in item) 1.192 + do_check_eq(info.index, item.index); 1.193 + } 1.194 +} 1.195 + 1.196 +function ensureItemsChanged(...items) { 1.197 + for (let item of items) { 1.198 + do_check_true(observer.itemsChanged.has(item.GUID)); 1.199 + let changes = observer.itemsChanged.get(item.GUID); 1.200 + do_check_true(changes.has(item.property)); 1.201 + let info = changes.get(item.property); 1.202 + do_check_eq(info.isAnnoProperty, Boolean(item.isAnnoProperty)); 1.203 + do_check_eq(info.newValue, item.newValue); 1.204 + if ("uri" in item) 1.205 + do_check_true(item.uri.equals(info.uri)); 1.206 + } 1.207 +} 1.208 + 1.209 +function ensureAnnotationsSet(aGUID, aAnnos) { 1.210 + do_check_true(observer.itemsChanged.has(aGUID)); 1.211 + let changes = observer.itemsChanged.get(aGUID); 1.212 + for (let anno of aAnnos) { 1.213 + do_check_true(changes.has(anno.name)); 1.214 + let changeInfo = changes.get(anno.name); 1.215 + do_check_true(changeInfo.isAnnoProperty); 1.216 + do_check_eq(changeInfo.newValue, anno.value); 1.217 + } 1.218 +} 1.219 + 1.220 +function ensureItemsMoved(...items) { 1.221 + do_check_true(observer.itemsMoved.size, items.length); 1.222 + for (let item of items) { 1.223 + do_check_true(observer.itemsMoved.has(item.GUID)); 1.224 + let info = observer.itemsMoved.get(item.GUID); 1.225 + do_check_eq(info.oldParentGUID, item.oldParentGUID); 1.226 + do_check_eq(info.oldIndex, item.oldIndex); 1.227 + do_check_eq(info.newParentGUID, item.newParentGUID); 1.228 + do_check_eq(info.newIndex, item.newIndex); 1.229 + } 1.230 +} 1.231 + 1.232 +function ensureTimestampsUpdated(aGUID, aCheckDateAdded = false) { 1.233 + do_check_true(observer.itemsChanged.has(aGUID)); 1.234 + let changes = observer.itemsChanged.get(aGUID); 1.235 + if (aCheckDateAdded) 1.236 + do_check_true(changes.has("dateAdded")) 1.237 + do_check_true(changes.has("lastModified")); 1.238 +} 1.239 + 1.240 +function ensureTagsForURI(aURI, aTags) { 1.241 + let tagsSet = tagssvc.getTagsForURI(aURI); 1.242 + do_check_eq(tagsSet.length, aTags.length); 1.243 + do_check_true(aTags.every( t => tagsSet.indexOf(t) != -1 )); 1.244 +} 1.245 + 1.246 +function* createTestFolderInfo(aTitle = "Test Folder") { 1.247 + return { parentGUID: yield PlacesUtils.promiseItemGUID(root) 1.248 + , title: "Test Folder" }; 1.249 +} 1.250 + 1.251 +add_task(function* test_invalid_transact_calls() { 1.252 + try { 1.253 + PT.transact({ execute: () => {}, undo: () => {}, redo: () => {}}); 1.254 + do_throw("transact shouldn't accept 'external' transactions"); 1.255 + PT.transact(null); 1.256 + do_throw("transact should throw for invalid arguments"); 1.257 + } 1.258 + catch(ex) { } 1.259 +}); 1.260 + 1.261 +add_task(function* test_recycled_transactions() { 1.262 + function ensureTransactThrowsFor(aTransaction) { 1.263 + let [txns, undoPosition] = getTransactionsHistoryState(); 1.264 + try { 1.265 + yield PT.transact(aTransaction); 1.266 + do_throw("Shouldn't be able to use the same transaction twice"); 1.267 + } 1.268 + catch(ex) { } 1.269 + ensureUndoState(txns, undoPosition); 1.270 + } 1.271 + 1.272 + let txn_a = PT.NewFolder(yield createTestFolderInfo()); 1.273 + ensureTransactThrowsFor(txn_a); 1.274 + yield PT.transact(txn_a); 1.275 + ensureUndoState([[txn_a]], 0); 1.276 + 1.277 + yield PT.undo(); 1.278 + ensureUndoState([[txn_a]], 1); 1.279 + ensureTransactThrowsFor(txn_a); 1.280 + 1.281 + yield PT.clearTransactionsHistory(); 1.282 + ensureUndoState(); 1.283 + ensureTransactThrowsFor(txn_a); 1.284 + 1.285 + let txn_b = PT.NewFolder(yield createTestFolderInfo()); 1.286 + yield PT.transact(function* () { 1.287 + try { 1.288 + yield txn_a; 1.289 + do_throw("Shouldn't be able to use the same transaction twice"); 1.290 + } 1.291 + catch(ex) { } 1.292 + ensureUndoState(); 1.293 + yield txn_b; 1.294 + }); 1.295 + ensureUndoState([[txn_b]], 0); 1.296 + 1.297 + yield PT.undo(); 1.298 + ensureUndoState([[txn_b]], 1); 1.299 + ensureTransactThrowsFor(txn_a); 1.300 + ensureTransactThrowsFor(txn_b); 1.301 + 1.302 + yield PT.clearTransactionsHistory(); 1.303 + ensureUndoState(); 1.304 + observer.reset(); 1.305 +}); 1.306 + 1.307 +add_task(function* test_nested_batches() { 1.308 + let txn_a = PT.NewFolder(yield createTestFolderInfo()), 1.309 + txn_b = PT.NewFolder(yield createTestFolderInfo()); 1.310 + yield PT.transact(function* () { 1.311 + yield txn_a; 1.312 + yield (function*() { 1.313 + yield txn_b; 1.314 + }()); 1.315 + }); 1.316 + ensureUndoState([[txn_b, txn_a]], 0); 1.317 + 1.318 + yield PT.undo(); 1.319 + ensureUndoState([[txn_b, txn_a]], 1); 1.320 + 1.321 + yield PT.clearTransactionsHistory(); 1.322 + ensureUndoState(); 1.323 + observer.reset(); 1.324 +}); 1.325 + 1.326 +add_task(function* test_new_folder_with_annotation() { 1.327 + const ANNO = { name: "TestAnno", value: "TestValue" }; 1.328 + let folder_info = yield createTestFolderInfo(); 1.329 + folder_info.index = bmStartIndex; 1.330 + folder_info.annotations = [ANNO]; 1.331 + ensureUndoState(); 1.332 + let txn = PT.NewFolder(folder_info); 1.333 + folder_info.GUID = yield PT.transact(txn); 1.334 + let ensureDo = function* (aRedo = false) { 1.335 + ensureUndoState([[txn]], 0); 1.336 + yield ensureItemsAdded(folder_info); 1.337 + ensureAnnotationsSet(folder_info.GUID, [ANNO]); 1.338 + if (aRedo) 1.339 + ensureTimestampsUpdated(folder_info.GUID, true); 1.340 + observer.reset(); 1.341 + }; 1.342 + 1.343 + let ensureUndo = () => { 1.344 + ensureUndoState([[txn]], 1); 1.345 + ensureItemsRemoved({ GUID: folder_info.GUID 1.346 + , parentGUID: folder_info.parentGUID 1.347 + , index: bmStartIndex }); 1.348 + observer.reset(); 1.349 + }; 1.350 + 1.351 + yield ensureDo(); 1.352 + yield PT.undo(); 1.353 + yield ensureUndo(); 1.354 + yield PT.redo(); 1.355 + yield ensureDo(true); 1.356 + yield PT.undo(); 1.357 + ensureUndo(); 1.358 + yield PT.clearTransactionsHistory(); 1.359 + ensureUndoState(); 1.360 +}); 1.361 + 1.362 +add_task(function* test_new_bookmark() { 1.363 + let bm_info = { parentGUID: yield PlacesUtils.promiseItemGUID(root) 1.364 + , uri: NetUtil.newURI("http://test_create_item.com") 1.365 + , index: bmStartIndex 1.366 + , title: "Test creating an item" }; 1.367 + 1.368 + ensureUndoState(); 1.369 + let txn = PT.NewBookmark(bm_info); 1.370 + bm_info.GUID = yield PT.transact(txn); 1.371 + 1.372 + let ensureDo = function* (aRedo = false) { 1.373 + ensureUndoState([[txn]], 0); 1.374 + yield ensureItemsAdded(bm_info); 1.375 + if (aRedo) 1.376 + ensureTimestampsUpdated(bm_info.GUID, true); 1.377 + observer.reset(); 1.378 + }; 1.379 + let ensureUndo = () => { 1.380 + ensureUndoState([[txn]], 1); 1.381 + ensureItemsRemoved({ GUID: bm_info.GUID 1.382 + , parentGUID: bm_info.parentGUID 1.383 + , index: bmStartIndex }); 1.384 + observer.reset(); 1.385 + }; 1.386 + 1.387 + yield ensureDo(); 1.388 + yield PT.undo(); 1.389 + ensureUndo(); 1.390 + yield PT.redo(true); 1.391 + yield ensureDo(); 1.392 + yield PT.undo(); 1.393 + ensureUndo(); 1.394 + 1.395 + yield PT.clearTransactionsHistory(); 1.396 + ensureUndoState(); 1.397 +}); 1.398 + 1.399 +add_task(function* test_merge_create_folder_and_item() { 1.400 + let folder_info = yield createTestFolderInfo(); 1.401 + let bm_info = { uri: NetUtil.newURI("http://test_create_item_to_folder.com") 1.402 + , title: "Test Bookmark" 1.403 + , index: bmStartIndex }; 1.404 + 1.405 + let { folderTxn, bkmTxn } = yield PT.transact( function* () { 1.406 + let folderTxn = PT.NewFolder(folder_info); 1.407 + folder_info.GUID = bm_info.parentGUID = yield folderTxn; 1.408 + let bkmTxn = PT.NewBookmark(bm_info); 1.409 + bm_info.GUID = yield bkmTxn;; 1.410 + return { folderTxn: folderTxn, bkmTxn: bkmTxn}; 1.411 + }); 1.412 + 1.413 + let ensureDo = function* () { 1.414 + ensureUndoState([[bkmTxn, folderTxn]], 0); 1.415 + yield ensureItemsAdded(folder_info, bm_info); 1.416 + observer.reset(); 1.417 + }; 1.418 + 1.419 + let ensureUndo = () => { 1.420 + ensureUndoState([[bkmTxn, folderTxn]], 1); 1.421 + ensureItemsRemoved(folder_info, bm_info); 1.422 + observer.reset(); 1.423 + }; 1.424 + 1.425 + yield ensureDo(); 1.426 + yield PT.undo(); 1.427 + ensureUndo(); 1.428 + yield PT.redo(); 1.429 + yield ensureDo(); 1.430 + yield PT.undo(); 1.431 + ensureUndo(); 1.432 + 1.433 + yield PT.clearTransactionsHistory(); 1.434 + ensureUndoState(); 1.435 +}); 1.436 + 1.437 +add_task(function* test_move_items_to_folder() { 1.438 + let folder_a_info = yield createTestFolderInfo("Folder A"); 1.439 + let bkm_a_info = { uri: NetUtil.newURI("http://test_move_items.com") 1.440 + , title: "Bookmark A" }; 1.441 + let bkm_b_info = { uri: NetUtil.newURI("http://test_move_items.com") 1.442 + , title: "Bookmark B" }; 1.443 + 1.444 + // Test moving items within the same folder. 1.445 + let [folder_a_txn, bkm_a_txn, bkm_b_txn] = yield PT.transact(function* () { 1.446 + let folder_a_txn = PT.NewFolder(folder_a_info); 1.447 + 1.448 + folder_a_info.GUID = 1.449 + bkm_a_info.parentGUID = bkm_b_info.parentGUID = yield folder_a_txn; 1.450 + let bkm_a_txn = PT.NewBookmark(bkm_a_info); 1.451 + bkm_a_info.GUID = yield bkm_a_txn; 1.452 + let bkm_b_txn = PT.NewBookmark(bkm_b_info); 1.453 + bkm_b_info.GUID = yield bkm_b_txn; 1.454 + return [folder_a_txn, bkm_a_txn, bkm_b_txn]; 1.455 + }); 1.456 + 1.457 + ensureUndoState([[bkm_b_txn, bkm_a_txn, folder_a_txn]], 0); 1.458 + 1.459 + let moveTxn = PT.MoveItem({ GUID: bkm_a_info.GUID 1.460 + , newParentGUID: folder_a_info.GUID }); 1.461 + yield PT.transact(moveTxn); 1.462 + 1.463 + let ensureDo = () => { 1.464 + ensureUndoState([[moveTxn], [bkm_b_txn, bkm_a_txn, folder_a_txn]], 0); 1.465 + ensureItemsMoved({ GUID: bkm_a_info.GUID 1.466 + , oldParentGUID: folder_a_info.GUID 1.467 + , newParentGUID: folder_a_info.GUID 1.468 + , oldIndex: 0 1.469 + , newIndex: 1 }); 1.470 + observer.reset(); 1.471 + }; 1.472 + let ensureUndo = () => { 1.473 + ensureUndoState([[moveTxn], [bkm_b_txn, bkm_a_txn, folder_a_txn]], 1); 1.474 + ensureItemsMoved({ GUID: bkm_a_info.GUID 1.475 + , oldParentGUID: folder_a_info.GUID 1.476 + , newParentGUID: folder_a_info.GUID 1.477 + , oldIndex: 1 1.478 + , newIndex: 0 }); 1.479 + observer.reset(); 1.480 + }; 1.481 + 1.482 + ensureDo(); 1.483 + yield PT.undo(); 1.484 + ensureUndo(); 1.485 + yield PT.redo(); 1.486 + ensureDo(); 1.487 + yield PT.undo(); 1.488 + ensureUndo(); 1.489 + 1.490 + yield PT.clearTransactionsHistory(false, true); 1.491 + ensureUndoState([[bkm_b_txn, bkm_a_txn, folder_a_txn]], 0); 1.492 + 1.493 + // Test moving items between folders. 1.494 + let folder_b_info = yield createTestFolderInfo("Folder B"); 1.495 + let folder_b_txn = PT.NewFolder(folder_b_info); 1.496 + folder_b_info.GUID = yield PT.transact(folder_b_txn); 1.497 + ensureUndoState([ [folder_b_txn] 1.498 + , [bkm_b_txn, bkm_a_txn, folder_a_txn] ], 0); 1.499 + 1.500 + moveTxn = PT.MoveItem({ GUID: bkm_a_info.GUID 1.501 + , newParentGUID: folder_b_info.GUID 1.502 + , newIndex: bmsvc.DEFAULT_INDEX }); 1.503 + yield PT.transact(moveTxn); 1.504 + 1.505 + ensureDo = () => { 1.506 + ensureUndoState([ [moveTxn] 1.507 + , [folder_b_txn] 1.508 + , [bkm_b_txn, bkm_a_txn, folder_a_txn] ], 0); 1.509 + ensureItemsMoved({ GUID: bkm_a_info.GUID 1.510 + , oldParentGUID: folder_a_info.GUID 1.511 + , newParentGUID: folder_b_info.GUID 1.512 + , oldIndex: 0 1.513 + , newIndex: 0 }); 1.514 + observer.reset(); 1.515 + }; 1.516 + let ensureUndo = () => { 1.517 + ensureUndoState([ [moveTxn] 1.518 + , [folder_b_txn] 1.519 + , [bkm_b_txn, bkm_a_txn, folder_a_txn] ], 1); 1.520 + ensureItemsMoved({ GUID: bkm_a_info.GUID 1.521 + , oldParentGUID: folder_b_info.GUID 1.522 + , newParentGUID: folder_a_info.GUID 1.523 + , oldIndex: 0 1.524 + , newIndex: 0 }); 1.525 + observer.reset(); 1.526 + }; 1.527 + 1.528 + ensureDo(); 1.529 + yield PT.undo(); 1.530 + ensureUndo(); 1.531 + yield PT.redo(); 1.532 + ensureDo(); 1.533 + yield PT.undo(); 1.534 + ensureUndo(); 1.535 + 1.536 + // Clean up 1.537 + yield PT.undo(); // folder_b_txn 1.538 + yield PT.undo(); // folder_a_txn + the bookmarks; 1.539 + do_check_eq(observer.itemsRemoved.size, 4); 1.540 + ensureUndoState([ [moveTxn] 1.541 + , [folder_b_txn] 1.542 + , [bkm_b_txn, bkm_a_txn, folder_a_txn] ], 3); 1.543 + yield PT.clearTransactionsHistory(); 1.544 + ensureUndoState(); 1.545 +}); 1.546 + 1.547 +add_task(function* test_remove_folder() { 1.548 + let folder_level_1_info = yield createTestFolderInfo("Folder Level 1"); 1.549 + let folder_level_2_info = { title: "Folder Level 2" }; 1.550 + let [folder_level_1_txn, 1.551 + folder_level_2_txn] = yield PT.transact(function* () { 1.552 + let folder_level_1_txn = PT.NewFolder(folder_level_1_info); 1.553 + folder_level_1_info.GUID = yield folder_level_1_txn; 1.554 + folder_level_2_info.parentGUID = folder_level_1_info.GUID; 1.555 + let folder_level_2_txn = PT.NewFolder(folder_level_2_info); 1.556 + folder_level_2_info.GUID = yield folder_level_2_txn; 1.557 + return [folder_level_1_txn, folder_level_2_txn]; 1.558 + }); 1.559 + 1.560 + ensureUndoState([[folder_level_2_txn, folder_level_1_txn]]); 1.561 + yield ensureItemsAdded(folder_level_1_info, folder_level_2_info); 1.562 + observer.reset(); 1.563 + 1.564 + let remove_folder_2_txn = PT.RemoveItem(folder_level_2_info); 1.565 + yield PT.transact(remove_folder_2_txn); 1.566 + 1.567 + ensureUndoState([ [remove_folder_2_txn] 1.568 + , [folder_level_2_txn, folder_level_1_txn] ]); 1.569 + yield ensureItemsRemoved(folder_level_2_info); 1.570 + 1.571 + // Undo RemoveItem "Folder Level 2" 1.572 + yield PT.undo(); 1.573 + ensureUndoState([ [remove_folder_2_txn] 1.574 + , [folder_level_2_txn, folder_level_1_txn] ], 1); 1.575 + yield ensureItemsAdded(folder_level_2_info); 1.576 + ensureTimestampsUpdated(folder_level_2_info.GUID, true); 1.577 + observer.reset(); 1.578 + 1.579 + // Redo RemoveItem "Folder Level 2" 1.580 + yield PT.redo(); 1.581 + ensureUndoState([ [remove_folder_2_txn] 1.582 + , [folder_level_2_txn, folder_level_1_txn] ]); 1.583 + yield ensureItemsRemoved(folder_level_2_info); 1.584 + observer.reset(); 1.585 + 1.586 + // Undo it again 1.587 + yield PT.undo(); 1.588 + ensureUndoState([ [remove_folder_2_txn] 1.589 + , [folder_level_2_txn, folder_level_1_txn] ], 1); 1.590 + yield ensureItemsAdded(folder_level_2_info); 1.591 + ensureTimestampsUpdated(folder_level_2_info.GUID, true); 1.592 + observer.reset(); 1.593 + 1.594 + // Undo the creation of both folders 1.595 + yield PT.undo(); 1.596 + ensureUndoState([ [remove_folder_2_txn] 1.597 + , [folder_level_2_txn, folder_level_1_txn] ], 2); 1.598 + yield ensureItemsRemoved(folder_level_2_info, folder_level_1_info); 1.599 + observer.reset(); 1.600 + 1.601 + // Redo the creation of both folders 1.602 + yield PT.redo(); 1.603 + ensureUndoState([ [remove_folder_2_txn] 1.604 + , [folder_level_2_txn, folder_level_1_txn] ], 1); 1.605 + yield ensureItemsAdded(folder_level_1_info, folder_level_2_info); 1.606 + ensureTimestampsUpdated(folder_level_1_info.GUID, true); 1.607 + ensureTimestampsUpdated(folder_level_2_info.GUID, true); 1.608 + observer.reset(); 1.609 + 1.610 + // Redo RemoveItem "Folder Level 2" 1.611 + yield PT.redo(); 1.612 + ensureUndoState([ [remove_folder_2_txn] 1.613 + , [folder_level_2_txn, folder_level_1_txn] ]); 1.614 + yield ensureItemsRemoved(folder_level_2_info); 1.615 + observer.reset(); 1.616 + 1.617 + // Undo everything one last time 1.618 + yield PT.undo(); 1.619 + ensureUndoState([ [remove_folder_2_txn] 1.620 + , [folder_level_2_txn, folder_level_1_txn] ], 1); 1.621 + yield ensureItemsAdded(folder_level_2_info); 1.622 + observer.reset(); 1.623 + 1.624 + yield PT.undo(); 1.625 + ensureUndoState([ [remove_folder_2_txn] 1.626 + , [folder_level_2_txn, folder_level_1_txn] ], 2); 1.627 + yield ensureItemsRemoved(folder_level_2_info, folder_level_2_info); 1.628 + observer.reset(); 1.629 + 1.630 + yield PT.clearTransactionsHistory(); 1.631 + ensureUndoState(); 1.632 +}); 1.633 + 1.634 +add_task(function* test_add_and_remove_bookmarks_with_additional_info() { 1.635 + const testURI = NetUtil.newURI("http://add.remove.tag") 1.636 + , TAG_1 = "TestTag1", TAG_2 = "TestTag2" 1.637 + , KEYWORD = "test_keyword" 1.638 + , POST_DATA = "post_data" 1.639 + , ANNO = { name: "TestAnno", value: "TestAnnoValue" }; 1.640 + 1.641 + let folder_info = yield createTestFolderInfo(); 1.642 + folder_info.GUID = yield PT.transact(PT.NewFolder(folder_info)); 1.643 + let ensureTags = ensureTagsForURI.bind(null, testURI); 1.644 + 1.645 + // Check that the NewBookmark transaction preserves tags. 1.646 + observer.reset(); 1.647 + let b1_info = { parentGUID: folder_info.GUID, uri: testURI, tags: [TAG_1] }; 1.648 + b1_info.GUID = yield PT.transact(PT.NewBookmark(b1_info)); 1.649 + ensureTags([TAG_1]); 1.650 + yield PT.undo(); 1.651 + ensureTags([]); 1.652 + 1.653 + observer.reset(); 1.654 + yield PT.redo(); 1.655 + ensureTimestampsUpdated(b1_info.GUID, true); 1.656 + ensureTags([TAG_1]); 1.657 + 1.658 + // Check if the RemoveItem transaction removes and restores tags of children 1.659 + // correctly. 1.660 + yield PT.transact(PT.RemoveItem(folder_info.GUID)); 1.661 + ensureTags([]); 1.662 + 1.663 + observer.reset(); 1.664 + yield PT.undo(); 1.665 + ensureTimestampsUpdated(b1_info.GUID, true); 1.666 + ensureTags([TAG_1]); 1.667 + 1.668 + yield PT.redo(); 1.669 + ensureTags([]); 1.670 + 1.671 + observer.reset(); 1.672 + yield PT.undo(); 1.673 + ensureTimestampsUpdated(b1_info.GUID, true); 1.674 + ensureTags([TAG_1]); 1.675 + 1.676 + // * Check that no-op tagging (the uri is already tagged with TAG_1) is 1.677 + // also a no-op on undo. 1.678 + // * Test the "keyword" property of the NewBookmark transaction. 1.679 + observer.reset(); 1.680 + let b2_info = { parentGUID: folder_info.GUID 1.681 + , uri: testURI, tags: [TAG_1, TAG_2] 1.682 + , keyword: KEYWORD 1.683 + , postData: POST_DATA 1.684 + , annotations: [ANNO] }; 1.685 + b2_info.GUID = yield PT.transact(PT.NewBookmark(b2_info)); 1.686 + let b2_post_creation_changes = [ 1.687 + { GUID: b2_info.GUID 1.688 + , isAnnoProperty: true 1.689 + , property: ANNO.name 1.690 + , newValue: ANNO.value }, 1.691 + { GUID: b2_info.GUID 1.692 + , property: "keyword" 1.693 + , newValue: KEYWORD }, 1.694 + { GUID: b2_info.GUID 1.695 + , isAnnoProperty: true 1.696 + , property: PlacesUtils.POST_DATA_ANNO 1.697 + , newValue: POST_DATA } ]; 1.698 + ensureItemsChanged(...b2_post_creation_changes); 1.699 + ensureTags([TAG_1, TAG_2]); 1.700 + 1.701 + observer.reset(); 1.702 + yield PT.undo(); 1.703 + yield ensureItemsRemoved(b2_info); 1.704 + ensureTags([TAG_1]); 1.705 + 1.706 + // Check if RemoveItem correctly restores keywords, tags and annotations. 1.707 + observer.reset(); 1.708 + yield PT.redo(); 1.709 + ensureItemsChanged(...b2_post_creation_changes); 1.710 + ensureTags([TAG_1, TAG_2]); 1.711 + 1.712 + // Test RemoveItem for multiple items. 1.713 + observer.reset(); 1.714 + yield PT.transact(PT.RemoveItem(b1_info.GUID)); 1.715 + yield PT.transact(PT.RemoveItem(b2_info.GUID)); 1.716 + yield PT.transact(PT.RemoveItem(folder_info.GUID)); 1.717 + yield ensureItemsRemoved(b1_info, b2_info, folder_info); 1.718 + ensureTags([]); 1.719 + 1.720 + observer.reset(); 1.721 + yield PT.undo(); 1.722 + yield ensureItemsAdded(folder_info); 1.723 + ensureTags([]); 1.724 + 1.725 + observer.reset(); 1.726 + yield PT.undo(); 1.727 + ensureItemsChanged(...b2_post_creation_changes); 1.728 + ensureTags([TAG_1, TAG_2]); 1.729 + 1.730 + observer.reset(); 1.731 + yield PT.undo(); 1.732 + yield ensureItemsAdded(b1_info); 1.733 + ensureTags([TAG_1, TAG_2]); 1.734 + 1.735 + // The redo calls below cleanup everything we did. 1.736 + observer.reset(); 1.737 + yield PT.redo(); 1.738 + yield ensureItemsRemoved(b1_info); 1.739 + ensureTags([TAG_1, TAG_2]); 1.740 + 1.741 + observer.reset(); 1.742 + yield PT.redo(); 1.743 + yield ensureItemsRemoved(b2_info); 1.744 + ensureTags([]); 1.745 + 1.746 + observer.reset(); 1.747 + yield PT.redo(); 1.748 + yield ensureItemsRemoved(folder_info); 1.749 + ensureTags([]); 1.750 + 1.751 + yield PT.clearTransactionsHistory(); 1.752 + ensureUndoState(); 1.753 +}); 1.754 + 1.755 +add_task(function* test_creating_and_removing_a_separator() { 1.756 + let folder_info = yield createTestFolderInfo(); 1.757 + let separator_info = {}; 1.758 + let undoEntries = []; 1.759 + 1.760 + observer.reset(); 1.761 + let create_txns = yield PT.transact(function* () { 1.762 + let folder_txn = PT.NewFolder(folder_info); 1.763 + folder_info.GUID = separator_info.parentGUID = yield folder_txn; 1.764 + let separator_txn = PT.NewSeparator(separator_info); 1.765 + separator_info.GUID = yield separator_txn; 1.766 + return [separator_txn, folder_txn]; 1.767 + }); 1.768 + undoEntries.unshift(create_txns); 1.769 + ensureUndoState(undoEntries, 0); 1.770 + ensureItemsAdded(folder_info, separator_info); 1.771 + 1.772 + observer.reset(); 1.773 + yield PT.undo(); 1.774 + ensureUndoState(undoEntries, 1); 1.775 + ensureItemsRemoved(folder_info, separator_info); 1.776 + 1.777 + observer.reset(); 1.778 + yield PT.redo(); 1.779 + ensureUndoState(undoEntries, 0); 1.780 + ensureItemsAdded(folder_info, separator_info); 1.781 + 1.782 + observer.reset(); 1.783 + let remove_sep_txn = PT.RemoveItem(separator_info); 1.784 + yield PT.transact(remove_sep_txn); 1.785 + undoEntries.unshift([remove_sep_txn]); 1.786 + ensureUndoState(undoEntries, 0); 1.787 + ensureItemsRemoved(separator_info); 1.788 + 1.789 + observer.reset(); 1.790 + yield PT.undo(); 1.791 + ensureUndoState(undoEntries, 1); 1.792 + ensureItemsAdded(separator_info); 1.793 + 1.794 + observer.reset(); 1.795 + yield PT.undo(); 1.796 + ensureUndoState(undoEntries, 2); 1.797 + ensureItemsRemoved(folder_info, separator_info); 1.798 + 1.799 + observer.reset(); 1.800 + yield PT.redo(); 1.801 + ensureUndoState(undoEntries, 1); 1.802 + ensureItemsAdded(folder_info, separator_info); 1.803 + 1.804 + // Clear redo entries and check that |redo| does nothing 1.805 + observer.reset(); 1.806 + yield PT.clearTransactionsHistory(false, true); 1.807 + undoEntries.shift(); 1.808 + ensureUndoState(undoEntries, 0); 1.809 + yield PT.redo(); 1.810 + ensureItemsAdded(); 1.811 + ensureItemsRemoved(); 1.812 + 1.813 + // Cleanup 1.814 + observer.reset(); 1.815 + yield PT.undo(); 1.816 + ensureUndoState(undoEntries, 1); 1.817 + ensureItemsRemoved(folder_info, separator_info); 1.818 + yield PT.clearTransactionsHistory(); 1.819 + ensureUndoState(); 1.820 +}); 1.821 + 1.822 +add_task(function* test_edit_title() { 1.823 + let bm_info = { parentGUID: yield PlacesUtils.promiseItemGUID(root) 1.824 + , uri: NetUtil.newURI("http://test_create_item.com") 1.825 + , title: "Original Title" }; 1.826 + 1.827 + function ensureTitleChange(aCurrentTitle) { 1.828 + ensureItemsChanged({ GUID: bm_info.GUID 1.829 + , property: "title" 1.830 + , newValue: aCurrentTitle}); 1.831 + } 1.832 + 1.833 + bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); 1.834 + 1.835 + observer.reset(); 1.836 + yield PT.transact(PT.EditTitle({ GUID: bm_info.GUID, title: "New Title" })); 1.837 + ensureTitleChange("New Title"); 1.838 + 1.839 + observer.reset(); 1.840 + yield PT.undo(); 1.841 + ensureTitleChange("Original Title"); 1.842 + 1.843 + observer.reset(); 1.844 + yield PT.redo(); 1.845 + ensureTitleChange("New Title"); 1.846 + 1.847 + // Cleanup 1.848 + observer.reset(); 1.849 + yield PT.undo(); 1.850 + ensureTitleChange("Original Title"); 1.851 + yield PT.undo(); 1.852 + ensureItemsRemoved(bm_info); 1.853 + 1.854 + yield PT.clearTransactionsHistory(); 1.855 + ensureUndoState(); 1.856 +}); 1.857 + 1.858 +add_task(function* test_edit_url() { 1.859 + let oldURI = NetUtil.newURI("http://old.test_editing_item_uri.com/"); 1.860 + let newURI = NetUtil.newURI("http://new.test_editing_item_uri.com/"); 1.861 + let bm_info = { parentGUID: yield PlacesUtils.promiseItemGUID(root) 1.862 + , uri: oldURI 1.863 + , tags: ["TestTag"]}; 1.864 + 1.865 + function ensureURIAndTags(aPreChangeURI, aPostChangeURI, aOLdURITagsPreserved) { 1.866 + ensureItemsChanged({ GUID: bm_info.GUID 1.867 + , property: "uri" 1.868 + , newValue: aPostChangeURI.spec }); 1.869 + ensureTagsForURI(aPostChangeURI, bm_info.tags); 1.870 + ensureTagsForURI(aPreChangeURI, aOLdURITagsPreserved ? bm_info.tags : []); 1.871 + } 1.872 + 1.873 + bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); 1.874 + ensureTagsForURI(oldURI, bm_info.tags); 1.875 + 1.876 + // When there's a single bookmark for the same url, tags should be moved. 1.877 + observer.reset(); 1.878 + yield PT.transact(PT.EditURI({ GUID: bm_info.GUID, uri: newURI })); 1.879 + ensureURIAndTags(oldURI, newURI, false); 1.880 + 1.881 + observer.reset(); 1.882 + yield PT.undo(); 1.883 + ensureURIAndTags(newURI, oldURI, false); 1.884 + 1.885 + observer.reset(); 1.886 + yield PT.redo(); 1.887 + ensureURIAndTags(oldURI, newURI, false); 1.888 + 1.889 + observer.reset(); 1.890 + yield PT.undo(); 1.891 + ensureURIAndTags(newURI, oldURI, false); 1.892 + 1.893 + // When there're multiple bookmarks for the same url, tags should be copied. 1.894 + let bm2_info = Object.create(bm_info); 1.895 + bm2_info.GUID = yield PT.transact(PT.NewBookmark(bm2_info)); 1.896 + let bm3_info = Object.create(bm_info); 1.897 + bm3_info.uri = newURI; 1.898 + bm3_info.GUID = yield PT.transact(PT.NewBookmark(bm3_info)); 1.899 + 1.900 + observer.reset(); 1.901 + yield PT.transact(PT.EditURI({ GUID: bm_info.GUID, uri: newURI })); 1.902 + ensureURIAndTags(oldURI, newURI, true); 1.903 + 1.904 + observer.reset(); 1.905 + yield PT.undo(); 1.906 + ensureURIAndTags(newURI, oldURI, true); 1.907 + 1.908 + observer.reset(); 1.909 + yield PT.redo(); 1.910 + ensureURIAndTags(oldURI, newURI, true); 1.911 + 1.912 + // Cleanup 1.913 + observer.reset(); 1.914 + yield PT.undo(); 1.915 + ensureURIAndTags(newURI, oldURI, true); 1.916 + yield PT.undo(); 1.917 + yield PT.undo(); 1.918 + yield PT.undo(); 1.919 + ensureItemsRemoved(bm3_info, bm2_info, bm_info); 1.920 + 1.921 + yield PT.clearTransactionsHistory(); 1.922 + ensureUndoState(); 1.923 +}); 1.924 + 1.925 +add_task(function* test_edit_keyword() { 1.926 + let bm_info = { parentGUID: yield PlacesUtils.promiseItemGUID(root) 1.927 + , uri: NetUtil.newURI("http://test.edit.keyword") }; 1.928 + const KEYWORD = "test_keyword"; 1.929 + bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); 1.930 + function ensureKeywordChange(aCurrentKeyword = "") { 1.931 + ensureItemsChanged({ GUID: bm_info.GUID 1.932 + , property: "keyword" 1.933 + , newValue: aCurrentKeyword }); 1.934 + } 1.935 + 1.936 + bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); 1.937 + 1.938 + observer.reset(); 1.939 + yield PT.transact(PT.EditKeyword({ GUID: bm_info.GUID, keyword: KEYWORD })); 1.940 + ensureKeywordChange(KEYWORD); 1.941 + 1.942 + observer.reset(); 1.943 + yield PT.undo(); 1.944 + ensureKeywordChange(); 1.945 + 1.946 + observer.reset(); 1.947 + yield PT.redo(); 1.948 + ensureKeywordChange(KEYWORD); 1.949 + 1.950 + // Cleanup 1.951 + observer.reset(); 1.952 + yield PT.undo(); 1.953 + ensureKeywordChange(); 1.954 + yield PT.undo(); 1.955 + ensureItemsRemoved(bm_info); 1.956 + 1.957 + yield PT.clearTransactionsHistory(); 1.958 + ensureUndoState(); 1.959 +}); 1.960 + 1.961 +add_task(function* test_tag_uri_unbookmarked_uri() { 1.962 + let info = { uri: NetUtil.newURI("http://un.book.marked"), tags: ["MyTag"] }; 1.963 + 1.964 + function ensureDo() { 1.965 + // A new bookmark should be created. 1.966 + // (getMostRecentBookmarkForURI ignores tags) 1.967 + do_check_neq(PlacesUtils.getMostRecentBookmarkForURI(info.uri), -1); 1.968 + ensureTagsForURI(info.uri, info.tags); 1.969 + } 1.970 + function ensureUndo() { 1.971 + do_check_eq(PlacesUtils.getMostRecentBookmarkForURI(info.uri), -1); 1.972 + ensureTagsForURI(info.uri, []); 1.973 + } 1.974 + 1.975 + yield PT.transact(PT.TagURI(info)); 1.976 + ensureDo(); 1.977 + yield PT.undo(); 1.978 + ensureUndo(); 1.979 + yield PT.redo(); 1.980 + ensureDo(); 1.981 + yield PT.undo(); 1.982 + ensureUndo(); 1.983 +}); 1.984 + 1.985 +add_task(function* test_tag_uri_bookmarked_uri() { 1.986 + let bm_info = { uri: NetUtil.newURI("http://bookmarked.uri") 1.987 + , parentGUID: yield PlacesUtils.promiseItemGUID(root) }; 1.988 + bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); 1.989 + 1.990 + let tagging_info = { uri: bm_info.uri, tags: ["MyTag"] }; 1.991 + yield PT.transact(PT.TagURI(tagging_info)); 1.992 + ensureTagsForURI(tagging_info.uri, tagging_info.tags); 1.993 + 1.994 + yield PT.undo(); 1.995 + ensureTagsForURI(tagging_info.uri, []); 1.996 + yield PT.redo(); 1.997 + ensureTagsForURI(tagging_info.uri, tagging_info.tags); 1.998 + 1.999 + // Cleanup 1.1000 + yield PT.undo(); 1.1001 + ensureTagsForURI(tagging_info.uri, []); 1.1002 + observer.reset(); 1.1003 + yield PT.undo(); 1.1004 + ensureItemsRemoved(bm_info); 1.1005 + 1.1006 + yield PT.clearTransactionsHistory(); 1.1007 + ensureUndoState(); 1.1008 +}); 1.1009 + 1.1010 +add_task(function* test_untag_uri() { 1.1011 + let bm_info = { uri: NetUtil.newURI("http://test.untag.uri") 1.1012 + , parentGUID: yield PlacesUtils.promiseItemGUID(root) 1.1013 + , tags: ["T"]}; 1.1014 + bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); 1.1015 + 1.1016 + yield PT.transact(PT.UntagURI(bm_info)); 1.1017 + ensureTagsForURI(bm_info.uri, []); 1.1018 + yield PT.undo(); 1.1019 + ensureTagsForURI(bm_info.uri, bm_info.tags); 1.1020 + yield PT.redo(); 1.1021 + ensureTagsForURI(bm_info.uri, []); 1.1022 + yield PT.undo(); 1.1023 + ensureTagsForURI(bm_info.uri, bm_info.tags); 1.1024 + 1.1025 + // Also test just passing the uri (should remove all tags) 1.1026 + yield PT.transact(PT.UntagURI(bm_info.uri)); 1.1027 + ensureTagsForURI(bm_info.uri, []); 1.1028 + yield PT.undo(); 1.1029 + ensureTagsForURI(bm_info.uri, bm_info.tags); 1.1030 + yield PT.redo(); 1.1031 + ensureTagsForURI(bm_info.uri, []); 1.1032 +}); 1.1033 + 1.1034 +add_task(function* test_set_item_annotation() { 1.1035 + let bm_info = { uri: NetUtil.newURI("http://test.item.annotation") 1.1036 + , parentGUID: yield PlacesUtils.promiseItemGUID(root) }; 1.1037 + let anno_info = { name: "TestAnno", value: "TestValue" }; 1.1038 + function ensureAnnoState(aSet) { 1.1039 + ensureAnnotationsSet(bm_info.GUID, 1.1040 + [{ name: anno_info.name 1.1041 + , value: aSet ? anno_info.value : null }]); 1.1042 + } 1.1043 + 1.1044 + bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); 1.1045 + 1.1046 + observer.reset(); 1.1047 + yield PT.transact(PT.SetItemAnnotation({ GUID: bm_info.GUID 1.1048 + , annotationObject: anno_info })); 1.1049 + ensureAnnoState(true); 1.1050 + 1.1051 + observer.reset(); 1.1052 + yield PT.undo(); 1.1053 + ensureAnnoState(false); 1.1054 + 1.1055 + observer.reset(); 1.1056 + yield PT.redo(); 1.1057 + ensureAnnoState(true); 1.1058 + 1.1059 + // Test removing the annotation by not passing the |value| property. 1.1060 + observer.reset(); 1.1061 + yield PT.transact( 1.1062 + PT.SetItemAnnotation({ GUID: bm_info.GUID 1.1063 + , annotationObject: { name: anno_info.name }})); 1.1064 + ensureAnnoState(false); 1.1065 + 1.1066 + observer.reset(); 1.1067 + yield PT.undo(); 1.1068 + ensureAnnoState(true); 1.1069 + 1.1070 + observer.reset(); 1.1071 + yield PT.redo(); 1.1072 + ensureAnnoState(false); 1.1073 +}); 1.1074 + 1.1075 +add_task(function* test_sort_folder_by_name() { 1.1076 + let folder_info = yield createTestFolderInfo(); 1.1077 + 1.1078 + let uri = NetUtil.newURI("http://sort.by.name/"); 1.1079 + let preSep = [{ title: i, uri: uri } for (i of ["3","2","1"])]; 1.1080 + let sep = {}; 1.1081 + let postSep = [{ title: l, uri: uri } for (l of ["c","b","a"])]; 1.1082 + let originalOrder = [...preSep, sep, ...postSep]; 1.1083 + let sortedOrder = [...preSep.slice(0).reverse(), 1.1084 + sep, 1.1085 + ...postSep.slice(0).reverse()]; 1.1086 + yield PT.transact(function* () { 1.1087 + folder_info.GUID = yield PT.NewFolder(folder_info); 1.1088 + for (let info of originalOrder) { 1.1089 + info.parentGUID = folder_info.GUID; 1.1090 + info.GUID = yield info == sep ? 1.1091 + PT.NewSeparator(info) : PT.NewBookmark(info); 1.1092 + } 1.1093 + }); 1.1094 + 1.1095 + let folderId = yield PlacesUtils.promiseItemId(folder_info.GUID); 1.1096 + let folderContainer = PlacesUtils.getFolderContents(folderId).root; 1.1097 + function ensureOrder(aOrder) { 1.1098 + for (let i = 0; i < folderContainer.childCount; i++) { 1.1099 + do_check_eq(folderContainer.getChild(i).bookmarkGuid, aOrder[i].GUID); 1.1100 + } 1.1101 + } 1.1102 + 1.1103 + ensureOrder(originalOrder); 1.1104 + yield PT.transact(PT.SortByName(folder_info.GUID)); 1.1105 + ensureOrder(sortedOrder); 1.1106 + yield PT.undo(); 1.1107 + ensureOrder(originalOrder); 1.1108 + yield PT.redo(); 1.1109 + ensureOrder(sortedOrder); 1.1110 + 1.1111 + // Cleanup 1.1112 + observer.reset(); 1.1113 + yield PT.undo(); 1.1114 + ensureOrder(originalOrder); 1.1115 + yield PT.undo(); 1.1116 + ensureItemsRemoved(...originalOrder, folder_info); 1.1117 +}); 1.1118 + 1.1119 +add_task(function* test_livemark_txns() { 1.1120 + let livemark_info = 1.1121 + { feedURI: NetUtil.newURI("http://test.feed.uri") 1.1122 + , parentGUID: yield PlacesUtils.promiseItemGUID(root) 1.1123 + , title: "Test Livemark" }; 1.1124 + function ensureLivemarkAdded() { 1.1125 + ensureItemsAdded({ GUID: livemark_info.GUID 1.1126 + , title: livemark_info.title 1.1127 + , parentGUID: livemark_info.parentGUID 1.1128 + , itemType: bmsvc.TYPE_FOLDER }); 1.1129 + let annos = [{ name: PlacesUtils.LMANNO_FEEDURI 1.1130 + , value: livemark_info.feedURI.spec }]; 1.1131 + if ("siteURI" in livemark_info) { 1.1132 + annos.push({ name: PlacesUtils.LMANNO_SITEURI 1.1133 + , value: livemark_info.siteURI.spec }); 1.1134 + } 1.1135 + ensureAnnotationsSet(livemark_info.GUID, annos); 1.1136 + } 1.1137 + function ensureLivemarkRemoved() { 1.1138 + ensureItemsRemoved({ GUID: livemark_info.GUID 1.1139 + , parentGUID: livemark_info.parentGUID }); 1.1140 + } 1.1141 + 1.1142 + function* _testDoUndoRedoUndo() { 1.1143 + observer.reset(); 1.1144 + livemark_info.GUID = yield PT.transact(PT.NewLivemark(livemark_info)); 1.1145 + ensureLivemarkAdded(); 1.1146 + 1.1147 + observer.reset(); 1.1148 + yield PT.undo(); 1.1149 + ensureLivemarkRemoved(); 1.1150 + 1.1151 + observer.reset(); 1.1152 + yield PT.redo(); 1.1153 + ensureLivemarkAdded(); 1.1154 + 1.1155 + yield PT.undo(); 1.1156 + ensureLivemarkRemoved(); 1.1157 + } 1.1158 + 1.1159 + yield* _testDoUndoRedoUndo() 1.1160 + livemark_info.siteURI = NetUtil.newURI("http://feed.site.uri"); 1.1161 + yield* _testDoUndoRedoUndo(); 1.1162 + 1.1163 + yield PT.clearTransactionsHistory(); 1.1164 +});