diff options
Diffstat (limited to 'toolkit/components/places/tests/unit/test_promiseBookmarksTree.js')
-rw-r--r-- | toolkit/components/places/tests/unit/test_promiseBookmarksTree.js | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/toolkit/components/places/tests/unit/test_promiseBookmarksTree.js b/toolkit/components/places/tests/unit/test_promiseBookmarksTree.js new file mode 100644 index 000000000..0719a0cd4 --- /dev/null +++ b/toolkit/components/places/tests/unit/test_promiseBookmarksTree.js @@ -0,0 +1,256 @@ +/* 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/. */ + +function* check_has_child(aParentGuid, aChildGuid) { + let parentTree = yield PlacesUtils.promiseBookmarksTree(aParentGuid); + do_check_true("children" in parentTree); + do_check_true(parentTree.children.find( e => e.guid == aChildGuid ) != null); +} + +function* compareToNode(aItem, aNode, aIsRootItem, aExcludedGuids = []) { + // itemId==-1 indicates a non-bookmark node, which is unexpected. + do_check_neq(aNode.itemId, -1); + + function check_unset(...aProps) { + aProps.forEach( p => { do_check_false(p in aItem); } ); + } + function strict_eq_check(v1, v2) { + dump("v1: " + v1 + " v2: " + v2 + "\n"); + do_check_eq(typeof v1, typeof v2); + do_check_eq(v1, v2); + } + function compare_prop(aItemProp, aNodeProp = aItemProp, aOptional = false) { + if (aOptional && aNode[aNodeProp] === null) + check_unset(aItemProp); + else + strict_eq_check(aItem[aItemProp], aNode[aNodeProp]); + } + function compare_prop_to_value(aItemProp, aValue, aOptional = true) { + if (aOptional && aValue === null) + check_unset(aItemProp); + else + strict_eq_check(aItem[aItemProp], aValue); + } + + // Bug 1013053 - bookmarkIndex is unavailable for the query's root + if (aNode.bookmarkIndex == -1) { + compare_prop_to_value("index", + PlacesUtils.bookmarks.getItemIndex(aNode.itemId), + false); + } + else { + compare_prop("index", "bookmarkIndex"); + } + + compare_prop("dateAdded"); + compare_prop("lastModified"); + + if (aIsRootItem && aNode.itemId != PlacesUtils.placesRootId) { + do_check_true("parentGuid" in aItem); + yield check_has_child(aItem.parentGuid, aItem.guid) + } + else { + check_unset("parentGuid"); + } + + let expectedAnnos = PlacesUtils.getAnnotationsForItem(aItem.id); + if (expectedAnnos.length > 0) { + let annosToString = annos => { + return annos.map(a => a.name + ":" + a.value).sort().join(","); + }; + do_check_true(Array.isArray(aItem.annos)) + do_check_eq(annosToString(aItem.annos), annosToString(expectedAnnos)); + } + else { + check_unset("annos"); + } + const BOOKMARK_ONLY_PROPS = ["uri", "iconuri", "tags", "charset", "keyword"]; + const FOLDER_ONLY_PROPS = ["children", "root"]; + + let nodesCount = 1; + + switch (aNode.type) { + case Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER: + do_check_eq(aItem.type, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER); + compare_prop("title", "title", true); + check_unset(...BOOKMARK_ONLY_PROPS); + + let expectedChildrenNodes = []; + + PlacesUtils.asContainer(aNode); + if (!aNode.containerOpen) + aNode.containerOpen = true; + + for (let i = 0; i < aNode.childCount; i++) { + let childNode = aNode.getChild(i); + if (childNode.itemId == PlacesUtils.tagsFolderId || + aExcludedGuids.includes(childNode.bookmarkGuid)) { + continue; + } + expectedChildrenNodes.push(childNode); + } + + if (expectedChildrenNodes.length > 0) { + do_check_true(Array.isArray(aItem.children)); + do_check_eq(aItem.children.length, expectedChildrenNodes.length); + for (let i = 0; i < aItem.children.length; i++) { + nodesCount += + yield compareToNode(aItem.children[i], expectedChildrenNodes[i], + false, aExcludedGuids); + } + } + else { + check_unset("children"); + } + + switch (aItem.id) { + case PlacesUtils.placesRootId: + compare_prop_to_value("root", "placesRoot"); + break; + case PlacesUtils.bookmarksMenuFolderId: + compare_prop_to_value("root", "bookmarksMenuFolder"); + break; + case PlacesUtils.toolbarFolderId: + compare_prop_to_value("root", "toolbarFolder"); + break; + case PlacesUtils.unfiledBookmarksFolderId: + compare_prop_to_value("root", "unfiledBookmarksFolder"); + break; + case PlacesUtils.mobileFolderId: + compare_prop_to_value("root", "mobileFolder"); + break; + default: + check_unset("root"); + } + break; + case Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR: + do_check_eq(aItem.type, PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR); + check_unset(...BOOKMARK_ONLY_PROPS, ...FOLDER_ONLY_PROPS); + break; + default: + do_check_eq(aItem.type, PlacesUtils.TYPE_X_MOZ_PLACE); + compare_prop("uri"); + // node.tags's format is "a, b" whilst promiseBoookmarksTree is "a,b" + if (aNode.tags === null) + check_unset("tags"); + else + compare_prop_to_value("tags", aNode.tags.replace(/, /g, ","), false); + + if (aNode.icon) { + let nodeIconData = aNode.icon.replace("moz-anno:favicon:", ""); + compare_prop_to_value("iconuri", nodeIconData); + } + else { + check_unset(aItem.iconuri); + } + + check_unset(...FOLDER_ONLY_PROPS); + + let itemURI = uri(aNode.uri); + compare_prop_to_value("charset", + yield PlacesUtils.getCharsetForURI(itemURI)); + + let entry = yield PlacesUtils.keywords.fetch({ url: aNode.uri }); + compare_prop_to_value("keyword", entry ? entry.keyword : null); + + if ("title" in aItem) + compare_prop("title"); + else + do_check_null(aNode.title); + } + + if (aIsRootItem) + do_check_eq(aItem.itemsCount, nodesCount); + + return nodesCount; +} + +var itemsCount = 0; +function* new_bookmark(aInfo) { + ++itemsCount; + if (!("url" in aInfo)) + aInfo.url = uri("http://test.item." + itemsCount); + + if (!("title" in aInfo)) + aInfo.title = "Test Item (bookmark) " + itemsCount; + + yield PlacesTransactions.NewBookmark(aInfo).transact(); +} + +function* new_folder(aInfo) { + if (!("title" in aInfo)) + aInfo.title = "Test Item (folder) " + itemsCount; + return yield PlacesTransactions.NewFolder(aInfo).transact(); +} + +// Walks a result nodes tree and test promiseBookmarksTree for each node. +// DO NOT COPY THIS LOGIC: It is done here to accomplish a more comprehensive +// test of the API (the entire hierarchy data is available in the very test). +function* test_promiseBookmarksTreeForEachNode(aNode, aOptions, aExcludedGuids) { + do_check_true(aNode.bookmarkGuid && aNode.bookmarkGuid.length > 0); + let item = yield PlacesUtils.promiseBookmarksTree(aNode.bookmarkGuid, aOptions); + yield* compareToNode(item, aNode, true, aExcludedGuids); + + for (let i = 0; i < aNode.childCount; i++) { + let child = aNode.getChild(i); + if (child.itemId != PlacesUtils.tagsFolderId) + yield test_promiseBookmarksTreeForEachNode(child, + { includeItemIds: true }, + aExcludedGuids); + } + return item; +} + +function* test_promiseBookmarksTreeAgainstResult(aItemGuid = "", + aOptions = { includeItemIds: true }, + aExcludedGuids) { + let itemId = aItemGuid ? + yield PlacesUtils.promiseItemId(aItemGuid) : PlacesUtils.placesRootId; + let node = PlacesUtils.getFolderContents(itemId).root; + return yield test_promiseBookmarksTreeForEachNode(node, aOptions, aExcludedGuids); +} + +add_task(function* () { + // Add some bookmarks to cover various use cases. + yield new_bookmark({ parentGuid: PlacesUtils.bookmarks.toolbarGuid }); + yield new_folder({ parentGuid: PlacesUtils.bookmarks.menuGuid, + annotations: [{ name: "TestAnnoA", value: "TestVal" }, + { name: "TestAnnoB", value: 0 }]}); + let sepInfo = { parentGuid: PlacesUtils.bookmarks.menuGuid }; + yield PlacesTransactions.NewSeparator(sepInfo).transact(); + let folderGuid = yield new_folder({ parentGuid: PlacesUtils.bookmarks.menuGuid }); + yield new_bookmark({ title: null, + parentGuid: folderGuid, + keyword: "test_keyword", + tags: ["TestTagA", "TestTagB"], + annotations: [{ name: "TestAnnoA", value: "TestVal2"}]}); + let urlWithCharsetAndFavicon = uri("http://charset.and.favicon"); + yield new_bookmark({ parentGuid: folderGuid, url: urlWithCharsetAndFavicon }); + yield PlacesUtils.setCharsetForURI(urlWithCharsetAndFavicon, "UTF-8"); + yield promiseSetIconForPage(urlWithCharsetAndFavicon, SMALLPNG_DATA_URI); + // Test the default places root without specifying it. + yield test_promiseBookmarksTreeAgainstResult(); + + // Do specify it + yield test_promiseBookmarksTreeAgainstResult(PlacesUtils.bookmarks.rootGuid); + + // Exclude the bookmarks menu. + // The calllback should be four times - once for the toolbar, once for + // the bookmark we inserted under, and once for the menu (and not + // at all for any of its descendants) and once for the unsorted bookmarks + // folder. However, promiseBookmarksTree is called multiple times, so + // rather than counting the calls, we count the number of unique items + // passed in. + let guidsPassedToExcludeCallback = new Set(); + let placesRootWithoutTheMenu = + yield test_promiseBookmarksTreeAgainstResult(PlacesUtils.bookmarks.rootGuid, { + excludeItemsCallback: aItem => { + guidsPassedToExcludeCallback.add(aItem.guid); + return aItem.root == "bookmarksMenuFolder"; + }, + includeItemIds: true + }, [PlacesUtils.bookmarks.menuGuid]); + do_check_eq(guidsPassedToExcludeCallback.size, 5); + do_check_eq(placesRootWithoutTheMenu.children.length, 3); +}); |