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