diff options
Diffstat (limited to 'toolkit/components/places/tests/bookmarks/test_bookmarks_moveToFolder.js')
-rw-r--r-- | toolkit/components/places/tests/bookmarks/test_bookmarks_moveToFolder.js | 723 |
1 files changed, 723 insertions, 0 deletions
diff --git a/toolkit/components/places/tests/bookmarks/test_bookmarks_moveToFolder.js b/toolkit/components/places/tests/bookmarks/test_bookmarks_moveToFolder.js new file mode 100644 index 0000000000..7cc3eb0916 --- /dev/null +++ b/toolkit/components/places/tests/bookmarks/test_bookmarks_moveToFolder.js @@ -0,0 +1,723 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function ensurePosition(info, parentGuid, index) { + print(`Checking ${info.guid}`); + checkBookmarkObject(info); + Assert.equal( + info.parentGuid, + parentGuid, + "Should be in the correct parent folder" + ); + Assert.equal(info.index, index, "Should have the correct index"); +} + +function insertChildren(folder, items) { + if (!items.length) { + return []; + } + + let children = []; + for (let i = 0; i < items.length; i++) { + if (items[i].type === TYPE_BOOKMARK) { + children.push({ + title: `${i}`, + url: "http://example.com", + }); + } else { + throw new Error(`Type ${items[i].type} is not supported.`); + } + } + return PlacesUtils.bookmarks.insertTree({ + guid: folder.guid, + children, + }); +} + +async function dumpFolderChildren( + folder, + details, + folderA, + folderB, + originalAChildren, + originalBChildren +) { + info(`${folder} Details:`); + info(`Input: ${JSON.stringify(details.initial[folder])}`); + info(`Expected: ${JSON.stringify(details.expected[folder])}`); + info("Index\tOriginal\tExpected\tResult"); + + let originalChildren; + let folderGuid; + if (folder == "folderA") { + originalChildren = originalAChildren; + folderGuid = folderA.guid; + } else { + originalChildren = originalBChildren; + folderGuid = folderB.guid; + } + + let tree = await PlacesUtils.promiseBookmarksTree(folderGuid); + let childrenCount = tree.children ? tree.children.length : 0; + for (let i = 0; i < originalChildren.length || i < childrenCount; i++) { + let originalGuid = + i < originalChildren.length ? originalChildren[i].guid : " "; + let resultGuid = i < childrenCount ? tree.children[i].guid : " "; + let expectedGuid = " "; + if (i < details.expected[folder].length) { + let expected = details.expected[folder][i]; + expectedGuid = + expected.folder == "a" + ? originalAChildren[expected.originalIndex].guid + : originalBChildren[expected.originalIndex].guid; + } + info(`${i}\t${originalGuid}\t${expectedGuid}\t${resultGuid}\n`); + } +} + +async function checkExpectedResults( + details, + folder, + folderGuid, + lastModified, + movedItems, + folderAChildren, + folderBChildren +) { + let expectedResults = details.expected[folder]; + for (let i = 0; i < expectedResults.length; i++) { + let expectedDetails = expectedResults[i]; + let originalItem = + expectedDetails.folder == "a" + ? folderAChildren[expectedDetails.originalIndex] + : folderBChildren[expectedDetails.originalIndex]; + + // Check the item got updated correctly in the database. + let updatedItem = await PlacesUtils.bookmarks.fetch(originalItem.guid); + + ensurePosition(updatedItem, folderGuid, i); + Assert.greaterOrEqual( + updatedItem.lastModified.getTime(), + lastModified.getTime(), + "Last modified should be later or equal to before" + ); + } + + if (details.expected.skipResultIndexChecks) { + return; + } + + // Check the items returned from the actual move() call are correct. + let index = 0; + for (let item of details.initial[folder]) { + if (!("targetFolder" in item)) { + // We weren't moving this item, so skip it and continue. + continue; + } + + let movedItem = movedItems[index]; + let updatedItem = await PlacesUtils.bookmarks.fetch(movedItem.guid); + + ensurePosition(movedItem, updatedItem.parentGuid, updatedItem.index); + + index++; + } +} + +async function checkLastModifiedForFolders(details, folder, movedItems) { + // For the tests, the moves always come from folderA. + if ( + details.initial.folderA.some( + item => "targetFolder" in item && item.targetFolder == folder.title + ) + ) { + let updatedFolder = await PlacesUtils.bookmarks.fetch(folder.guid); + + Assert.greaterOrEqual( + updatedFolder.lastModified.getTime(), + folder.lastModified.getTime(), + "Should have updated the folder's last modified time." + ); + print(JSON.stringify(movedItems[0])); + Assert.deepEqual( + updatedFolder.lastModified, + movedItems[0].lastModified, + "Should have the same last modified as the moved items." + ); + } +} + +async function testMoveToFolder(details) { + await PlacesUtils.bookmarks.eraseEverything(); + + // Always create two folders by default. + let [folderA, folderB] = await PlacesUtils.bookmarks.insertTree({ + guid: PlacesUtils.bookmarks.unfiledGuid, + children: [ + { + title: "a", + type: PlacesUtils.bookmarks.TYPE_FOLDER, + }, + { + title: "b", + type: PlacesUtils.bookmarks.TYPE_FOLDER, + }, + ], + }); + + checkBookmarkObject(folderA); + let folderAChildren = await insertChildren(folderA, details.initial.folderA); + checkBookmarkObject(folderB); + let folderBChildren = await insertChildren(folderB, details.initial.folderB); + + const originalAChildren = folderAChildren.map(child => { + return { ...child }; + }); + const originalBChildren = folderBChildren.map(child => { + return { ...child }; + }); + + let lastModified; + if (folderAChildren.length) { + lastModified = folderAChildren[0].lastModified; + } else if (folderBChildren.length) { + lastModified = folderBChildren[0].lastModified; + } else { + throw new Error("No children added, can't determine lastModified"); + } + + // Work out which children to move and to where. + let childrenToUpdate = []; + for (let i = 0; i < details.initial.folderA.length; i++) { + if ("move" in details.initial.folderA[i]) { + childrenToUpdate.push(folderAChildren[i].guid); + } + } + + let observer; + if (details.notifications) { + observer = expectPlacesObserverNotifications(["bookmark-moved"]); + } + + let movedItems = await PlacesUtils.bookmarks.moveToFolder( + childrenToUpdate, + details.targetFolder == "a" ? folderA.guid : folderB.guid, + details.targetIndex + ); + + await dumpFolderChildren( + "folderA", + details, + folderA, + folderB, + originalAChildren, + originalBChildren + ); + await dumpFolderChildren( + "folderB", + details, + folderA, + folderB, + originalAChildren, + originalBChildren + ); + + Assert.equal(movedItems.length, childrenToUpdate.length); + await checkExpectedResults( + details, + "folderA", + folderA.guid, + lastModified, + movedItems, + folderAChildren, + folderBChildren + ); + await checkExpectedResults( + details, + "folderB", + folderB.guid, + lastModified, + movedItems, + folderAChildren, + folderBChildren + ); + + if (details.notifications) { + let expectedNotifications = []; + + for (let notification of details.notifications) { + let origItem = + notification.originalFolder == "folderA" + ? originalAChildren[notification.originalIndex] + : originalBChildren[notification.originalIndex]; + let newFolder = notification.newFolder == "folderA" ? folderA : folderB; + + expectedNotifications.push({ + type: "bookmark-moved", + id: await PlacesUtils.promiseItemId(origItem.guid), + itemType: PlacesUtils.bookmarks.TYPE_BOOKMARK, + url: origItem.url, + guid: origItem.guid, + parentGuid: newFolder.guid, + source: PlacesUtils.bookmarks.SOURCES.DEFAULT, + index: notification.newIndex, + oldParentGuid: origItem.parentGuid, + oldIndex: notification.originalIndex, + isTagging: false, + }); + } + observer.check(expectedNotifications); + } + + await checkLastModifiedForFolders(details, folderA, movedItems); + await checkLastModifiedForFolders(details, folderB, movedItems); +} + +const TYPE_BOOKMARK = PlacesUtils.bookmarks.TYPE_BOOKMARK; + +add_task(async function invalid_input_throws() { + Assert.throws( + () => PlacesUtils.bookmarks.moveToFolder(), + /guids should be an array/ + ); + Assert.throws( + () => PlacesUtils.bookmarks.moveToFolder({}), + /guids should be an array/ + ); + Assert.throws( + () => PlacesUtils.bookmarks.moveToFolder([]), + /guids should be an array of at least one item/ + ); + + Assert.throws( + () => PlacesUtils.bookmarks.moveToFolder(["test"]), + /Expected only valid GUIDs to be passed/ + ); + Assert.throws( + () => PlacesUtils.bookmarks.moveToFolder([null]), + /Expected only valid GUIDs to be passed/ + ); + Assert.throws( + () => PlacesUtils.bookmarks.moveToFolder([123]), + /Expected only valid GUIDs to be passed/ + ); + + Assert.throws( + () => PlacesUtils.bookmarks.moveToFolder(["123456789012"], 123), + /Error: parentGuid should be a valid GUID/ + ); + + Assert.throws( + () => + PlacesUtils.bookmarks.moveToFolder( + ["123456789012"], + PlacesUtils.bookmarks.rootGuid + ), + /Cannot move bookmarks into root/ + ); + + Assert.throws( + () => + PlacesUtils.bookmarks.moveToFolder(["123456789012"], "123456789012", -2), + /index should be a number greater than/ + ); + Assert.throws( + () => + PlacesUtils.bookmarks.moveToFolder( + ["123456789012"], + "123456789012", + "sdffd" + ), + /index should be a number greater than/ + ); +}); + +add_task(async function test_move_nonexisting_bookmark_rejects() { + await Assert.rejects( + PlacesUtils.bookmarks.moveToFolder(["123456789012"], "123456789012", -1), + /No bookmarks found for the provided GUID/, + "Should reject when moving a non-existing bookmark" + ); +}); + +add_task(async function test_move_folder_into_descendant_rejects() { + let parent = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + }); + let descendant = await PlacesUtils.bookmarks.insert({ + parentGuid: parent.guid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + }); + + await Assert.rejects( + PlacesUtils.bookmarks.moveToFolder([parent.guid], parent.guid, 0), + /Cannot insert a folder into itself or one of its descendants/, + "Should reject when moving a folder into itself" + ); + + await Assert.rejects( + PlacesUtils.bookmarks.moveToFolder([parent.guid], descendant.guid, 0), + /Cannot insert a folder into itself or one of its descendants/, + "Should reject when moving a folder into a descendant" + ); +}); + +add_task(async function test_move_from_differnt_with_no_target_rejects() { + let bm1 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + }); + let bm2 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + }); + + await Assert.rejects( + PlacesUtils.bookmarks.moveToFolder([bm1.guid, bm2.guid], null, -1), + /All bookmarks should be in the same folder if no parent is specified/, + "Should reject when moving bookmarks from different folders with no target folder" + ); +}); + +add_task(async function test_move_append_same_folder() { + await testMoveToFolder({ + initial: { + folderA: [ + { type: TYPE_BOOKMARK }, + { type: TYPE_BOOKMARK, move: true }, + { type: TYPE_BOOKMARK }, + ], + folderB: [], + }, + targetFolder: "a", + targetIndex: -1, + expected: { + folderA: [ + { folder: "a", originalIndex: 0 }, + { folder: "a", originalIndex: 2 }, + { folder: "a", originalIndex: 1 }, + ], + folderB: [], + }, + notifications: [ + { + originalFolder: "folderA", + originalIndex: 1, + newFolder: "folderA", + newIndex: 2, + }, + ], + }); +}); + +add_task(async function test_move_append_multiple_same_folder() { + await testMoveToFolder({ + initial: { + folderA: [ + { type: TYPE_BOOKMARK, move: true }, + { type: TYPE_BOOKMARK, move: true }, + { type: TYPE_BOOKMARK, move: true }, + { type: TYPE_BOOKMARK }, + ], + folderB: [], + }, + targetFolder: "a", + targetIndex: -1, + expected: { + folderA: [ + { folder: "a", originalIndex: 3 }, + { folder: "a", originalIndex: 0 }, + { folder: "a", originalIndex: 1 }, + { folder: "a", originalIndex: 2 }, + ], + folderB: [], + }, + // These are all inserted at position 3 as that's what the views require + // to be notified, to ensure the new items are displayed in their correct + // positions. + notifications: [ + { + originalFolder: "folderA", + originalIndex: 0, + newFolder: "folderA", + newIndex: 3, + }, + { + originalFolder: "folderA", + originalIndex: 1, + newFolder: "folderA", + newIndex: 3, + }, + { + originalFolder: "folderA", + originalIndex: 2, + newFolder: "folderA", + newIndex: 3, + }, + ], + }); +}); + +add_task(async function test_move_append_multiple_new_folder() { + await testMoveToFolder({ + initial: { + folderA: [ + { type: TYPE_BOOKMARK, move: true }, + { type: TYPE_BOOKMARK, move: true }, + { type: TYPE_BOOKMARK, move: true }, + ], + folderB: [], + }, + targetFolder: "b", + targetIndex: -1, + expected: { + folderA: [], + folderB: [ + { folder: "a", originalIndex: 0 }, + { folder: "a", originalIndex: 1 }, + { folder: "a", originalIndex: 2 }, + ], + }, + notifications: [ + { + originalFolder: "folderA", + originalIndex: 0, + newFolder: "folderB", + newIndex: 0, + }, + { + originalFolder: "folderA", + originalIndex: 1, + newFolder: "folderB", + newIndex: 1, + }, + { + originalFolder: "folderA", + originalIndex: 2, + newFolder: "folderB", + newIndex: 2, + }, + ], + }); +}); + +add_task(async function test_move_append_multiple_new_folder_with_existing() { + await testMoveToFolder({ + initial: { + folderA: [ + { type: TYPE_BOOKMARK, move: true }, + { type: TYPE_BOOKMARK, move: true }, + { type: TYPE_BOOKMARK, move: true }, + ], + folderB: [{ type: TYPE_BOOKMARK }, { type: TYPE_BOOKMARK }], + }, + targetFolder: "b", + targetIndex: -1, + expected: { + folderA: [], + folderB: [ + { folder: "b", originalIndex: 0 }, + { folder: "b", originalIndex: 1 }, + { folder: "a", originalIndex: 0 }, + { folder: "a", originalIndex: 1 }, + { folder: "a", originalIndex: 2 }, + ], + }, + notifications: [ + { + originalFolder: "folderA", + originalIndex: 0, + newFolder: "folderB", + newIndex: 2, + }, + { + originalFolder: "folderA", + originalIndex: 1, + newFolder: "folderB", + newIndex: 3, + }, + { + originalFolder: "folderA", + originalIndex: 2, + newFolder: "folderB", + newIndex: 4, + }, + ], + }); +}); + +add_task(async function test_move_insert_same_folder_up() { + await testMoveToFolder({ + initial: { + folderA: [ + { type: TYPE_BOOKMARK }, + { type: TYPE_BOOKMARK }, + { type: TYPE_BOOKMARK, move: true }, + ], + folderB: [], + }, + targetFolder: "a", + targetIndex: 0, + expected: { + folderA: [ + { folder: "a", originalIndex: 2 }, + { folder: "a", originalIndex: 0 }, + { folder: "a", originalIndex: 1 }, + ], + folderB: [], + }, + notifications: [ + { + originalFolder: "folderA", + originalIndex: 2, + newFolder: "folderA", + newIndex: 0, + }, + ], + }); +}); + +add_task(async function test_move_insert_same_folder_down() { + await testMoveToFolder({ + initial: { + folderA: [ + { type: TYPE_BOOKMARK, move: true }, + { type: TYPE_BOOKMARK }, + { type: TYPE_BOOKMARK }, + ], + folderB: [], + }, + targetFolder: "a", + targetIndex: 2, + expected: { + folderA: [ + { folder: "a", originalIndex: 1 }, + { folder: "a", originalIndex: 0 }, + { folder: "a", originalIndex: 2 }, + ], + folderB: [], + }, + notifications: [ + { + originalFolder: "folderA", + originalIndex: 0, + newFolder: "folderA", + newIndex: 1, + }, + ], + }); +}); + +add_task( + async function test_move_insert_multiple_same_folder_split_locations() { + await testMoveToFolder({ + initial: { + folderA: [ + { type: TYPE_BOOKMARK, move: true }, + { type: TYPE_BOOKMARK }, + { type: TYPE_BOOKMARK }, + { type: TYPE_BOOKMARK, move: true }, + { type: TYPE_BOOKMARK }, + { type: TYPE_BOOKMARK }, + { type: TYPE_BOOKMARK, move: true }, + { type: TYPE_BOOKMARK }, + { type: TYPE_BOOKMARK }, + { type: TYPE_BOOKMARK, move: true }, + ], + folderB: [], + }, + targetFolder: "a", + targetIndex: 2, + expected: { + folderA: [ + { folder: "a", originalIndex: 1 }, + { folder: "a", originalIndex: 0 }, + { folder: "a", originalIndex: 3 }, + { folder: "a", originalIndex: 6 }, + { folder: "a", originalIndex: 9 }, + { folder: "a", originalIndex: 2 }, + { folder: "a", originalIndex: 4 }, + { folder: "a", originalIndex: 5 }, + { folder: "a", originalIndex: 7 }, + { folder: "a", originalIndex: 8 }, + ], + folderB: [], + }, + notifications: [ + { + originalFolder: "folderA", + originalIndex: 0, + newFolder: "folderA", + newIndex: 1, + }, + { + originalFolder: "folderA", + originalIndex: 3, + newFolder: "folderA", + newIndex: 2, + }, + { + originalFolder: "folderA", + originalIndex: 6, + newFolder: "folderA", + newIndex: 3, + }, + { + originalFolder: "folderA", + originalIndex: 9, + newFolder: "folderA", + newIndex: 4, + }, + ], + }); + } +); + +add_task(async function test_move_folder_with_descendant() { + let parent = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + }); + let bm = await PlacesUtils.bookmarks.insert({ + parentGuid: parent.guid, + url: "http://example.com/", + type: PlacesUtils.bookmarks.TYPE_BOOKMARK, + }); + let descendant = await PlacesUtils.bookmarks.insert({ + parentGuid: parent.guid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + }); + Assert.equal(descendant.index, 1); + let lastModified = bm.lastModified; + + // This is moving to a nonexisting index by purpose, it will be appended. + [bm] = await PlacesUtils.bookmarks.moveToFolder( + [bm.guid], + descendant.guid, + 1 + ); + checkBookmarkObject(bm); + Assert.equal(bm.parentGuid, descendant.guid); + Assert.equal(bm.index, 0); + Assert.ok(bm.lastModified >= lastModified); + + parent = await PlacesUtils.bookmarks.fetch(parent.guid); + descendant = await PlacesUtils.bookmarks.fetch(descendant.guid); + Assert.deepEqual(parent.lastModified, bm.lastModified); + Assert.deepEqual(descendant.lastModified, bm.lastModified); + Assert.equal(descendant.index, 0); + + bm = await PlacesUtils.bookmarks.fetch(bm.guid); + Assert.equal(bm.parentGuid, descendant.guid); + Assert.equal(bm.index, 0); + + [bm] = await PlacesUtils.bookmarks.moveToFolder([bm.guid], parent.guid, 0); + Assert.equal(bm.parentGuid, parent.guid); + Assert.equal(bm.index, 0); + + descendant = await PlacesUtils.bookmarks.fetch(descendant.guid); + Assert.equal(descendant.index, 1); +}); |