summaryrefslogtreecommitdiffstats
path: root/toolkit/components/places/tests/unit/test_async_transactions.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/places/tests/unit/test_async_transactions.js')
-rw-r--r--toolkit/components/places/tests/unit/test_async_transactions.js1739
1 files changed, 0 insertions, 1739 deletions
diff --git a/toolkit/components/places/tests/unit/test_async_transactions.js b/toolkit/components/places/tests/unit/test_async_transactions.js
deleted file mode 100644
index edc9abf87..000000000
--- a/toolkit/components/places/tests/unit/test_async_transactions.js
+++ /dev/null
@@ -1,1739 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const bmsvc = PlacesUtils.bookmarks;
-const tagssvc = PlacesUtils.tagging;
-const annosvc = PlacesUtils.annotations;
-const PT = PlacesTransactions;
-const rootGuid = PlacesUtils.bookmarks.rootGuid;
-
-Components.utils.importGlobalProperties(["URL"]);
-
-// Create and add bookmarks observer.
-var observer = {
- __proto__: NavBookmarkObserver.prototype,
-
- tagRelatedGuids: new Set(),
-
- reset: function () {
- this.itemsAdded = new Map();
- this.itemsRemoved = new Map();
- this.itemsChanged = new Map();
- this.itemsMoved = new Map();
- this.beginUpdateBatch = false;
- this.endUpdateBatch = false;
- },
-
- onBeginUpdateBatch: function () {
- this.beginUpdateBatch = true;
- },
-
- onEndUpdateBatch: function () {
- this.endUpdateBatch = true;
- },
-
- onItemAdded:
- function (aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded,
- aGuid, aParentGuid) {
- // Ignore tag items.
- if (aParentId == PlacesUtils.tagsFolderId ||
- (aParentId != PlacesUtils.placesRootId &&
- bmsvc.getFolderIdForItem(aParentId) == PlacesUtils.tagsFolderId)) {
- this.tagRelatedGuids.add(aGuid);
- return;
- }
-
- this.itemsAdded.set(aGuid, { itemId: aItemId
- , parentGuid: aParentGuid
- , index: aIndex
- , itemType: aItemType
- , title: aTitle
- , url: aURI });
- },
-
- onItemRemoved:
- function (aItemId, aParentId, aIndex, aItemType, aURI, aGuid, aParentGuid) {
- if (this.tagRelatedGuids.has(aGuid))
- return;
-
- this.itemsRemoved.set(aGuid, { parentGuid: aParentGuid
- , index: aIndex
- , itemType: aItemType });
- },
-
- onItemChanged:
- function (aItemId, aProperty, aIsAnnoProperty, aNewValue, aLastModified,
- aItemType, aParentId, aGuid, aParentGuid) {
- if (this.tagRelatedGuids.has(aGuid))
- return;
-
- let changesForGuid = this.itemsChanged.get(aGuid);
- if (changesForGuid === undefined) {
- changesForGuid = new Map();
- this.itemsChanged.set(aGuid, changesForGuid);
- }
-
- let newValue = aNewValue;
- if (aIsAnnoProperty) {
- if (annosvc.itemHasAnnotation(aItemId, aProperty))
- newValue = annosvc.getItemAnnotation(aItemId, aProperty);
- else
- newValue = null;
- }
- let change = { isAnnoProperty: aIsAnnoProperty
- , newValue: newValue
- , lastModified: aLastModified
- , itemType: aItemType };
- changesForGuid.set(aProperty, change);
- },
-
- onItemVisited: () => {},
-
- onItemMoved:
- function (aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex, aItemType,
- aGuid, aOldParentGuid, aNewParentGuid) {
- this.itemsMoved.set(aGuid, { oldParentGuid: aOldParentGuid
- , oldIndex: aOldIndex
- , newParentGuid: aNewParentGuid
- , newIndex: aNewIndex
- , itemType: aItemType });
- }
-};
-observer.reset();
-
-// index at which items should begin
-var bmStartIndex = 0;
-
-function run_test() {
- bmsvc.addObserver(observer, false);
- do_register_cleanup(function () {
- bmsvc.removeObserver(observer);
- });
-
- run_next_test();
-}
-
-function sanityCheckTransactionHistory() {
- do_check_true(PT.undoPosition <= PT.length);
-
- let check_entry_throws = f => {
- try {
- f();
- do_throw("PT.entry should throw for invalid input");
- } catch (ex) {}
- };
- check_entry_throws( () => PT.entry(-1) );
- check_entry_throws( () => PT.entry({}) );
- check_entry_throws( () => PT.entry(PT.length) );
-
- if (PT.undoPosition < PT.length)
- do_check_eq(PT.topUndoEntry, PT.entry(PT.undoPosition));
- else
- do_check_null(PT.topUndoEntry);
- if (PT.undoPosition > 0)
- do_check_eq(PT.topRedoEntry, PT.entry(PT.undoPosition - 1));
- else
- do_check_null(PT.topRedoEntry);
-}
-
-function getTransactionsHistoryState() {
- let history = [];
- for (let i = 0; i < PT.length; i++) {
- history.push(PT.entry(i));
- }
- return [history, PT.undoPosition];
-}
-
-function ensureUndoState(aExpectedEntries = [], aExpectedUndoPosition = 0) {
- // ensureUndoState is called in various places during this test, so it's
- // a good places to sanity-check the transaction-history APIs in all
- // cases.
- sanityCheckTransactionHistory();
-
- let [actualEntries, actualUndoPosition] = getTransactionsHistoryState();
- do_check_eq(actualEntries.length, aExpectedEntries.length);
- do_check_eq(actualUndoPosition, aExpectedUndoPosition);
-
- function checkEqualEntries(aExpectedEntry, aActualEntry) {
- do_check_eq(aExpectedEntry.length, aActualEntry.length);
- aExpectedEntry.forEach( (t, i) => do_check_eq(t, aActualEntry[i]) );
- }
- aExpectedEntries.forEach( (e, i) => checkEqualEntries(e, actualEntries[i]) );
-}
-
-function ensureItemsAdded(...items) {
- Assert.equal(observer.itemsAdded.size, items.length);
- for (let item of items) {
- Assert.ok(observer.itemsAdded.has(item.guid));
- let info = observer.itemsAdded.get(item.guid);
- Assert.equal(info.parentGuid, item.parentGuid);
- for (let propName of ["title", "index", "itemType"]) {
- if (propName in item)
- Assert.equal(info[propName], item[propName]);
- }
- if ("url" in item)
- Assert.ok(info.url.equals(item.url));
- }
-}
-
-function ensureItemsRemoved(...items) {
- Assert.equal(observer.itemsRemoved.size, items.length);
- for (let item of items) {
- // We accept both guids and full info object here.
- if (typeof(item) == "string") {
- Assert.ok(observer.itemsRemoved.has(item));
- }
- else {
- Assert.ok(observer.itemsRemoved.has(item.guid));
- let info = observer.itemsRemoved.get(item.guid);
- Assert.equal(info.parentGuid, item.parentGuid);
- if ("index" in item)
- Assert.equal(info.index, item.index);
- }
- }
-}
-
-function ensureItemsChanged(...items) {
- for (let item of items) {
- do_check_true(observer.itemsChanged.has(item.guid));
- let changes = observer.itemsChanged.get(item.guid);
- do_check_true(changes.has(item.property));
- let info = changes.get(item.property);
- do_check_eq(info.isAnnoProperty, Boolean(item.isAnnoProperty));
- do_check_eq(info.newValue, item.newValue);
- if ("url" in item)
- do_check_true(item.url.equals(info.url));
- }
-}
-
-function ensureAnnotationsSet(aGuid, aAnnos) {
- do_check_true(observer.itemsChanged.has(aGuid));
- let changes = observer.itemsChanged.get(aGuid);
- for (let anno of aAnnos) {
- do_check_true(changes.has(anno.name));
- let changeInfo = changes.get(anno.name);
- do_check_true(changeInfo.isAnnoProperty);
- do_check_eq(changeInfo.newValue, anno.value);
- }
-}
-
-function ensureItemsMoved(...items) {
- do_check_true(observer.itemsMoved.size, items.length);
- for (let item of items) {
- do_check_true(observer.itemsMoved.has(item.guid));
- let info = observer.itemsMoved.get(item.guid);
- do_check_eq(info.oldParentGuid, item.oldParentGuid);
- do_check_eq(info.oldIndex, item.oldIndex);
- do_check_eq(info.newParentGuid, item.newParentGuid);
- do_check_eq(info.newIndex, item.newIndex);
- }
-}
-
-function ensureTimestampsUpdated(aGuid, aCheckDateAdded = false) {
- do_check_true(observer.itemsChanged.has(aGuid));
- let changes = observer.itemsChanged.get(aGuid);
- if (aCheckDateAdded)
- do_check_true(changes.has("dateAdded"))
- do_check_true(changes.has("lastModified"));
-}
-
-function ensureTagsForURI(aURI, aTags) {
- let tagsSet = tagssvc.getTagsForURI(aURI);
- do_check_eq(tagsSet.length, aTags.length);
- do_check_true(aTags.every( t => tagsSet.includes(t)));
-}
-
-function createTestFolderInfo(aTitle = "Test Folder") {
- return { parentGuid: rootGuid, title: "Test Folder" };
-}
-
-function isLivemarkTree(aTree) {
- return !!aTree.annos &&
- aTree.annos.some( a => a.name == PlacesUtils.LMANNO_FEEDURI );
-}
-
-function* ensureLivemarkCreatedByAddLivemark(aLivemarkGuid) {
- // This throws otherwise.
- yield PlacesUtils.livemarks.getLivemark({ guid: aLivemarkGuid });
-}
-
-// Checks if two bookmark trees (as returned by promiseBookmarksTree) are the
-// same.
-// false value for aCheckParentAndPosition is ignored if aIsRestoredItem is set.
-function* ensureEqualBookmarksTrees(aOriginal,
- aNew,
- aIsRestoredItem = true,
- aCheckParentAndPosition = false) {
- // Note "id" is not-enumerable, and is therefore skipped by Object.keys (both
- // ours and the one at deepEqual). This is fine for us because ids are not
- // restored by Redo.
- if (aIsRestoredItem) {
- Assert.deepEqual(aOriginal, aNew);
- if (isLivemarkTree(aNew))
- yield ensureLivemarkCreatedByAddLivemark(aNew.guid);
- return;
- }
-
- for (let property of Object.keys(aOriginal)) {
- if (property == "children") {
- Assert.equal(aOriginal.children.length, aNew.children.length);
- for (let i = 0; i < aOriginal.children.length; i++) {
- yield ensureEqualBookmarksTrees(aOriginal.children[i],
- aNew.children[i],
- false,
- true);
- }
- }
- else if (property == "guid") {
- // guid shouldn't be copied if the item was not restored.
- Assert.notEqual(aOriginal.guid, aNew.guid);
- }
- else if (property == "dateAdded") {
- // dateAdded shouldn't be copied if the item was not restored.
- Assert.ok(is_time_ordered(aOriginal.dateAdded, aNew.dateAdded));
- }
- else if (property == "lastModified") {
- // same same, except for the never-changed case
- if (!aOriginal.lastModified)
- Assert.ok(!aNew.lastModified);
- else
- Assert.ok(is_time_ordered(aOriginal.lastModified, aNew.lastModified));
- }
- else if (aCheckParentAndPosition ||
- (property != "parentGuid" && property != "index")) {
- Assert.deepEqual(aOriginal[property], aNew[property]);
- }
- }
-
- if (isLivemarkTree(aNew))
- yield ensureLivemarkCreatedByAddLivemark(aNew.guid);
-}
-
-function* ensureBookmarksTreeRestoredCorrectly(...aOriginalBookmarksTrees) {
- for (let originalTree of aOriginalBookmarksTrees) {
- let restoredTree =
- yield PlacesUtils.promiseBookmarksTree(originalTree.guid);
- yield ensureEqualBookmarksTrees(originalTree, restoredTree);
- }
-}
-
-function* ensureNonExistent(...aGuids) {
- for (let guid of aGuids) {
- Assert.strictEqual((yield PlacesUtils.promiseBookmarksTree(guid)), null);
- }
-}
-
-add_task(function* test_recycled_transactions() {
- function* ensureTransactThrowsFor(aTransaction) {
- let [txns, undoPosition] = getTransactionsHistoryState();
- try {
- yield aTransaction.transact();
- do_throw("Shouldn't be able to use the same transaction twice");
- }
- catch (ex) { }
- ensureUndoState(txns, undoPosition);
- }
-
- let txn_a = PT.NewFolder(createTestFolderInfo());
- yield txn_a.transact();
- ensureUndoState([[txn_a]], 0);
- yield ensureTransactThrowsFor(txn_a);
-
- yield PT.undo();
- ensureUndoState([[txn_a]], 1);
- ensureTransactThrowsFor(txn_a);
-
- yield PT.clearTransactionsHistory();
- ensureUndoState();
- ensureTransactThrowsFor(txn_a);
-
- let txn_b = PT.NewFolder(createTestFolderInfo());
- yield PT.batch(function* () {
- try {
- yield txn_a.transact();
- do_throw("Shouldn't be able to use the same transaction twice");
- }
- catch (ex) { }
- ensureUndoState();
- yield txn_b.transact();
- });
- ensureUndoState([[txn_b]], 0);
-
- yield PT.undo();
- ensureUndoState([[txn_b]], 1);
- ensureTransactThrowsFor(txn_a);
- ensureTransactThrowsFor(txn_b);
-
- yield PT.clearTransactionsHistory();
- ensureUndoState();
- observer.reset();
-});
-
-add_task(function* test_new_folder_with_annotation() {
- const ANNO = { name: "TestAnno", value: "TestValue" };
- let folder_info = createTestFolderInfo();
- folder_info.index = bmStartIndex;
- folder_info.annotations = [ANNO];
- ensureUndoState();
- let txn = PT.NewFolder(folder_info);
- folder_info.guid = yield txn.transact();
- let ensureDo = function* (aRedo = false) {
- ensureUndoState([[txn]], 0);
- yield ensureItemsAdded(folder_info);
- ensureAnnotationsSet(folder_info.guid, [ANNO]);
- if (aRedo)
- ensureTimestampsUpdated(folder_info.guid, true);
- observer.reset();
- };
-
- let ensureUndo = () => {
- ensureUndoState([[txn]], 1);
- ensureItemsRemoved({ guid: folder_info.guid
- , parentGuid: folder_info.parentGuid
- , index: bmStartIndex });
- observer.reset();
- };
-
- yield ensureDo();
- yield PT.undo();
- yield ensureUndo();
- yield PT.redo();
- yield ensureDo(true);
- yield PT.undo();
- ensureUndo();
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_new_bookmark() {
- let bm_info = { parentGuid: rootGuid
- , url: NetUtil.newURI("http://test_create_item.com")
- , index: bmStartIndex
- , title: "Test creating an item" };
-
- ensureUndoState();
- let txn = PT.NewBookmark(bm_info);
- bm_info.guid = yield txn.transact();
-
- let ensureDo = function* (aRedo = false) {
- ensureUndoState([[txn]], 0);
- yield ensureItemsAdded(bm_info);
- if (aRedo)
- ensureTimestampsUpdated(bm_info.guid, true);
- observer.reset();
- };
- let ensureUndo = () => {
- ensureUndoState([[txn]], 1);
- ensureItemsRemoved({ guid: bm_info.guid
- , parentGuid: bm_info.parentGuid
- , index: bmStartIndex });
- observer.reset();
- };
-
- yield ensureDo();
- yield PT.undo();
- ensureUndo();
- yield PT.redo(true);
- yield ensureDo();
- yield PT.undo();
- ensureUndo();
-
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_merge_create_folder_and_item() {
- let folder_info = createTestFolderInfo();
- let bm_info = { url: NetUtil.newURI("http://test_create_item_to_folder.com")
- , title: "Test Bookmark"
- , index: bmStartIndex };
-
- let [folderTxnResult, bkmTxnResult] = yield PT.batch(function* () {
- let folderTxn = PT.NewFolder(folder_info);
- folder_info.guid = bm_info.parentGuid = yield folderTxn.transact();
- let bkmTxn = PT.NewBookmark(bm_info);
- bm_info.guid = yield bkmTxn.transact();
- return [folderTxn, bkmTxn];
- });
-
- let ensureDo = function* () {
- ensureUndoState([[bkmTxnResult, folderTxnResult]], 0);
- yield ensureItemsAdded(folder_info, bm_info);
- observer.reset();
- };
-
- let ensureUndo = () => {
- ensureUndoState([[bkmTxnResult, folderTxnResult]], 1);
- ensureItemsRemoved(folder_info, bm_info);
- observer.reset();
- };
-
- yield ensureDo();
- yield PT.undo();
- ensureUndo();
- yield PT.redo();
- yield ensureDo();
- yield PT.undo();
- ensureUndo();
-
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_move_items_to_folder() {
- let folder_a_info = createTestFolderInfo("Folder A");
- let bkm_a_info = { url: new URL("http://test_move_items.com")
- , title: "Bookmark A" };
- let bkm_b_info = { url: NetUtil.newURI("http://test_move_items.com")
- , title: "Bookmark B" };
-
- // Test moving items within the same folder.
- let [folder_a_txn_result, bkm_a_txn_result, bkm_b_txn_result] = yield PT.batch(function* () {
- let folder_a_txn = PT.NewFolder(folder_a_info);
-
- folder_a_info.guid = bkm_a_info.parentGuid = bkm_b_info.parentGuid =
- yield folder_a_txn.transact();
- let bkm_a_txn = PT.NewBookmark(bkm_a_info);
- bkm_a_info.guid = yield bkm_a_txn.transact();
- let bkm_b_txn = PT.NewBookmark(bkm_b_info);
- bkm_b_info.guid = yield bkm_b_txn.transact();
- return [folder_a_txn, bkm_a_txn, bkm_b_txn];
- });
-
- ensureUndoState([[bkm_b_txn_result, bkm_a_txn_result, folder_a_txn_result]], 0);
-
- let moveTxn = PT.Move({ guid: bkm_a_info.guid
- , newParentGuid: folder_a_info.guid });
- yield moveTxn.transact();
-
- let ensureDo = () => {
- ensureUndoState([[moveTxn], [bkm_b_txn_result, bkm_a_txn_result, folder_a_txn_result]], 0);
- ensureItemsMoved({ guid: bkm_a_info.guid
- , oldParentGuid: folder_a_info.guid
- , newParentGuid: folder_a_info.guid
- , oldIndex: 0
- , newIndex: 1 });
- observer.reset();
- };
- let ensureUndo = () => {
- ensureUndoState([[moveTxn], [bkm_b_txn_result, bkm_a_txn_result, folder_a_txn_result]], 1);
- ensureItemsMoved({ guid: bkm_a_info.guid
- , oldParentGuid: folder_a_info.guid
- , newParentGuid: folder_a_info.guid
- , oldIndex: 1
- , newIndex: 0 });
- observer.reset();
- };
-
- ensureDo();
- yield PT.undo();
- ensureUndo();
- yield PT.redo();
- ensureDo();
- yield PT.undo();
- ensureUndo();
-
- yield PT.clearTransactionsHistory(false, true);
- ensureUndoState([[bkm_b_txn_result, bkm_a_txn_result, folder_a_txn_result]], 0);
-
- // Test moving items between folders.
- let folder_b_info = createTestFolderInfo("Folder B");
- let folder_b_txn = PT.NewFolder(folder_b_info);
- folder_b_info.guid = yield folder_b_txn.transact();
- ensureUndoState([ [folder_b_txn]
- , [bkm_b_txn_result, bkm_a_txn_result, folder_a_txn_result] ], 0);
-
- moveTxn = PT.Move({ guid: bkm_a_info.guid
- , newParentGuid: folder_b_info.guid
- , newIndex: bmsvc.DEFAULT_INDEX });
- yield moveTxn.transact();
-
- ensureDo = () => {
- ensureUndoState([ [moveTxn]
- , [folder_b_txn]
- , [bkm_b_txn_result, bkm_a_txn_result, folder_a_txn_result] ], 0);
- ensureItemsMoved({ guid: bkm_a_info.guid
- , oldParentGuid: folder_a_info.guid
- , newParentGuid: folder_b_info.guid
- , oldIndex: 0
- , newIndex: 0 });
- observer.reset();
- };
- ensureUndo = () => {
- ensureUndoState([ [moveTxn]
- , [folder_b_txn]
- , [bkm_b_txn_result, bkm_a_txn_result, folder_a_txn_result] ], 1);
- ensureItemsMoved({ guid: bkm_a_info.guid
- , oldParentGuid: folder_b_info.guid
- , newParentGuid: folder_a_info.guid
- , oldIndex: 0
- , newIndex: 0 });
- observer.reset();
- };
-
- ensureDo();
- yield PT.undo();
- ensureUndo();
- yield PT.redo();
- ensureDo();
- yield PT.undo();
- ensureUndo();
-
- // Clean up
- yield PT.undo(); // folder_b_txn
- yield PT.undo(); // folder_a_txn + the bookmarks;
- do_check_eq(observer.itemsRemoved.size, 4);
- ensureUndoState([ [moveTxn]
- , [folder_b_txn]
- , [bkm_b_txn_result, bkm_a_txn_result, folder_a_txn_result] ], 3);
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_remove_folder() {
- let folder_level_1_info = createTestFolderInfo("Folder Level 1");
- let folder_level_2_info = { title: "Folder Level 2" };
- let [folder_level_1_txn_result,
- folder_level_2_txn_result] = yield PT.batch(function* () {
- let folder_level_1_txn = PT.NewFolder(folder_level_1_info);
- folder_level_1_info.guid = yield folder_level_1_txn.transact();
- folder_level_2_info.parentGuid = folder_level_1_info.guid;
- let folder_level_2_txn = PT.NewFolder(folder_level_2_info);
- folder_level_2_info.guid = yield folder_level_2_txn.transact();
- return [folder_level_1_txn, folder_level_2_txn];
- });
-
- ensureUndoState([[folder_level_2_txn_result, folder_level_1_txn_result]]);
- yield ensureItemsAdded(folder_level_1_info, folder_level_2_info);
- observer.reset();
-
- let remove_folder_2_txn = PT.Remove(folder_level_2_info);
- yield remove_folder_2_txn.transact();
-
- ensureUndoState([ [remove_folder_2_txn]
- , [folder_level_2_txn_result, folder_level_1_txn_result] ]);
- yield ensureItemsRemoved(folder_level_2_info);
-
- // Undo Remove "Folder Level 2"
- yield PT.undo();
- ensureUndoState([ [remove_folder_2_txn]
- , [folder_level_2_txn_result, folder_level_1_txn_result] ], 1);
- yield ensureItemsAdded(folder_level_2_info);
- ensureTimestampsUpdated(folder_level_2_info.guid, true);
- observer.reset();
-
- // Redo Remove "Folder Level 2"
- yield PT.redo();
- ensureUndoState([ [remove_folder_2_txn]
- , [folder_level_2_txn_result, folder_level_1_txn_result] ]);
- yield ensureItemsRemoved(folder_level_2_info);
- observer.reset();
-
- // Undo it again
- yield PT.undo();
- ensureUndoState([ [remove_folder_2_txn]
- , [folder_level_2_txn_result, folder_level_1_txn_result] ], 1);
- yield ensureItemsAdded(folder_level_2_info);
- ensureTimestampsUpdated(folder_level_2_info.guid, true);
- observer.reset();
-
- // Undo the creation of both folders
- yield PT.undo();
- ensureUndoState([ [remove_folder_2_txn]
- , [folder_level_2_txn_result, folder_level_1_txn_result] ], 2);
- yield ensureItemsRemoved(folder_level_2_info, folder_level_1_info);
- observer.reset();
-
- // Redo the creation of both folders
- yield PT.redo();
- ensureUndoState([ [remove_folder_2_txn]
- , [folder_level_2_txn_result, folder_level_1_txn_result] ], 1);
- yield ensureItemsAdded(folder_level_1_info, folder_level_2_info);
- ensureTimestampsUpdated(folder_level_1_info.guid, true);
- ensureTimestampsUpdated(folder_level_2_info.guid, true);
- observer.reset();
-
- // Redo Remove "Folder Level 2"
- yield PT.redo();
- ensureUndoState([ [remove_folder_2_txn]
- , [folder_level_2_txn_result, folder_level_1_txn_result] ]);
- yield ensureItemsRemoved(folder_level_2_info);
- observer.reset();
-
- // Undo everything one last time
- yield PT.undo();
- ensureUndoState([ [remove_folder_2_txn]
- , [folder_level_2_txn_result, folder_level_1_txn_result] ], 1);
- yield ensureItemsAdded(folder_level_2_info);
- observer.reset();
-
- yield PT.undo();
- ensureUndoState([ [remove_folder_2_txn]
- , [folder_level_2_txn_result, folder_level_1_txn_result] ], 2);
- yield ensureItemsRemoved(folder_level_2_info, folder_level_2_info);
- observer.reset();
-
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_add_and_remove_bookmarks_with_additional_info() {
- const testURI = NetUtil.newURI("http://add.remove.tag");
- const TAG_1 = "TestTag1";
- const TAG_2 = "TestTag2";
- const KEYWORD = "test_keyword";
- const POST_DATA = "post_data";
- const ANNO = { name: "TestAnno", value: "TestAnnoValue" };
-
- let folder_info = createTestFolderInfo();
- folder_info.guid = yield PT.NewFolder(folder_info).transact();
- let ensureTags = ensureTagsForURI.bind(null, testURI);
-
- // Check that the NewBookmark transaction preserves tags.
- observer.reset();
- let b1_info = { parentGuid: folder_info.guid, url: testURI, tags: [TAG_1] };
- b1_info.guid = yield PT.NewBookmark(b1_info).transact();
- ensureTags([TAG_1]);
- yield PT.undo();
- ensureTags([]);
-
- observer.reset();
- yield PT.redo();
- ensureTimestampsUpdated(b1_info.guid, true);
- ensureTags([TAG_1]);
-
- // Check if the Remove transaction removes and restores tags of children
- // correctly.
- yield PT.Remove(folder_info.guid).transact();
- ensureTags([]);
-
- observer.reset();
- yield PT.undo();
- ensureTimestampsUpdated(b1_info.guid, true);
- ensureTags([TAG_1]);
-
- yield PT.redo();
- ensureTags([]);
-
- observer.reset();
- yield PT.undo();
- ensureTimestampsUpdated(b1_info.guid, true);
- ensureTags([TAG_1]);
-
- // * Check that no-op tagging (the uri is already tagged with TAG_1) is
- // also a no-op on undo.
- // * Test the "keyword" property of the NewBookmark transaction.
- observer.reset();
- let b2_info = { parentGuid: folder_info.guid
- , url: testURI, tags: [TAG_1, TAG_2]
- , keyword: KEYWORD
- , postData: POST_DATA
- , annotations: [ANNO] };
- b2_info.guid = yield PT.NewBookmark(b2_info).transact();
- let b2_post_creation_changes = [
- { guid: b2_info.guid
- , isAnnoProperty: true
- , property: ANNO.name
- , newValue: ANNO.value },
- { guid: b2_info.guid
- , property: "keyword"
- , newValue: KEYWORD } ];
- ensureItemsChanged(...b2_post_creation_changes);
- ensureTags([TAG_1, TAG_2]);
-
- observer.reset();
- yield PT.undo();
- yield ensureItemsRemoved(b2_info);
- ensureTags([TAG_1]);
-
- // Check if Remove correctly restores keywords, tags and annotations.
- // Since both bookmarks share the same uri, they also share the keyword that
- // is not removed along with one of the bookmarks.
- observer.reset();
- yield PT.redo();
- ensureItemsChanged({ guid: b2_info.guid
- , isAnnoProperty: true
- , property: ANNO.name
- , newValue: ANNO.value });
- ensureTags([TAG_1, TAG_2]);
-
- // Test Remove for multiple items.
- observer.reset();
- yield PT.Remove(b1_info.guid).transact();
- yield PT.Remove(b2_info.guid).transact();
- yield PT.Remove(folder_info.guid).transact();
- yield ensureItemsRemoved(b1_info, b2_info, folder_info);
- ensureTags([]);
- // There is no keyword removal notification cause all bookmarks are removed
- // before the keyword itself, so there's no one to notify.
- let entry = yield PlacesUtils.keywords.fetch(KEYWORD);
- Assert.equal(entry, null, "keyword has been removed");
-
- observer.reset();
- yield PT.undo();
- yield ensureItemsAdded(folder_info);
- ensureTags([]);
-
- observer.reset();
- yield PT.undo();
- ensureItemsChanged(...b2_post_creation_changes);
- ensureTags([TAG_1, TAG_2]);
-
- observer.reset();
- yield PT.undo();
- yield ensureItemsAdded(b1_info);
- ensureTags([TAG_1, TAG_2]);
-
- // The redo calls below cleanup everything we did.
- observer.reset();
- yield PT.redo();
- yield ensureItemsRemoved(b1_info);
- ensureTags([TAG_1, TAG_2]);
-
- observer.reset();
- yield PT.redo();
- yield ensureItemsRemoved(b2_info);
- ensureTags([]);
-
- observer.reset();
- yield PT.redo();
- yield ensureItemsRemoved(folder_info);
- ensureTags([]);
-
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_creating_and_removing_a_separator() {
- let folder_info = createTestFolderInfo();
- let separator_info = {};
- let undoEntries = [];
-
- observer.reset();
- let create_txns = yield PT.batch(function* () {
- let folder_txn = PT.NewFolder(folder_info);
- folder_info.guid = separator_info.parentGuid = yield folder_txn.transact();
- let separator_txn = PT.NewSeparator(separator_info);
- separator_info.guid = yield separator_txn.transact();
- return [separator_txn, folder_txn];
- });
- undoEntries.unshift(create_txns);
- ensureUndoState(undoEntries, 0);
- ensureItemsAdded(folder_info, separator_info);
-
- observer.reset();
- yield PT.undo();
- ensureUndoState(undoEntries, 1);
- ensureItemsRemoved(folder_info, separator_info);
-
- observer.reset();
- yield PT.redo();
- ensureUndoState(undoEntries, 0);
- ensureItemsAdded(folder_info, separator_info);
-
- observer.reset();
- let remove_sep_txn = PT.Remove(separator_info);
- yield remove_sep_txn.transact();
- undoEntries.unshift([remove_sep_txn]);
- ensureUndoState(undoEntries, 0);
- ensureItemsRemoved(separator_info);
-
- observer.reset();
- yield PT.undo();
- ensureUndoState(undoEntries, 1);
- ensureItemsAdded(separator_info);
-
- observer.reset();
- yield PT.undo();
- ensureUndoState(undoEntries, 2);
- ensureItemsRemoved(folder_info, separator_info);
-
- observer.reset();
- yield PT.redo();
- ensureUndoState(undoEntries, 1);
- ensureItemsAdded(folder_info, separator_info);
-
- // Clear redo entries and check that |redo| does nothing
- observer.reset();
- yield PT.clearTransactionsHistory(false, true);
- undoEntries.shift();
- ensureUndoState(undoEntries, 0);
- yield PT.redo();
- ensureItemsAdded();
- ensureItemsRemoved();
-
- // Cleanup
- observer.reset();
- yield PT.undo();
- ensureUndoState(undoEntries, 1);
- ensureItemsRemoved(folder_info, separator_info);
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_add_and_remove_livemark() {
- let createLivemarkTxn = PT.NewLivemark(
- { feedUrl: NetUtil.newURI("http://test.remove.livemark")
- , parentGuid: rootGuid
- , title: "Test Remove Livemark" });
- let guid = yield createLivemarkTxn.transact();
- let originalInfo = yield PlacesUtils.promiseBookmarksTree(guid);
- Assert.ok(originalInfo);
- yield ensureLivemarkCreatedByAddLivemark(guid);
-
- let removeTxn = PT.Remove(guid);
- yield removeTxn.transact();
- yield ensureNonExistent(guid);
- function* undo() {
- ensureUndoState([[removeTxn], [createLivemarkTxn]], 0);
- yield PT.undo();
- ensureUndoState([[removeTxn], [createLivemarkTxn]], 1);
- yield ensureBookmarksTreeRestoredCorrectly(originalInfo);
- yield PT.undo();
- ensureUndoState([[removeTxn], [createLivemarkTxn]], 2);
- yield ensureNonExistent(guid);
- }
- function* redo() {
- ensureUndoState([[removeTxn], [createLivemarkTxn]], 2);
- yield PT.redo();
- ensureUndoState([[removeTxn], [createLivemarkTxn]], 1);
- yield ensureBookmarksTreeRestoredCorrectly(originalInfo);
- yield PT.redo();
- ensureUndoState([[removeTxn], [createLivemarkTxn]], 0);
- yield ensureNonExistent(guid);
- }
-
- yield undo();
- yield redo();
- yield undo();
- yield redo();
-
- // Cleanup
- yield undo();
- observer.reset();
- yield PT.clearTransactionsHistory();
-});
-
-add_task(function* test_edit_title() {
- let bm_info = { parentGuid: rootGuid
- , url: NetUtil.newURI("http://test_create_item.com")
- , title: "Original Title" };
-
- function ensureTitleChange(aCurrentTitle) {
- ensureItemsChanged({ guid: bm_info.guid
- , property: "title"
- , newValue: aCurrentTitle});
- }
-
- bm_info.guid = yield PT.NewBookmark(bm_info).transact();
-
- observer.reset();
- yield PT.EditTitle({ guid: bm_info.guid, title: "New Title" }).transact();
- ensureTitleChange("New Title");
-
- observer.reset();
- yield PT.undo();
- ensureTitleChange("Original Title");
-
- observer.reset();
- yield PT.redo();
- ensureTitleChange("New Title");
-
- // Cleanup
- observer.reset();
- yield PT.undo();
- ensureTitleChange("Original Title");
- yield PT.undo();
- ensureItemsRemoved(bm_info);
-
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_edit_url() {
- let oldURI = NetUtil.newURI("http://old.test_editing_item_uri.com/");
- let newURI = NetUtil.newURI("http://new.test_editing_item_uri.com/");
- let bm_info = { parentGuid: rootGuid, url: oldURI, tags: ["TestTag"] };
- function ensureURIAndTags(aPreChangeURI, aPostChangeURI, aOLdURITagsPreserved) {
- ensureItemsChanged({ guid: bm_info.guid
- , property: "uri"
- , newValue: aPostChangeURI.spec });
- ensureTagsForURI(aPostChangeURI, bm_info.tags);
- ensureTagsForURI(aPreChangeURI, aOLdURITagsPreserved ? bm_info.tags : []);
- }
-
- bm_info.guid = yield PT.NewBookmark(bm_info).transact();
- ensureTagsForURI(oldURI, bm_info.tags);
-
- // When there's a single bookmark for the same url, tags should be moved.
- observer.reset();
- yield PT.EditUrl({ guid: bm_info.guid, url: newURI }).transact();
- ensureURIAndTags(oldURI, newURI, false);
-
- observer.reset();
- yield PT.undo();
- ensureURIAndTags(newURI, oldURI, false);
-
- observer.reset();
- yield PT.redo();
- ensureURIAndTags(oldURI, newURI, false);
-
- observer.reset();
- yield PT.undo();
- ensureURIAndTags(newURI, oldURI, false);
-
- // When there're multiple bookmarks for the same url, tags should be copied.
- let bm2_info = Object.create(bm_info);
- bm2_info.guid = yield PT.NewBookmark(bm2_info).transact();
- let bm3_info = Object.create(bm_info);
- bm3_info.url = newURI;
- bm3_info.guid = yield PT.NewBookmark(bm3_info).transact();
-
- observer.reset();
- yield PT.EditUrl({ guid: bm_info.guid, url: newURI }).transact();
- ensureURIAndTags(oldURI, newURI, true);
-
- observer.reset();
- yield PT.undo();
- ensureURIAndTags(newURI, oldURI, true);
-
- observer.reset();
- yield PT.redo();
- ensureURIAndTags(oldURI, newURI, true);
-
- // Cleanup
- observer.reset();
- yield PT.undo();
- ensureURIAndTags(newURI, oldURI, true);
- yield PT.undo();
- yield PT.undo();
- yield PT.undo();
- ensureItemsRemoved(bm3_info, bm2_info, bm_info);
-
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_edit_keyword() {
- let bm_info = { parentGuid: rootGuid
- , url: NetUtil.newURI("http://test.edit.keyword") };
- const KEYWORD = "test_keyword";
- bm_info.guid = yield PT.NewBookmark(bm_info).transact();
- function ensureKeywordChange(aCurrentKeyword = "") {
- ensureItemsChanged({ guid: bm_info.guid
- , property: "keyword"
- , newValue: aCurrentKeyword });
- }
-
- bm_info.guid = yield PT.NewBookmark(bm_info).transact();
-
- observer.reset();
- yield PT.EditKeyword({ guid: bm_info.guid, keyword: KEYWORD, postData: "postData" }).transact();
- ensureKeywordChange(KEYWORD);
- let entry = yield PlacesUtils.keywords.fetch(KEYWORD);
- Assert.equal(entry.url.href, bm_info.url.spec);
- Assert.equal(entry.postData, "postData");
-
- observer.reset();
- yield PT.undo();
- ensureKeywordChange();
- entry = yield PlacesUtils.keywords.fetch(KEYWORD);
- Assert.equal(entry, null);
-
- observer.reset();
- yield PT.redo();
- ensureKeywordChange(KEYWORD);
- entry = yield PlacesUtils.keywords.fetch(KEYWORD);
- Assert.equal(entry.url.href, bm_info.url.spec);
- Assert.equal(entry.postData, "postData");
-
- // Cleanup
- observer.reset();
- yield PT.undo();
- ensureKeywordChange();
- yield PT.undo();
- ensureItemsRemoved(bm_info);
-
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_edit_specific_keyword() {
- let bm_info = { parentGuid: rootGuid
- , url: NetUtil.newURI("http://test.edit.keyword") };
- bm_info.guid = yield PT.NewBookmark(bm_info).transact();
- function ensureKeywordChange(aCurrentKeyword = "", aPreviousKeyword = "") {
- ensureItemsChanged({ guid: bm_info.guid
- , property: "keyword"
- , newValue: aCurrentKeyword
- });
- }
-
- yield PlacesUtils.keywords.insert({ keyword: "kw1", url: bm_info.url.spec, postData: "postData1" });
- yield PlacesUtils.keywords.insert({ keyword: "kw2", url: bm_info.url.spec, postData: "postData2" });
- bm_info.guid = yield PT.NewBookmark(bm_info).transact();
-
- observer.reset();
- yield PT.EditKeyword({ guid: bm_info.guid, keyword: "keyword", oldKeyword: "kw2" }).transact();
- ensureKeywordChange("keyword", "kw2");
- let entry = yield PlacesUtils.keywords.fetch("kw1");
- Assert.equal(entry.url.href, bm_info.url.spec);
- Assert.equal(entry.postData, "postData1");
- entry = yield PlacesUtils.keywords.fetch("keyword");
- Assert.equal(entry.url.href, bm_info.url.spec);
- Assert.equal(entry.postData, "postData2");
- entry = yield PlacesUtils.keywords.fetch("kw2");
- Assert.equal(entry, null);
-
- observer.reset();
- yield PT.undo();
- ensureKeywordChange("kw2", "keyword");
- entry = yield PlacesUtils.keywords.fetch("kw1");
- Assert.equal(entry.url.href, bm_info.url.spec);
- Assert.equal(entry.postData, "postData1");
- entry = yield PlacesUtils.keywords.fetch("kw2");
- Assert.equal(entry.url.href, bm_info.url.spec);
- Assert.equal(entry.postData, "postData2");
- entry = yield PlacesUtils.keywords.fetch("keyword");
- Assert.equal(entry, null);
-
- observer.reset();
- yield PT.redo();
- ensureKeywordChange("keyword", "kw2");
- entry = yield PlacesUtils.keywords.fetch("kw1");
- Assert.equal(entry.url.href, bm_info.url.spec);
- Assert.equal(entry.postData, "postData1");
- entry = yield PlacesUtils.keywords.fetch("keyword");
- Assert.equal(entry.url.href, bm_info.url.spec);
- Assert.equal(entry.postData, "postData2");
- entry = yield PlacesUtils.keywords.fetch("kw2");
- Assert.equal(entry, null);
-
- // Cleanup
- observer.reset();
- yield PT.undo();
- ensureKeywordChange("kw2");
- yield PT.undo();
- ensureItemsRemoved(bm_info);
-
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_tag_uri() {
- // This also tests passing uri specs.
- let bm_info_a = { url: "http://bookmarked.uri"
- , parentGuid: rootGuid };
- let bm_info_b = { url: NetUtil.newURI("http://bookmarked2.uri")
- , parentGuid: rootGuid };
- let unbookmarked_uri = NetUtil.newURI("http://un.bookmarked.uri");
-
- function* promiseIsBookmarked(aURI) {
- let deferred = Promise.defer();
- PlacesUtils.asyncGetBookmarkIds(aURI, ids => {
- deferred.resolve(ids.length > 0);
- });
- return deferred.promise;
- }
-
- yield PT.batch(function* () {
- bm_info_a.guid = yield PT.NewBookmark(bm_info_a).transact();
- bm_info_b.guid = yield PT.NewBookmark(bm_info_b).transact();
- });
-
- function* doTest(aInfo) {
- let urls = "url" in aInfo ? [aInfo.url] : aInfo.urls;
- let tags = "tag" in aInfo ? [aInfo.tag] : aInfo.tags;
-
- let ensureURI = url => typeof(url) == "string" ? NetUtil.newURI(url) : url;
- urls = urls.map(ensureURI);
-
- let tagWillAlsoBookmark = new Set();
- for (let url of urls) {
- if (!(yield promiseIsBookmarked(url))) {
- tagWillAlsoBookmark.add(url);
- }
- }
-
- function* ensureTagsSet() {
- for (let url of urls) {
- ensureTagsForURI(url, tags);
- Assert.ok(yield promiseIsBookmarked(url));
- }
- }
- function* ensureTagsUnset() {
- for (let url of urls) {
- ensureTagsForURI(url, []);
- if (tagWillAlsoBookmark.has(url))
- Assert.ok(!(yield promiseIsBookmarked(url)));
- else
- Assert.ok(yield promiseIsBookmarked(url));
- }
- }
-
- yield PT.Tag(aInfo).transact();
- yield ensureTagsSet();
- yield PT.undo();
- yield ensureTagsUnset();
- yield PT.redo();
- yield ensureTagsSet();
- yield PT.undo();
- yield ensureTagsUnset();
- }
-
- yield doTest({ url: bm_info_a.url, tags: ["MyTag"] });
- yield doTest({ urls: [bm_info_a.url], tag: "MyTag" });
- yield doTest({ urls: [bm_info_a.url, bm_info_b.url], tags: ["A, B"] });
- yield doTest({ urls: [bm_info_a.url, unbookmarked_uri], tag: "C" });
-
- // Cleanup
- observer.reset();
- yield PT.undo();
- ensureItemsRemoved(bm_info_a, bm_info_b);
-
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_untag_uri() {
- let bm_info_a = { url: NetUtil.newURI("http://bookmarked.uri")
- , parentGuid: rootGuid
- , tags: ["A", "B"] };
- let bm_info_b = { url: NetUtil.newURI("http://bookmarked2.uri")
- , parentGuid: rootGuid
- , tag: "B" };
-
- yield PT.batch(function* () {
- bm_info_a.guid = yield PT.NewBookmark(bm_info_a).transact();
- ensureTagsForURI(bm_info_a.url, bm_info_a.tags);
- bm_info_b.guid = yield PT.NewBookmark(bm_info_b).transact();
- ensureTagsForURI(bm_info_b.url, [bm_info_b.tag]);
- });
-
- function* doTest(aInfo) {
- let urls, tagsRemoved;
- if (aInfo instanceof Ci.nsIURI) {
- urls = [aInfo];
- tagsRemoved = [];
- }
- else if (Array.isArray(aInfo)) {
- urls = aInfo;
- tagsRemoved = [];
- }
- else {
- urls = "url" in aInfo ? [aInfo.url] : aInfo.urls;
- tagsRemoved = "tag" in aInfo ? [aInfo.tag] : aInfo.tags;
- }
-
- let preRemovalTags = new Map();
- for (let url of urls) {
- preRemovalTags.set(url, tagssvc.getTagsForURI(url));
- }
-
- function ensureTagsSet() {
- for (let url of urls) {
- ensureTagsForURI(url, preRemovalTags.get(url));
- }
- }
- function ensureTagsUnset() {
- for (let url of urls) {
- let expectedTags = tagsRemoved.length == 0 ?
- [] : preRemovalTags.get(url).filter(tag => !tagsRemoved.includes(tag));
- ensureTagsForURI(url, expectedTags);
- }
- }
-
- yield PT.Untag(aInfo).transact();
- yield ensureTagsUnset();
- yield PT.undo();
- yield ensureTagsSet();
- yield PT.redo();
- yield ensureTagsUnset();
- yield PT.undo();
- yield ensureTagsSet();
- }
-
- yield doTest(bm_info_a);
- yield doTest(bm_info_b);
- yield doTest(bm_info_a.url);
- yield doTest(bm_info_b.url);
- yield doTest([bm_info_a.url, bm_info_b.url]);
- yield doTest({ urls: [bm_info_a.url, bm_info_b.url], tags: ["A", "B"] });
- yield doTest({ urls: [bm_info_a.url, bm_info_b.url], tag: "B" });
- yield doTest({ urls: [bm_info_a.url, bm_info_b.url], tag: "C" });
- yield doTest({ urls: [bm_info_a.url, bm_info_b.url], tags: ["C"] });
-
- // Cleanup
- observer.reset();
- yield PT.undo();
- ensureItemsRemoved(bm_info_a, bm_info_b);
-
- yield PT.clearTransactionsHistory();
- ensureUndoState();
-});
-
-add_task(function* test_annotate() {
- let bm_info = { url: NetUtil.newURI("http://test.item.annotation")
- , parentGuid: rootGuid };
- let anno_info = { name: "TestAnno", value: "TestValue" };
- function ensureAnnoState(aSet) {
- ensureAnnotationsSet(bm_info.guid,
- [{ name: anno_info.name
- , value: aSet ? anno_info.value : null }]);
- }
-
- bm_info.guid = yield PT.NewBookmark(bm_info).transact();
-
- observer.reset();
- yield PT.Annotate({ guid: bm_info.guid, annotation: anno_info }).transact();
- ensureAnnoState(true);
-
- observer.reset();
- yield PT.undo();
- ensureAnnoState(false);
-
- observer.reset();
- yield PT.redo();
- ensureAnnoState(true);
-
- // Test removing the annotation by not passing the |value| property.
- observer.reset();
- yield PT.Annotate({ guid: bm_info.guid,
- annotation: { name: anno_info.name }}).transact();
- ensureAnnoState(false);
-
- observer.reset();
- yield PT.undo();
- ensureAnnoState(true);
-
- observer.reset();
- yield PT.redo();
- ensureAnnoState(false);
-
- // Cleanup
- yield PT.undo();
- observer.reset();
-});
-
-add_task(function* test_annotate_multiple() {
- let guid = yield PT.NewFolder(createTestFolderInfo()).transact();
- let itemId = yield PlacesUtils.promiseItemId(guid);
-
- function AnnoObj(aName, aValue) {
- this.name = aName;
- this.value = aValue;
- this.flags = 0;
- this.expires = Ci.nsIAnnotationService.EXPIRE_NEVER;
- }
-
- function annos(a = null, b = null) {
- return [new AnnoObj("A", a), new AnnoObj("B", b)];
- }
-
- function verifyAnnoValues(a = null, b = null) {
- let currentAnnos = PlacesUtils.getAnnotationsForItem(itemId);
- let expectedAnnos = [];
- if (a !== null)
- expectedAnnos.push(new AnnoObj("A", a));
- if (b !== null)
- expectedAnnos.push(new AnnoObj("B", b));
-
- Assert.deepEqual(currentAnnos, expectedAnnos);
- }
-
- yield PT.Annotate({ guid: guid, annotations: annos(1, 2) }).transact();
- verifyAnnoValues(1, 2);
- yield PT.undo();
- verifyAnnoValues();
- yield PT.redo();
- verifyAnnoValues(1, 2);
-
- yield PT.Annotate({ guid: guid
- , annotation: { name: "A" } }).transact();
- verifyAnnoValues(null, 2);
-
- yield PT.Annotate({ guid: guid
- , annotation: { name: "B", value: 0 } }).transact();
- verifyAnnoValues(null, 0);
- yield PT.undo();
- verifyAnnoValues(null, 2);
- yield PT.redo();
- verifyAnnoValues(null, 0);
- yield PT.undo();
- verifyAnnoValues(null, 2);
- yield PT.undo();
- verifyAnnoValues(1, 2);
- yield PT.undo();
- verifyAnnoValues();
-
- // Cleanup
- yield PT.undo();
- observer.reset();
-});
-
-add_task(function* test_sort_folder_by_name() {
- let folder_info = createTestFolderInfo();
-
- let url = NetUtil.newURI("http://sort.by.name/");
- let preSep = ["3", "2", "1"].map(i => ({ title: i, url }));
- let sep = {};
- let postSep = ["c", "b", "a"].map(l => ({ title: l, url }));
- let originalOrder = [...preSep, sep, ...postSep];
- let sortedOrder = [...preSep.slice(0).reverse(),
- sep,
- ...postSep.slice(0).reverse()];
- yield PT.batch(function* () {
- folder_info.guid = yield PT.NewFolder(folder_info).transact();
- for (let info of originalOrder) {
- info.parentGuid = folder_info.guid;
- info.guid = yield info == sep ?
- PT.NewSeparator(info).transact() :
- PT.NewBookmark(info).transact();
- }
- });
-
- let folderId = yield PlacesUtils.promiseItemId(folder_info.guid);
- let folderContainer = PlacesUtils.getFolderContents(folderId).root;
- function ensureOrder(aOrder) {
- for (let i = 0; i < folderContainer.childCount; i++) {
- do_check_eq(folderContainer.getChild(i).bookmarkGuid, aOrder[i].guid);
- }
- }
-
- ensureOrder(originalOrder);
- yield PT.SortByName(folder_info.guid).transact();
- ensureOrder(sortedOrder);
- yield PT.undo();
- ensureOrder(originalOrder);
- yield PT.redo();
- ensureOrder(sortedOrder);
-
- // Cleanup
- observer.reset();
- yield PT.undo();
- ensureOrder(originalOrder);
- yield PT.undo();
- ensureItemsRemoved(...originalOrder, folder_info);
-});
-
-add_task(function* test_livemark_txns() {
- let livemark_info =
- { feedUrl: NetUtil.newURI("http://test.feed.uri")
- , parentGuid: rootGuid
- , title: "Test Livemark" };
- function ensureLivemarkAdded() {
- ensureItemsAdded({ guid: livemark_info.guid
- , title: livemark_info.title
- , parentGuid: livemark_info.parentGuid
- , itemType: bmsvc.TYPE_FOLDER });
- let annos = [{ name: PlacesUtils.LMANNO_FEEDURI
- , value: livemark_info.feedUrl.spec }];
- if ("siteUrl" in livemark_info) {
- annos.push({ name: PlacesUtils.LMANNO_SITEURI
- , value: livemark_info.siteUrl.spec });
- }
- ensureAnnotationsSet(livemark_info.guid, annos);
- }
- function ensureLivemarkRemoved() {
- ensureItemsRemoved({ guid: livemark_info.guid
- , parentGuid: livemark_info.parentGuid });
- }
-
- function* _testDoUndoRedoUndo() {
- observer.reset();
- livemark_info.guid = yield PT.NewLivemark(livemark_info).transact();
- ensureLivemarkAdded();
-
- observer.reset();
- yield PT.undo();
- ensureLivemarkRemoved();
-
- observer.reset();
- yield PT.redo();
- ensureLivemarkAdded();
-
- yield PT.undo();
- ensureLivemarkRemoved();
- }
-
- yield* _testDoUndoRedoUndo()
- livemark_info.siteUrl = NetUtil.newURI("http://feed.site.uri");
- yield* _testDoUndoRedoUndo();
-
- // Cleanup
- observer.reset();
- yield PT.clearTransactionsHistory();
-});
-
-add_task(function* test_copy() {
- function* duplicate_and_test(aOriginalGuid) {
- let txn = PT.Copy({ guid: aOriginalGuid, newParentGuid: rootGuid });
- yield duplicateGuid = yield txn.transact();
- let originalInfo = yield PlacesUtils.promiseBookmarksTree(aOriginalGuid);
- let duplicateInfo = yield PlacesUtils.promiseBookmarksTree(duplicateGuid);
- yield ensureEqualBookmarksTrees(originalInfo, duplicateInfo, false);
-
- function* redo() {
- yield PT.redo();
- yield ensureBookmarksTreeRestoredCorrectly(originalInfo);
- yield PT.redo();
- yield ensureBookmarksTreeRestoredCorrectly(duplicateInfo);
- }
- function* undo() {
- yield PT.undo();
- // also undo the original item addition.
- yield PT.undo();
- yield ensureNonExistent(aOriginalGuid, duplicateGuid);
- }
-
- yield undo();
- yield redo();
- yield undo();
- yield redo();
-
- // Cleanup. This also remove the original item.
- yield PT.undo();
- observer.reset();
- yield PT.clearTransactionsHistory();
- }
-
- // Test duplicating leafs (bookmark, separator, empty folder)
- PT.NewBookmark({ url: new URL("http://test.item.duplicate")
- , parentGuid: rootGuid
- , annos: [{ name: "Anno", value: "AnnoValue"}] });
- let sepTxn = PT.NewSeparator({ parentGuid: rootGuid, index: 1 });
- let livemarkTxn = PT.NewLivemark(
- { feedUrl: new URL("http://test.feed.uri")
- , parentGuid: rootGuid
- , title: "Test Livemark", index: 1 });
- let emptyFolderTxn = PT.NewFolder(createTestFolderInfo());
- for (let txn of [livemarkTxn, sepTxn, emptyFolderTxn]) {
- let guid = yield txn.transact();
- yield duplicate_and_test(guid);
- }
-
- // Test duplicating a folder having some contents.
- let filledFolderGuid = yield PT.batch(function *() {
- let folderGuid = yield PT.NewFolder(createTestFolderInfo()).transact();
- let nestedFolderGuid =
- yield PT.NewFolder({ parentGuid: folderGuid
- , title: "Nested Folder" }).transact();
- // Insert a bookmark under the nested folder.
- yield PT.NewBookmark({ url: new URL("http://nested.nested.bookmark")
- , parentGuid: nestedFolderGuid }).transact();
- // Insert a separator below the nested folder
- yield PT.NewSeparator({ parentGuid: folderGuid }).transact();
- // And another bookmark.
- yield PT.NewBookmark({ url: new URL("http://nested.bookmark")
- , parentGuid: folderGuid }).transact();
- return folderGuid;
- });
-
- yield duplicate_and_test(filledFolderGuid);
-
- // Cleanup
- yield PT.clearTransactionsHistory();
-});
-
-add_task(function* test_array_input_for_batch() {
- let folderTxn = PT.NewFolder(createTestFolderInfo());
- let folderGuid = yield folderTxn.transact();
-
- let sep1_txn = PT.NewSeparator({ parentGuid: folderGuid });
- let sep2_txn = PT.NewSeparator({ parentGuid: folderGuid });
- yield PT.batch([sep1_txn, sep2_txn]);
- ensureUndoState([[sep2_txn, sep1_txn], [folderTxn]], 0);
-
- let ensureChildCount = function* (count) {
- let tree = yield PlacesUtils.promiseBookmarksTree(folderGuid);
- if (count == 0)
- Assert.ok(!("children" in tree));
- else
- Assert.equal(tree.children.length, count);
- };
-
- yield ensureChildCount(2);
- yield PT.undo();
- yield ensureChildCount(0);
- yield PT.redo()
- yield ensureChildCount(2);
- yield PT.undo();
- yield ensureChildCount(0);
-
- yield PT.undo();
- Assert.equal((yield PlacesUtils.promiseBookmarksTree(folderGuid)), null);
-
- // Cleanup
- yield PT.clearTransactionsHistory();
-});
-
-add_task(function* test_copy_excluding_annotations() {
- let folderInfo = createTestFolderInfo();
- let anno = n => { return { name: n, value: 1 } };
- folderInfo.annotations = [anno("a"), anno("b"), anno("c")];
- let folderGuid = yield PT.NewFolder(folderInfo).transact();
-
- let ensureAnnosSet = function* (guid, ...expectedAnnoNames) {
- let tree = yield PlacesUtils.promiseBookmarksTree(guid);
- let annoNames = "annos" in tree ?
- tree.annos.map(a => a.name).sort() : [];
- Assert.deepEqual(annoNames, expectedAnnoNames);
- };
-
- yield ensureAnnosSet(folderGuid, "a", "b", "c");
-
- let excluding_a_dupeGuid =
- yield PT.Copy({ guid: folderGuid
- , newParentGuid: rootGuid
- , excludingAnnotation: "a" }).transact();
- yield ensureAnnosSet(excluding_a_dupeGuid, "b", "c");
-
- let excluding_ac_dupeGuid =
- yield PT.Copy({ guid: folderGuid
- , newParentGuid: rootGuid
- , excludingAnnotations: ["a", "c"] }).transact();
- yield ensureAnnosSet(excluding_ac_dupeGuid, "b");
-
- // Cleanup
- yield PT.undo();
- yield PT.undo();
- yield PT.undo();
- yield PT.clearTransactionsHistory();
-});
-
-add_task(function* test_invalid_uri_spec_throws() {
- Assert.throws(() =>
- PT.NewBookmark({ parentGuid: rootGuid
- , url: "invalid uri spec"
- , title: "test bookmark" }));
- Assert.throws(() =>
- PT.Tag({ tag: "TheTag"
- , urls: ["invalid uri spec"] }));
- Assert.throws(() =>
- PT.Tag({ tag: "TheTag"
- , urls: ["about:blank", "invalid uri spec"] }));
-});
-
-add_task(function* test_annotate_multiple_items() {
- let parentGuid = rootGuid;
- let guids = [
- yield PT.NewBookmark({ url: "about:blank", parentGuid }).transact(),
- yield PT.NewFolder({ title: "Test Folder", parentGuid }).transact()];
-
- let annotation = { name: "TestAnno", value: "TestValue" };
- yield PT.Annotate({ guids, annotation }).transact();
-
- function *ensureAnnoSet() {
- for (let guid of guids) {
- let itemId = yield PlacesUtils.promiseItemId(guid);
- Assert.equal(annosvc.getItemAnnotation(itemId, annotation.name),
- annotation.value);
- }
- }
- function *ensureAnnoUnset() {
- for (let guid of guids) {
- let itemId = yield PlacesUtils.promiseItemId(guid);
- Assert.ok(!annosvc.itemHasAnnotation(itemId, annotation.name));
- }
- }
-
- yield ensureAnnoSet();
- yield PT.undo();
- yield ensureAnnoUnset();
- yield PT.redo();
- yield ensureAnnoSet();
- yield PT.undo();
- yield ensureAnnoUnset();
-
- // Cleanup
- yield PT.undo();
- yield PT.undo();
- yield ensureNonExistent(...guids);
- yield PT.clearTransactionsHistory();
- observer.reset();
-});
-
-add_task(function* test_remove_multiple() {
- let guids = [];
- yield PT.batch(function* () {
- let folderGuid = yield PT.NewFolder({ title: "Test Folder"
- , parentGuid: rootGuid }).transact();
- let nestedFolderGuid =
- yield PT.NewFolder({ title: "Nested Test Folder"
- , parentGuid: folderGuid }).transact();
- yield PT.NewSeparator(nestedFolderGuid).transact();
-
- guids.push(folderGuid);
-
- let bmGuid =
- yield PT.NewBookmark({ url: new URL("http://test.bookmark.removed")
- , parentGuid: rootGuid }).transact();
- guids.push(bmGuid);
- });
-
- let originalInfos = [];
- for (let guid of guids) {
- originalInfos.push(yield PlacesUtils.promiseBookmarksTree(guid));
- }
-
- yield PT.Remove(guids).transact();
- yield ensureNonExistent(...guids);
- yield PT.undo();
- yield ensureBookmarksTreeRestoredCorrectly(...originalInfos);
- yield PT.redo();
- yield ensureNonExistent(...guids);
- yield PT.undo();
- yield ensureBookmarksTreeRestoredCorrectly(...originalInfos);
-
- // Undo the New* transactions batch.
- yield PT.undo();
- yield ensureNonExistent(...guids);
-
- // Redo it.
- yield PT.redo();
- yield ensureBookmarksTreeRestoredCorrectly(...originalInfos);
-
- // Redo remove.
- yield PT.redo();
- yield ensureNonExistent(...guids);
-
- // Cleanup
- yield PT.clearTransactionsHistory();
- observer.reset();
-});
-
-add_task(function* test_remove_bookmarks_for_urls() {
- let urls = [new URL("http://test.url.1"), new URL("http://test.url.2")];
- let guids = [];
- yield PT.batch(function* () {
- for (let url of urls) {
- for (let title of ["test title a", "test title b"]) {
- let txn = PT.NewBookmark({ url, title, parentGuid: rootGuid });
- guids.push(yield txn.transact());
- }
- }
- });
-
- let originalInfos = [];
- for (let guid of guids) {
- originalInfos.push(yield PlacesUtils.promiseBookmarksTree(guid));
- }
-
- yield PT.RemoveBookmarksForUrls(urls).transact();
- yield ensureNonExistent(...guids);
- yield PT.undo();
- yield ensureBookmarksTreeRestoredCorrectly(...originalInfos);
- yield PT.redo();
- yield ensureNonExistent(...guids);
- yield PT.undo();
- yield ensureBookmarksTreeRestoredCorrectly(...originalInfos);
-
- // Cleanup.
- yield PT.redo();
- yield ensureNonExistent(...guids);
- yield PT.clearTransactionsHistory();
- observer.reset();
-});