summaryrefslogtreecommitdiffstats
path: root/toolkit/components/places/tests/bookmarks/test_bookmarks_moveToFolder.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/places/tests/bookmarks/test_bookmarks_moveToFolder.js')
-rw-r--r--toolkit/components/places/tests/bookmarks/test_bookmarks_moveToFolder.js723
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);
+});