/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; const { AddonTestUtils } = ChromeUtils.importESModule( "resource://testing-common/AddonTestUtils.sys.mjs" ); ChromeUtils.defineESModuleGetters(this, { PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", }); AddonTestUtils.init(this); AddonTestUtils.overrideCertDB(); AddonTestUtils.createAppInfo( "xpcshell@tests.mozilla.org", "XPCShell", "1", "43" ); add_task(async function test_bookmarks() { async function background() { let unsortedId, ourId; let initialBookmarkCount = 0; let createdBookmarks = new Set(); let createdFolderId; let createdSeparatorId; let collectedEvents = []; const nonExistentId = "000000000000"; const bookmarkGuids = { menuGuid: "menu________", toolbarGuid: "toolbar_____", unfiledGuid: "unfiled_____", rootGuid: "root________", }; function checkOurBookmark(bookmark) { browser.test.assertEq(ourId, bookmark.id, "Bookmark has the expected Id"); browser.test.assertTrue( "parentId" in bookmark, "Bookmark has a parentId" ); browser.test.assertEq( 0, bookmark.index, "Bookmark has the expected index" ); // We assume there are no other bookmarks. browser.test.assertEq( "http://example.org/", bookmark.url, "Bookmark has the expected url" ); browser.test.assertEq( "test bookmark", bookmark.title, "Bookmark has the expected title" ); browser.test.assertTrue( "dateAdded" in bookmark, "Bookmark has a dateAdded" ); browser.test.assertFalse( "dateGroupModified" in bookmark, "Bookmark does not have a dateGroupModified" ); browser.test.assertFalse( "unmodifiable" in bookmark, "Bookmark is not unmodifiable" ); browser.test.assertEq( "bookmark", bookmark.type, "Bookmark is of type bookmark" ); } function checkBookmark(expected, bookmark) { browser.test.assertEq( expected.url, bookmark.url, "Bookmark has the expected url" ); browser.test.assertEq( expected.title, bookmark.title, "Bookmark has the expected title" ); browser.test.assertEq( expected.index, bookmark.index, "Bookmark has expected index" ); browser.test.assertEq( "bookmark", bookmark.type, "Bookmark is of type bookmark" ); if ("parentId" in expected) { browser.test.assertEq( expected.parentId, bookmark.parentId, "Bookmark has the expected parentId" ); } } function checkOnCreated( id, parentId, index, title, url, dateAdded, type = "bookmark" ) { let createdData = collectedEvents.pop(); browser.test.assertEq( "onCreated", createdData.event, "onCreated was the last event received" ); browser.test.assertEq( id, createdData.id, "onCreated event received the expected id" ); let bookmark = createdData.bookmark; browser.test.assertEq( id, bookmark.id, "onCreated event received the expected bookmark id" ); browser.test.assertEq( parentId, bookmark.parentId, "onCreated event received the expected bookmark parentId" ); browser.test.assertEq( index, bookmark.index, "onCreated event received the expected bookmark index" ); browser.test.assertEq( title, bookmark.title, "onCreated event received the expected bookmark title" ); browser.test.assertEq( url, bookmark.url, "onCreated event received the expected bookmark url" ); browser.test.assertEq( dateAdded, bookmark.dateAdded, "onCreated event received the expected bookmark dateAdded" ); browser.test.assertEq( type, bookmark.type, "onCreated event received the expected bookmark type" ); } function checkOnChanged(id, url, title) { // If both url and title are changed, then url is fired last. let changedData = collectedEvents.pop(); browser.test.assertEq( "onChanged", changedData.event, "onChanged was the last event received" ); browser.test.assertEq( id, changedData.id, "onChanged event received the expected id" ); browser.test.assertEq( url, changedData.info.url, "onChanged event received the expected url" ); // title is fired first. changedData = collectedEvents.pop(); browser.test.assertEq( "onChanged", changedData.event, "onChanged was the last event received" ); browser.test.assertEq( id, changedData.id, "onChanged event received the expected id" ); browser.test.assertEq( title, changedData.info.title, "onChanged event received the expected title" ); } function checkOnMoved(id, parentId, oldParentId, index, oldIndex) { let movedData = collectedEvents.pop(); browser.test.assertEq( "onMoved", movedData.event, "onMoved was the last event received" ); browser.test.assertEq( id, movedData.id, "onMoved event received the expected id" ); let info = movedData.info; browser.test.assertEq( parentId, info.parentId, "onMoved event received the expected parentId" ); browser.test.assertEq( oldParentId, info.oldParentId, "onMoved event received the expected oldParentId" ); browser.test.assertEq( index, info.index, "onMoved event received the expected index" ); browser.test.assertEq( oldIndex, info.oldIndex, "onMoved event received the expected oldIndex" ); } function checkOnRemoved(id, parentId, index, title, url, type = "folder") { let removedData = collectedEvents.pop(); browser.test.assertEq( "onRemoved", removedData.event, "onRemoved was the last event received" ); browser.test.assertEq( id, removedData.id, "onRemoved event received the expected id" ); let info = removedData.info; browser.test.assertEq( parentId, removedData.info.parentId, "onRemoved event received the expected parentId" ); browser.test.assertEq( index, removedData.info.index, "onRemoved event received the expected index" ); let node = info.node; browser.test.assertEq( id, node.id, "onRemoved event received the expected node id" ); browser.test.assertEq( parentId, node.parentId, "onRemoved event received the expected node parentId" ); browser.test.assertEq( index, node.index, "onRemoved event received the expected node index" ); browser.test.assertEq( url, node.url, "onRemoved event received the expected node url" ); browser.test.assertEq( title, node.title, "onRemoved event received the expected node title" ); browser.test.assertEq( type, node.type, "onRemoved event received the expected node type" ); } browser.bookmarks.onChanged.addListener((id, info) => { collectedEvents.push({ event: "onChanged", id, info }); }); browser.bookmarks.onCreated.addListener((id, bookmark) => { collectedEvents.push({ event: "onCreated", id, bookmark }); }); browser.bookmarks.onMoved.addListener((id, info) => { collectedEvents.push({ event: "onMoved", id, info }); }); browser.bookmarks.onRemoved.addListener((id, info) => { collectedEvents.push({ event: "onRemoved", id, info }); }); await browser.test.assertRejects( browser.bookmarks.get(["not-a-bookmark-guid"]), /Invalid value for property 'guid': "not-a-bookmark-guid"/, "Expected error thrown when trying to get a bookmark using an invalid guid" ); await browser.test .assertRejects( browser.bookmarks.get([nonExistentId]), /Bookmark not found/, "Expected error thrown when trying to get a bookmark using a non-existent Id" ) .then(() => { return browser.bookmarks.search({}); }) .then(results => { initialBookmarkCount = results.length; return browser.bookmarks.create({ title: "test bookmark", url: "http://example.org", type: "bookmark", }); }) .then(result => { ourId = result.id; checkOurBookmark(result); browser.test.assertEq( 1, collectedEvents.length, "1 expected event received" ); checkOnCreated( ourId, bookmarkGuids.unfiledGuid, 0, "test bookmark", "http://example.org/", result.dateAdded ); return browser.bookmarks.get(ourId); }) .then(results => { browser.test.assertEq(results.length, 1); checkOurBookmark(results[0]); unsortedId = results[0].parentId; return browser.bookmarks.get(unsortedId); }) .then(results => { let folder = results[0]; browser.test.assertEq(1, results.length, "1 bookmark was returned"); browser.test.assertEq( unsortedId, folder.id, "Folder has the expected id" ); browser.test.assertTrue("parentId" in folder, "Folder has a parentId"); browser.test.assertTrue("index" in folder, "Folder has an index"); browser.test.assertEq( undefined, folder.url, "Folder does not have a url" ); browser.test.assertEq( "Other Bookmarks", folder.title, "Folder has the expected title" ); browser.test.assertTrue( "dateAdded" in folder, "Folder has a dateAdded" ); browser.test.assertTrue( "dateGroupModified" in folder, "Folder has a dateGroupModified" ); browser.test.assertFalse( "unmodifiable" in folder, "Folder is not unmodifiable" ); // TODO: Do we want to enable this? browser.test.assertEq( "folder", folder.type, "Folder has a type of folder" ); return browser.bookmarks.getChildren(unsortedId); }) .then(async results => { browser.test.assertEq(1, results.length, "The folder has one child"); checkOurBookmark(results[0]); await browser.test.assertRejects( browser.bookmarks.update(nonExistentId, { title: "new test title" }), /No bookmarks found for the provided GUID/, "Expected error thrown when trying to update a non-existent bookmark" ); return browser.bookmarks.update(ourId, { title: "new test title", url: "http://example.com/", }); }) .then(async result => { browser.test.assertEq( "new test title", result.title, "Updated bookmark has the expected title" ); browser.test.assertEq( "http://example.com/", result.url, "Updated bookmark has the expected URL" ); browser.test.assertEq( ourId, result.id, "Updated bookmark has the expected id" ); browser.test.assertEq( "bookmark", result.type, "Updated bookmark has a type of bookmark" ); browser.test.assertEq( 2, collectedEvents.length, "2 expected events received" ); checkOnChanged(ourId, "http://example.com/", "new test title"); await browser.test.assertRejects( browser.bookmarks.update(ourId, { url: "this is not a valid url" }), /Invalid bookmark:/, "Expected error thrown when trying update with an invalid url" ); return browser.bookmarks.getTree(); }) .then(results => { browser.test.assertEq(1, results.length, "getTree returns one result"); let bookmark = results[0].children.find( bookmarkItem => bookmarkItem.id == unsortedId ); browser.test.assertEq( "Other Bookmarks", bookmark.title, "Folder returned from getTree has the expected title" ); browser.test.assertEq( "folder", bookmark.type, "Folder returned from getTree has the expected type" ); return browser.test.assertRejects( browser.bookmarks.create({ parentId: "invalid" }), error => error.message.includes("Invalid bookmark") && error.message.includes(`"parentGuid":"invalid"`), "Expected error thrown when trying to create a bookmark with an invalid parentId" ); }) .then(() => { return browser.bookmarks.remove(ourId); }) .then(result => { browser.test.assertEq( undefined, result, "Removing a bookmark returns undefined" ); browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnRemoved( ourId, bookmarkGuids.unfiledGuid, 0, "new test title", "http://example.com/", "bookmark" ); return browser.test.assertRejects( browser.bookmarks.get(ourId), /Bookmark not found/, "Expected error thrown when trying to get a removed bookmark" ); }) .then(() => { return browser.test.assertRejects( browser.bookmarks.remove(nonExistentId), /No bookmarks found for the provided GUID/, "Expected error thrown when trying removed a non-existent bookmark" ); }) .then(() => { // test bookmarks.search return Promise.all([ browser.bookmarks.create({ title: "Μοζιλλας", url: "http://møzîllä.örg/", }), browser.bookmarks.create({ title: "Example", url: "http://example.org/", }), browser.bookmarks.create({ title: "Mozilla Folder", type: "folder" }), browser.bookmarks.create({ title: "EFF", url: "http://eff.org/" }), browser.bookmarks.create({ title: "Menu Item", url: "http://menu.org/", parentId: bookmarkGuids.menuGuid, }), browser.bookmarks.create({ title: "Toolbar Item", url: "http://toolbar.org/", parentId: bookmarkGuids.toolbarGuid, }), ]); }) .then(results => { browser.test.assertEq( 6, collectedEvents.length, "6 expected events received" ); checkOnCreated( results[5].id, bookmarkGuids.toolbarGuid, 0, "Toolbar Item", "http://toolbar.org/", results[5].dateAdded ); checkOnCreated( results[4].id, bookmarkGuids.menuGuid, 0, "Menu Item", "http://menu.org/", results[4].dateAdded ); checkOnCreated( results[3].id, bookmarkGuids.unfiledGuid, 0, "EFF", "http://eff.org/", results[3].dateAdded ); checkOnCreated( results[2].id, bookmarkGuids.unfiledGuid, 0, "Mozilla Folder", undefined, results[2].dateAdded, "folder" ); checkOnCreated( results[1].id, bookmarkGuids.unfiledGuid, 0, "Example", "http://example.org/", results[1].dateAdded ); checkOnCreated( results[0].id, bookmarkGuids.unfiledGuid, 0, "Μοζιλλας", "http://xn--mzll-ooa1dud.xn--rg-eka/", results[0].dateAdded ); for (let result of results) { if (result.title !== "Mozilla Folder") { createdBookmarks.add(result.id); } } let folderResult = results[2]; createdFolderId = folderResult.id; return Promise.all([ browser.bookmarks.create({ title: "Mozilla", url: "http://allizom.org/", parentId: createdFolderId, }), browser.bookmarks.create({ parentId: createdFolderId, type: "separator", }), browser.bookmarks.create({ title: "Mozilla Corporation", url: "http://allizom.com/", parentId: createdFolderId, }), browser.bookmarks.create({ title: "Firefox", url: "http://allizom.org/firefox/", parentId: createdFolderId, }), ]) .then(newBookmarks => { browser.test.assertEq( 4, collectedEvents.length, "4 expected events received" ); checkOnCreated( newBookmarks[3].id, createdFolderId, 0, "Firefox", "http://allizom.org/firefox/", newBookmarks[3].dateAdded ); checkOnCreated( newBookmarks[2].id, createdFolderId, 0, "Mozilla Corporation", "http://allizom.com/", newBookmarks[2].dateAdded ); checkOnCreated( newBookmarks[1].id, createdFolderId, 0, "", "data:", newBookmarks[1].dateAdded, "separator" ); checkOnCreated( newBookmarks[0].id, createdFolderId, 0, "Mozilla", "http://allizom.org/", newBookmarks[0].dateAdded ); return browser.bookmarks.create({ title: "About Mozilla", url: "http://allizom.org/about/", parentId: createdFolderId, index: 1, }); }) .then(result => { browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnCreated( result.id, createdFolderId, 1, "About Mozilla", "http://allizom.org/about/", result.dateAdded ); // returns all items on empty object return browser.bookmarks.search({}); }) .then(async bookmarksSearchResults => { browser.test.assertTrue( bookmarksSearchResults.length >= 10, "At least as many bookmarks as added were returned by search({})" ); await browser.test.assertRejects( browser.bookmarks.remove(createdFolderId), /Cannot remove a non-empty folder/, "Expected error thrown when trying to remove a non-empty folder" ); return browser.bookmarks.getSubTree(createdFolderId); }); }) .then(results => { browser.test.assertEq( 1, results.length, "Expected number of nodes returned by getSubTree" ); browser.test.assertEq( "Mozilla Folder", results[0].title, "Folder has the expected title" ); browser.test.assertEq( bookmarkGuids.unfiledGuid, results[0].parentId, "Folder has the expected parentId" ); browser.test.assertEq( "folder", results[0].type, "Folder has the expected type" ); let children = results[0].children; browser.test.assertEq( 5, children.length, "Expected number of bookmarks returned by getSubTree" ); browser.test.assertEq( "Firefox", children[0].title, "Bookmark has the expected title" ); browser.test.assertEq( "bookmark", children[0].type, "Bookmark has the expected type" ); browser.test.assertEq( "About Mozilla", children[1].title, "Bookmark has the expected title" ); browser.test.assertEq( "bookmark", children[1].type, "Bookmark has the expected type" ); browser.test.assertEq( 1, children[1].index, "Bookmark has the expected index" ); browser.test.assertEq( "Mozilla Corporation", children[2].title, "Bookmark has the expected title" ); browser.test.assertEq( "", children[3].title, "Separator has the expected title" ); browser.test.assertEq( "data:", children[3].url, "Separator has the expected url" ); browser.test.assertEq( "separator", children[3].type, "Separator has the expected type" ); browser.test.assertEq( "Mozilla", children[4].title, "Bookmark has the expected title" ); // throws an error for invalid query objects browser.test.assertThrows( () => browser.bookmarks.search(), /Incorrect argument types for bookmarks.search/, "Expected error thrown when trying to search with no arguments" ); browser.test.assertThrows( () => browser.bookmarks.search(null), /Incorrect argument types for bookmarks.search/, "Expected error thrown when trying to search with null as an argument" ); browser.test.assertThrows( () => browser.bookmarks.search(() => {}), /Incorrect argument types for bookmarks.search/, "Expected error thrown when trying to search with a function as an argument" ); browser.test.assertThrows( () => browser.bookmarks.search({ banana: "banana" }), /an unexpected "banana" property/, "Expected error thrown when trying to search with a banana as an argument" ); browser.test.assertThrows( () => browser.bookmarks.search({ url: "spider-man vs. batman" }), /must match the format "url"/, "Expected error thrown when trying to search with a illegally formatted URL" ); // queries the full url return browser.bookmarks.search("http://example.org/"); }) .then(results => { browser.test.assertEq( 1, results.length, "Expected number of results returned for url search" ); checkBookmark( { title: "Example", url: "http://example.org/", index: 2 }, results[0] ); // queries a partial url return browser.bookmarks.search("example.org"); }) .then(results => { browser.test.assertEq( 1, results.length, "Expected number of results returned for url search" ); checkBookmark( { title: "Example", url: "http://example.org/", index: 2 }, results[0] ); // queries the title return browser.bookmarks.search("EFF"); }) .then(results => { browser.test.assertEq( 1, results.length, "Expected number of results returned for title search" ); checkBookmark( { title: "EFF", url: "http://eff.org/", index: 0, parentId: bookmarkGuids.unfiledGuid, }, results[0] ); // finds menu items return browser.bookmarks.search("Menu Item"); }) .then(results => { browser.test.assertEq( 1, results.length, "Expected number of results returned for menu item search" ); checkBookmark( { title: "Menu Item", url: "http://menu.org/", index: 0, parentId: bookmarkGuids.menuGuid, }, results[0] ); // finds toolbar items return browser.bookmarks.search("Toolbar Item"); }) .then(results => { browser.test.assertEq( 1, results.length, "Expected number of results returned for toolbar item search" ); checkBookmark( { title: "Toolbar Item", url: "http://toolbar.org/", index: 0, parentId: bookmarkGuids.toolbarGuid, }, results[0] ); // finds folders return browser.bookmarks.search("Mozilla Folder"); }) .then(results => { browser.test.assertEq( 1, results.length, "Expected number of folders returned" ); browser.test.assertEq( "Mozilla Folder", results[0].title, "Folder has the expected title" ); browser.test.assertEq( "folder", results[0].type, "Folder has the expected type" ); // is case-insensitive return browser.bookmarks.search("corporation"); }) .then(results => { browser.test.assertEq( 1, results.length, "Expected number of results returnedfor case-insensitive search" ); browser.test.assertEq( "Mozilla Corporation", results[0].title, "Bookmark has the expected title" ); // is case-insensitive for non-ascii return browser.bookmarks.search("ΜοΖΙΛΛΑς"); }) .then(results => { browser.test.assertEq( 1, results.length, "Expected number of results returned for non-ascii search" ); browser.test.assertEq( "Μοζιλλας", results[0].title, "Bookmark has the expected title" ); // returns multiple results return browser.bookmarks.search("allizom"); }) .then(results => { browser.test.assertEq( 4, results.length, "Expected number of multiple results returned" ); browser.test.assertEq( "Mozilla", results[0].title, "Bookmark has the expected title" ); browser.test.assertEq( "Mozilla Corporation", results[1].title, "Bookmark has the expected title" ); browser.test.assertEq( "Firefox", results[2].title, "Bookmark has the expected title" ); browser.test.assertEq( "About Mozilla", results[3].title, "Bookmark has the expected title" ); // accepts a url field return browser.bookmarks.search({ url: "http://allizom.com/" }); }) .then(results => { browser.test.assertEq( 1, results.length, "Expected number of results returned for url field" ); checkBookmark( { title: "Mozilla Corporation", url: "http://allizom.com/", index: 2, }, results[0] ); // normalizes urls return browser.bookmarks.search({ url: "http://allizom.com" }); }) .then(results => { browser.test.assertEq( results.length, 1, "Expected number of results returned for normalized url field" ); checkBookmark( { title: "Mozilla Corporation", url: "http://allizom.com/", index: 2, }, results[0] ); // normalizes urls even more return browser.bookmarks.search({ url: "http:allizom.com" }); }) .then(results => { browser.test.assertEq( results.length, 1, "Expected number of results returned for normalized url field" ); checkBookmark( { title: "Mozilla Corporation", url: "http://allizom.com/", index: 2, }, results[0] ); // accepts a title field return browser.bookmarks.search({ title: "Mozilla" }); }) .then(results => { browser.test.assertEq( results.length, 1, "Expected number of results returned for title field" ); checkBookmark( { title: "Mozilla", url: "http://allizom.org/", index: 4 }, results[0] ); // can combine title and query return browser.bookmarks.search({ title: "Mozilla", query: "allizom" }); }) .then(results => { browser.test.assertEq( 1, results.length, "Expected number of results returned for title and query fields" ); checkBookmark( { title: "Mozilla", url: "http://allizom.org/", index: 4 }, results[0] ); // uses AND conditions return browser.bookmarks.search({ title: "EFF", query: "allizom" }); }) .then(results => { browser.test.assertEq( 0, results.length, "Expected number of results returned for non-matching title and query fields" ); // returns an empty array on item not found return browser.bookmarks.search("microsoft"); }) .then(results => { browser.test.assertEq( 0, results.length, "Expected number of results returned for non-matching search" ); browser.test.assertThrows( () => browser.bookmarks.getRecent(""), /Incorrect argument types for bookmarks.getRecent/, "Expected error thrown when calling getRecent with an empty string" ); }) .then(() => { browser.test.assertThrows( () => browser.bookmarks.getRecent(1.234), /Incorrect argument types for bookmarks.getRecent/, "Expected error thrown when calling getRecent with a decimal number" ); }) .then(() => { return Promise.all([ browser.bookmarks.search("corporation"), browser.bookmarks.getChildren(bookmarkGuids.menuGuid), ]); }) .then(results => { let corporationBookmark = results[0][0]; let childCount = results[1].length; browser.test.assertEq( 2, corporationBookmark.index, "Bookmark has the expected index" ); return browser.bookmarks .move(corporationBookmark.id, { index: 0 }) .then(result => { browser.test.assertEq( 0, result.index, "Bookmark has the expected index" ); browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnMoved( corporationBookmark.id, createdFolderId, createdFolderId, 0, 2 ); return browser.bookmarks.move(corporationBookmark.id, { parentId: bookmarkGuids.menuGuid, }); }) .then(result => { browser.test.assertEq( bookmarkGuids.menuGuid, result.parentId, "Bookmark has the expected parent" ); browser.test.assertEq( childCount, result.index, "Bookmark has the expected index" ); browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnMoved( corporationBookmark.id, bookmarkGuids.menuGuid, createdFolderId, 1, 0 ); return browser.bookmarks.move(corporationBookmark.id, { index: 0 }); }) .then(result => { browser.test.assertEq( bookmarkGuids.menuGuid, result.parentId, "Bookmark has the expected parent" ); browser.test.assertEq( 0, result.index, "Bookmark has the expected index" ); browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnMoved( corporationBookmark.id, bookmarkGuids.menuGuid, bookmarkGuids.menuGuid, 0, 1 ); return browser.bookmarks.move(corporationBookmark.id, { parentId: bookmarkGuids.toolbarGuid, index: 1, }); }) .then(result => { browser.test.assertEq( bookmarkGuids.toolbarGuid, result.parentId, "Bookmark has the expected parent" ); browser.test.assertEq( 1, result.index, "Bookmark has the expected index" ); browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnMoved( corporationBookmark.id, bookmarkGuids.toolbarGuid, bookmarkGuids.menuGuid, 1, 0 ); createdBookmarks.add(corporationBookmark.id); }); }) .then(() => { return browser.bookmarks.getRecent(4); }) .then(results => { browser.test.assertEq( 4, results.length, "Expected number of results returned by getRecent" ); let prevDate = results[0].dateAdded; for (let bookmark of results) { browser.test.assertTrue( bookmark.dateAdded <= prevDate, "The recent bookmarks are sorted by dateAdded" ); prevDate = bookmark.dateAdded; } let bookmarksByTitle = results.sort((a, b) => { return a.title.localeCompare(b.title); }); browser.test.assertEq( "About Mozilla", bookmarksByTitle[0].title, "Bookmark has the expected title" ); browser.test.assertEq( "Firefox", bookmarksByTitle[1].title, "Bookmark has the expected title" ); browser.test.assertEq( "Mozilla", bookmarksByTitle[2].title, "Bookmark has the expected title" ); browser.test.assertEq( "Mozilla Corporation", bookmarksByTitle[3].title, "Bookmark has the expected title" ); return browser.bookmarks.search({}); }) .then(results => { let startBookmarkCount = results.length; return browser.bookmarks .search({ title: "Mozilla Folder" }) .then(result => { return browser.bookmarks.removeTree(result[0].id); }) .then(() => { browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnRemoved( createdFolderId, bookmarkGuids.unfiledGuid, 1, "Mozilla Folder" ); return browser.bookmarks.search({}).then(searchResults => { browser.test.assertEq( startBookmarkCount - 5, searchResults.length, "Expected number of results returned after removeTree" ); }); }); }) .then(() => { return browser.bookmarks.create({ title: "Empty Folder" }); }) .then(result => { createdFolderId = result.id; browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnCreated( createdFolderId, bookmarkGuids.unfiledGuid, 3, "Empty Folder", undefined, result.dateAdded, "folder" ); browser.test.assertEq( "Empty Folder", result.title, "Folder has the expected title" ); browser.test.assertEq( "folder", result.type, "Folder has the expected type" ); return browser.bookmarks.create({ parentId: createdFolderId, type: "separator", }); }) .then(result => { createdSeparatorId = result.id; browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnCreated( createdSeparatorId, createdFolderId, 0, "", "data:", result.dateAdded, "separator" ); return browser.bookmarks.remove(createdSeparatorId); }) .then(() => { browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnRemoved( createdSeparatorId, createdFolderId, 0, "", "data:", "separator" ); return browser.bookmarks.remove(createdFolderId); }) .then(() => { browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnRemoved( createdFolderId, bookmarkGuids.unfiledGuid, 3, "Empty Folder" ); return browser.test.assertRejects( browser.bookmarks.get(createdFolderId), /Bookmark not found/, "Expected error thrown when trying to get a removed folder" ); }) .then(() => { return browser.test.assertRejects( browser.bookmarks.getChildren(nonExistentId), /root is null/, "Expected error thrown when trying to getChildren for a non-existent folder" ); }) .then(() => { return browser.test.assertRejects( browser.bookmarks.move(nonExistentId, {}), /No bookmarks found for the provided GUID/, "Expected error thrown when calling move with a non-existent bookmark" ); }) .then(() => { return browser.test.assertRejects( browser.bookmarks.create({ title: "test root folder", parentId: bookmarkGuids.rootGuid, }), "The bookmark root cannot be modified", "Expected error thrown when creating bookmark folder at the root" ); }) .then(() => { return browser.test.assertRejects( browser.bookmarks.update(bookmarkGuids.rootGuid, { title: "test update title", }), "The bookmark root cannot be modified", "Expected error thrown when updating root" ); }) .then(() => { return browser.test.assertRejects( browser.bookmarks.remove(bookmarkGuids.rootGuid), "The bookmark root cannot be modified", "Expected error thrown when removing root" ); }) .then(() => { return browser.test.assertRejects( browser.bookmarks.removeTree(bookmarkGuids.rootGuid), "The bookmark root cannot be modified", "Expected error thrown when removing root tree" ); }) .then(() => { return browser.bookmarks.create({ title: "Empty Folder" }); }) .then(async result => { createdFolderId = result.id; browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnCreated( createdFolderId, bookmarkGuids.unfiledGuid, 3, "Empty Folder", undefined, result.dateAdded, "folder" ); await browser.test.assertRejects( browser.bookmarks.move(createdFolderId, { parentId: bookmarkGuids.rootGuid, }), "The bookmark root cannot be modified", "Expected error thrown when moving bookmark folder to the root" ); return browser.bookmarks.remove(createdFolderId); }) .then(() => { browser.test.assertEq( 1, collectedEvents.length, "1 expected events received" ); checkOnRemoved( createdFolderId, bookmarkGuids.unfiledGuid, 3, "Empty Folder", undefined, "folder" ); return browser.test.assertRejects( browser.bookmarks.get(createdFolderId), "Bookmark not found", "Expected error thrown when trying to get a removed folder" ); }) .then(() => { return browser.test.assertRejects( browser.bookmarks.move(bookmarkGuids.rootGuid, { parentId: bookmarkGuids.unfiledGuid, }), "The bookmark root cannot be modified", "Expected error thrown when moving root" ); }) .then(() => { // remove all created bookmarks let promises = Array.from(createdBookmarks, guid => browser.bookmarks.remove(guid) ); return Promise.all(promises); }) .then(() => { browser.test.assertEq( createdBookmarks.size, collectedEvents.length, "expected number of events received" ); return browser.bookmarks.search({}); }) .then(results => { browser.test.assertEq( initialBookmarkCount, results.length, "All created bookmarks have been removed" ); return browser.test.notifyPass("bookmarks"); }) .catch(error => { browser.test.fail(`Error: ${String(error)} :: ${error.stack}`); browser.test.notifyFail("bookmarks"); }); } let extension = ExtensionTestUtils.loadExtension({ background, manifest: { permissions: ["bookmarks"], }, }); await extension.startup(); await extension.awaitFinish("bookmarks"); await extension.unload(); }); add_task(async function test_get_recent_with_tag_and_query() { function background() { browser.bookmarks.getRecent(100).then(bookmarks => { browser.test.sendMessage("bookmarks", bookmarks); }); } let extension = ExtensionTestUtils.loadExtension({ background, manifest: { permissions: ["bookmarks"], }, }); // Start with an empty bookmarks database. await PlacesUtils.bookmarks.eraseEverything(); let createdBookmarks = []; for (let i = 0; i < 3; i++) { let bookmark = { type: PlacesUtils.bookmarks.TYPE_BOOKMARK, url: `http://example.com/${i}`, title: `My bookmark ${i}`, parentGuid: PlacesUtils.bookmarks.unfiledGuid, }; createdBookmarks.unshift(bookmark); await PlacesUtils.bookmarks.insert(bookmark); } // Add a tag to the most recent url to prove it doesn't get returned. PlacesUtils.tagging.tagURI(NetUtil.newURI("http://example.com/${i}"), [ "Test Tag", ]); // Add a query bookmark. let queryURL = `place:parent=${PlacesUtils.bookmarks.menuGuid}&queryType=1`; await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid, url: queryURL, title: "a test query", }); await extension.startup(); let receivedBookmarks = await extension.awaitMessage("bookmarks"); equal( receivedBookmarks.length, 3, "The expected number of bookmarks was returned." ); for (let i = 0; i < 3; i++) { let actual = receivedBookmarks[i]; let expected = createdBookmarks[i]; equal(actual.url, expected.url, "Bookmark has the expected url."); equal(actual.title, expected.title, "Bookmark has the expected title."); equal( actual.parentId, expected.parentGuid, "Bookmark has the expected parentId." ); } await extension.unload(); }); add_task(async function test_tree_with_empty_folder() { async function background() { await browser.bookmarks.create({ title: "Empty Folder" }); let nonEmptyFolder = await browser.bookmarks.create({ title: "Non-Empty Folder", }); await browser.bookmarks.create({ title: "A bookmark", url: "http://example.com", parentId: nonEmptyFolder.id, }); let tree = await browser.bookmarks.getSubTree(nonEmptyFolder.parentId); browser.test.assertEq( 0, tree[0].children[0].children.length, "The empty folder returns an empty array for children." ); browser.test.assertEq( 1, tree[0].children[1].children.length, "The non-empty folder returns a single item array for children." ); let children = await browser.bookmarks.getChildren(nonEmptyFolder.parentId); // getChildren should only return immediate children. This is not tested in the // monster test above. for (let child of children) { browser.test.assertEq( undefined, child.children, "Child from getChildren does not contain any children." ); } browser.test.sendMessage("done"); } let extension = ExtensionTestUtils.loadExtension({ background, manifest: { permissions: ["bookmarks"], }, }); // Start with an empty bookmarks database. await PlacesUtils.bookmarks.eraseEverything(); await extension.startup(); await extension.awaitMessage("done"); await extension.unload(); }); add_task( { pref_set: [["extensions.eventPages.enabled", true]], }, async function test_bookmarks_event_page() { await AddonTestUtils.promiseStartupManager(); let extension = ExtensionTestUtils.loadExtension({ useAddonManager: "permanent", manifest: { browser_specific_settings: { gecko: { id: "eventpage@bookmarks" } }, permissions: ["bookmarks"], background: { persistent: false }, }, background() { browser.bookmarks.onCreated.addListener(() => { browser.test.sendMessage("onCreated"); }); browser.bookmarks.onRemoved.addListener(() => { browser.test.sendMessage("onRemoved"); }); browser.bookmarks.onChanged.addListener(() => {}); browser.bookmarks.onMoved.addListener(() => {}); browser.test.sendMessage("ready"); }, }); const EVENTS = ["onCreated", "onRemoved", "onChanged", "onMoved"]; await PlacesUtils.bookmarks.eraseEverything(); await extension.startup(); await extension.awaitMessage("ready"); for (let event of EVENTS) { assertPersistentListeners(extension, "bookmarks", event, { primed: false, }); } // test events waken background await extension.terminateBackground(); for (let event of EVENTS) { assertPersistentListeners(extension, "bookmarks", event, { primed: true, }); } let bookmark = { type: PlacesUtils.bookmarks.TYPE_BOOKMARK, url: `http://example.com/12345`, title: `My bookmark 12345`, parentGuid: PlacesUtils.bookmarks.unfiledGuid, }; await PlacesUtils.bookmarks.insert(bookmark); await extension.awaitMessage("ready"); await extension.awaitMessage("onCreated"); for (let event of EVENTS) { assertPersistentListeners(extension, "bookmarks", event, { primed: false, }); } await AddonTestUtils.promiseRestartManager(); await extension.awaitStartup(); for (let event of EVENTS) { assertPersistentListeners(extension, "bookmarks", event, { primed: true, }); } await PlacesUtils.bookmarks.eraseEverything(); await extension.awaitMessage("ready"); await extension.awaitMessage("onRemoved"); await extension.unload(); await AddonTestUtils.promiseShutdownManager(); } );