diff options
Diffstat (limited to 'toolkit/components/places/tests/unit/test_promiseBookmarksTree.js')
-rw-r--r-- | toolkit/components/places/tests/unit/test_promiseBookmarksTree.js | 282 |
1 files changed, 282 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 0000000000..9824908cef --- /dev/null +++ b/toolkit/components/places/tests/unit/test_promiseBookmarksTree.js @@ -0,0 +1,282 @@ +/* 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/. */ + +async function check_has_child(aParentGuid, aChildGuid) { + let parentTree = await PlacesUtils.promiseBookmarksTree(aParentGuid); + Assert.ok("children" in parentTree); + Assert.notEqual( + parentTree.children.find(e => e.guid == aChildGuid), + null + ); +} + +async function compareToNode(aItem, aNode, aIsRootItem, aExcludedGuids = []) { + // itemId==-1 indicates a non-bookmark node, which is unexpected. + Assert.notEqual(aNode.itemId, -1); + + function check_unset(...aProps) { + for (let p of aProps) { + if (p in aItem) { + Assert.ok(false, `Unexpected property ${p} with value ${aItem[p]}`); + } + } + } + + function compare_prop(aItemProp, aNodeProp = aItemProp, aOptional = false) { + if (aOptional && aNode[aNodeProp] === null) { + check_unset(aItemProp); + } else { + Assert.strictEqual(aItem[aItemProp], aNode[aNodeProp]); + } + } + + // Bug 1013053 - bookmarkIndex is unavailable for the query's root + if (aNode.bookmarkIndex == -1) { + let bookmark = await PlacesUtils.bookmarks.fetch(aNode.bookmarkGuid); + Assert.strictEqual(aItem.index, bookmark.index); + } else { + compare_prop("index", "bookmarkIndex"); + } + + compare_prop("dateAdded"); + compare_prop("lastModified"); + + if (aIsRootItem && aNode.bookmarkGuid != PlacesUtils.bookmarks.rootGuid) { + Assert.ok("parentGuid" in aItem); + await check_has_child(aItem.parentGuid, aItem.guid); + } else { + check_unset("parentGuid"); + } + + 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: + Assert.equal(aItem.type, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER); + Assert.equal(aItem.typeCode, PlacesUtils.bookmarks.TYPE_FOLDER); + 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) { + Assert.ok(Array.isArray(aItem.children)); + Assert.equal(aItem.children.length, expectedChildrenNodes.length); + for (let i = 0; i < aItem.children.length; i++) { + nodesCount += await compareToNode( + aItem.children[i], + expectedChildrenNodes[i], + false, + aExcludedGuids + ); + } + } else { + check_unset("children"); + } + + let rootName = mapItemGuidToInternalRootName(aItem.guid); + if (rootName) { + Assert.equal(aItem.root, rootName); + } else { + check_unset("root"); + } + break; + case Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR: + Assert.equal(aItem.type, PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR); + Assert.equal(aItem.typeCode, PlacesUtils.bookmarks.TYPE_SEPARATOR); + check_unset(...BOOKMARK_ONLY_PROPS, ...FOLDER_ONLY_PROPS); + break; + default: + Assert.equal(aItem.type, PlacesUtils.TYPE_X_MOZ_PLACE); + Assert.equal(aItem.typeCode, PlacesUtils.bookmarks.TYPE_BOOKMARK); + compare_prop("uri"); + // node.tags's format is "a, b" whilst promiseBoookmarksTree is "a,b" + if (aNode.tags === null) { + check_unset("tags"); + } else { + Assert.equal(aItem.tags, aNode.tags.replace(/, /g, ",")); + } + + if (aNode.icon) { + try { + await compareFavicons(aNode.icon, aItem.iconUri); + } catch (ex) { + info(ex); + todo_check_true(false); + } + } else { + check_unset(aItem.iconUri); + } + + check_unset(...FOLDER_ONLY_PROPS); + + let pageInfo = await PlacesUtils.history.fetch(aNode.uri, { + includeAnnotations: true, + }); + let expectedCharset = pageInfo.annotations.get(PlacesUtils.CHARSET_ANNO); + if (expectedCharset) { + Assert.equal(aItem.charset, expectedCharset); + } else { + check_unset("charset"); + } + + let entry = await PlacesUtils.keywords.fetch({ url: aNode.uri }); + if (entry) { + Assert.equal(aItem.keyword, entry.keyword); + } else { + check_unset("keyword"); + } + + if ("title" in aItem) { + compare_prop("title"); + } else { + Assert.equal(null, aNode.title); + } + } + + if (aIsRootItem) { + Assert.strictEqual(aItem.itemsCount, nodesCount); + } + + return nodesCount; +} + +var itemsCount = 0; +async 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; + } + + await PlacesTransactions.NewBookmark(aInfo).transact(); +} + +function new_folder(aInfo) { + if (!("title" in aInfo)) { + aInfo.title = "Test Item (folder) " + itemsCount; + } + return 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). +async function test_promiseBookmarksTreeForEachNode( + aNode, + aOptions, + aExcludedGuids +) { + Assert.ok(aNode.bookmarkGuid && !!aNode.bookmarkGuid.length); + let item = await PlacesUtils.promiseBookmarksTree( + aNode.bookmarkGuid, + aOptions + ); + await compareToNode(item, aNode, true, aExcludedGuids); + + if (!PlacesUtils.nodeIsContainer(aNode)) { + return item; + } + + for (let i = 0; i < aNode.childCount; i++) { + let child = aNode.getChild(i); + if (child.itemId != PlacesUtils.tagsFolderId) { + await test_promiseBookmarksTreeForEachNode( + child, + { includeItemIds: true }, + aExcludedGuids + ); + } + } + return item; +} + +async function test_promiseBookmarksTreeAgainstResult( + aItemGuid = PlacesUtils.bookmarks.rootGuid, + aOptions = { includeItemIds: true }, + aExcludedGuids +) { + let node = PlacesUtils.getFolderContents(aItemGuid).root; + return test_promiseBookmarksTreeForEachNode(node, aOptions, aExcludedGuids); +} + +add_task(async function () { + // Add some bookmarks to cover various use cases. + await new_bookmark({ parentGuid: PlacesUtils.bookmarks.toolbarGuid }); + await new_folder({ + parentGuid: PlacesUtils.bookmarks.menuGuid, + annotations: [ + { name: "TestAnnoA", value: "TestVal" }, + { name: "TestAnnoB", value: 0 }, + ], + }); + let sepInfo = { parentGuid: PlacesUtils.bookmarks.menuGuid }; + await PlacesTransactions.NewSeparator(sepInfo).transact(); + let folderGuid = await new_folder({ + parentGuid: PlacesUtils.bookmarks.menuGuid, + }); + await new_bookmark({ + title: null, + parentGuid: folderGuid, + keyword: "test_keyword", + tags: ["TestTagA", "TestTagB"], + annotations: [{ name: "TestAnnoA", value: "TestVal2" }], + }); + let urlWithCharsetAndFavicon = uri("http://charset.and.favicon"); + await new_bookmark({ parentGuid: folderGuid, url: urlWithCharsetAndFavicon }); + await PlacesUtils.history.update({ + url: urlWithCharsetAndFavicon, + annotations: new Map([[PlacesUtils.CHARSET_ANNO, "UTF-16"]]), + }); + await setFaviconForPage(urlWithCharsetAndFavicon, SMALLPNG_DATA_URI); + // Test the default places root without specifying it. + await test_promiseBookmarksTreeAgainstResult(); + + // Do specify it + await 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 = await test_promiseBookmarksTreeAgainstResult( + PlacesUtils.bookmarks.rootGuid, + { + excludeItemsCallback: aItem => { + guidsPassedToExcludeCallback.add(aItem.guid); + return aItem.root == "bookmarksMenuFolder"; + }, + includeItemIds: true, + }, + [PlacesUtils.bookmarks.menuGuid] + ); + Assert.equal(guidsPassedToExcludeCallback.size, 5); + Assert.equal(placesRootWithoutTheMenu.children.length, 3); +}); |