/* 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/. */ /* eslint-disable mozilla/no-arbitrary-setTimeout */ "use strict"; const bookmarksInfo = [ { title: "firefox", url: "http://example.com", }, { title: "rules", url: "http://example.com/2", }, { title: "yo", url: "http://example.com/2", }, ]; /** * Test showing the "Other Bookmarks" folder in the bookmarks toolbar. */ // Setup. add_setup(async function () { // Disable window occlusion. See bug 1733955 / bug 1779559. if (navigator.platform.indexOf("Win") == 0) { await SpecialPowers.pushPrefEnv({ set: [["widget.windows.window_occlusion_tracking.enabled", false]], }); } let toolbar = document.getElementById("PersonalToolbar"); let wasCollapsed = toolbar.collapsed; await setupBookmarksToolbar(); // Cleanup. registerCleanupFunction(async () => { // Collapse the personal toolbar if needed. if (wasCollapsed) { await promiseSetToolbarVisibility(toolbar, false); } await PlacesUtils.bookmarks.eraseEverything(); }); }); // Test the "Other Bookmarks" folder is shown in the toolbar when // bookmarks are stored under that folder. add_task(async function testShowingOtherBookmarksInToolbar() { info("Check the initial state of the Other Bookmarks folder."); let win = await BrowserTestUtils.openNewBrowserWindow(); await setupBookmarksToolbar(win); ok( !win.document.getElementById("OtherBookmarks"), "Shouldn't have an Other Bookmarks button." ); await BrowserTestUtils.closeWindow(win); info("Check visibility of an empty Other Bookmarks folder."); await testIsOtherBookmarksHidden(true); info("Ensure folder appears in toolbar when a new bookmark is added."); let bookmarks = await PlacesUtils.bookmarks.insertTree({ guid: PlacesUtils.bookmarks.unfiledGuid, children: bookmarksInfo, }); await testIsOtherBookmarksHidden(false); info("Ensure folder disappears from toolbar when no bookmarks are present."); await PlacesUtils.bookmarks.remove(bookmarks); await testIsOtherBookmarksHidden(true); }); // Test that folder visibility is correct when moving bookmarks to an empty // "Other Bookmarks" folder and vice versa. add_task(async function testOtherBookmarksVisibilityWhenMovingBookmarks() { info("Add bookmarks to Bookmarks Toolbar."); let bookmarks = await PlacesUtils.bookmarks.insertTree({ guid: PlacesUtils.bookmarks.toolbarGuid, children: bookmarksInfo, }); await testIsOtherBookmarksHidden(true); info("Move toolbar bookmarks to Other Bookmarks folder."); await PlacesUtils.bookmarks.moveToFolder( bookmarks.map(b => b.guid), PlacesUtils.bookmarks.unfiledGuid, PlacesUtils.bookmarks.DEFAULT_INDEX ); await testIsOtherBookmarksHidden(false); info("Move bookmarks from Other Bookmarks back to the toolbar."); await PlacesUtils.bookmarks.moveToFolder( bookmarks.map(b => b.guid), PlacesUtils.bookmarks.toolbarGuid, PlacesUtils.bookmarks.DEFAULT_INDEX ); await testIsOtherBookmarksHidden(true); }); // Test OtherBookmarksPopup in toolbar. add_task(async function testOtherBookmarksMenuPopup() { info("Add bookmarks to Other Bookmarks folder."); let bookmarks = await PlacesUtils.bookmarks.insertTree({ guid: PlacesUtils.bookmarks.unfiledGuid, children: bookmarksInfo, }); await testIsOtherBookmarksHidden(false); info("Check the popup menu has correct number of children."); await openMenuPopup("#OtherBookmarksPopup", "#OtherBookmarks"); testNumberOfMenuPopupChildren("#OtherBookmarksPopup", 3); await closeMenuPopup("#OtherBookmarksPopup"); info("Remove a bookmark."); await PlacesUtils.bookmarks.remove(bookmarks[0]); await openMenuPopup("#OtherBookmarksPopup", "#OtherBookmarks"); testNumberOfMenuPopupChildren("#OtherBookmarksPopup", 2); await closeMenuPopup("#OtherBookmarksPopup"); }); // Test that folders in the Other Bookmarks folder expand add_task(async function testFolderPopup() { await PlacesUtils.bookmarks.insertTree({ guid: PlacesUtils.bookmarks.unfiledGuid, children: [ { title: "folder", type: PlacesUtils.bookmarks.TYPE_FOLDER, children: [ { title: "example", url: "http://example.com/3", }, ], }, ], }); info("Check for popup showing event when folder menuitem is selected."); await openMenuPopup("#OtherBookmarksPopup", "#OtherBookmarks"); await openMenuPopup( "#OtherBookmarksPopup menu menupopup", "#OtherBookmarksPopup menu" ); ok(true, "Folder menu stored in Other Bookmarks expands."); testNumberOfMenuPopupChildren("#OtherBookmarksPopup menu menupopup", 1); await closeMenuPopup("#OtherBookmarksPopup"); }); add_task(async function testOnlyShowOtherFolderInBookmarksToolbar() { await setupBookmarksToolbar(); await PlacesUtils.bookmarks.insertTree({ guid: PlacesUtils.bookmarks.unfiledGuid, children: bookmarksInfo, }); await testIsOtherBookmarksHidden(false); // Test that moving the personal-bookmarks widget out of the // Bookmarks Toolbar will hide the "Other Bookmarks" folder. let widgetId = "personal-bookmarks"; CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR); await testIsOtherBookmarksHidden(true); CustomizableUI.reset(); await testIsOtherBookmarksHidden(false); }); // Test that the menu popup updates when menu items are deleted from it while // it's open. add_task(async function testDeletingMenuItems() { await setupBookmarksToolbar(); await PlacesUtils.bookmarks.insertTree({ guid: PlacesUtils.bookmarks.unfiledGuid, children: bookmarksInfo, }); await testIsOtherBookmarksHidden(false); await openMenuPopup("#OtherBookmarksPopup", "#OtherBookmarks"); testNumberOfMenuPopupChildren("#OtherBookmarksPopup", 3); info("Open context menu for popup."); let placesContext = document.getElementById("placesContext"); let popupEventPromise = BrowserTestUtils.waitForPopupEvent( placesContext, "shown" ); let menuitem = document.querySelector("#OtherBookmarksPopup menuitem"); EventUtils.synthesizeMouseAtCenter(menuitem, { type: "contextmenu" }); await popupEventPromise; info("Delete bookmark menu item from popup."); let deleteMenuBookmark = document.getElementById( "placesContext_deleteBookmark" ); placesContext.activateItem(deleteMenuBookmark); await TestUtils.waitForCondition(() => { let popup = document.querySelector("#OtherBookmarksPopup"); let items = popup.querySelectorAll("menuitem"); return items.length === 2; }, "Failed to delete bookmark menuitem. Expected 2 menu items after deletion."); ok(true, "Menu item was removed from the popup."); await closeMenuPopup("#OtherBookmarksPopup"); }); add_task(async function no_errors_when_bookmarks_placed_in_palette() { CustomizableUI.removeWidgetFromArea("personal-bookmarks"); let consoleErrors = 0; let errorListener = { observe(error) { ok(false, `${error.message}, ${error.stack}, ${JSON.stringify(error)}`); consoleErrors++; }, }; Services.console.registerListener(errorListener); let bookmarks = await PlacesUtils.bookmarks.insertTree({ guid: PlacesUtils.bookmarks.unfiledGuid, children: bookmarksInfo, }); is(consoleErrors, 0, "There should be no console errors"); Services.console.unregisterListener(errorListener); await PlacesUtils.bookmarks.remove(bookmarks); CustomizableUI.reset(); }); // Test "Show Other Bookmarks" menu item visibility in toolbar context menu. add_task(async function testShowingOtherBookmarksContextMenuItem() { await setupBookmarksToolbar(); info("Add bookmark to Other Bookmarks."); let bookmark = await PlacesUtils.bookmarks.insertTree({ guid: PlacesUtils.bookmarks.unfiledGuid, children: [{ title: "firefox", url: "http://example.com" }], }); info("'Show Other Bookmarks' menu item should be checked by default."); await testOtherBookmarksCheckedState(true); info("Toggle off showing the Other Bookmarks folder."); await selectShowOtherBookmarksMenuItem(); await testOtherBookmarksCheckedState(false); await testIsOtherBookmarksHidden(true); info("Toggle on showing the Other Bookmarks folder."); await selectShowOtherBookmarksMenuItem(); await testOtherBookmarksCheckedState(true); await testIsOtherBookmarksHidden(false); info( "Ensure 'Show Other Bookmarks' isn't shown when Other Bookmarks is empty." ); await PlacesUtils.bookmarks.remove(bookmark); await testIsOtherBookmarksMenuItemEnabled(false); info("Add a bookmark to the empty Other Bookmarks folder."); await PlacesUtils.bookmarks.insertTree({ guid: PlacesUtils.bookmarks.unfiledGuid, children: [{ title: "firefox", url: "http://example.com" }], }); await testIsOtherBookmarksMenuItemEnabled(true); info( "Ensure that displaying Other Bookmarks is consistent across separate windows." ); let newWin = await BrowserTestUtils.openNewBrowserWindow(); await TestUtils.waitForCondition(() => { let otherBookmarks = newWin.document.getElementById("OtherBookmarks"); return otherBookmarks && !otherBookmarks.hidden; }, "Other Bookmarks folder failed to show in other window."); info("Hide the Other Bookmarks folder from the original window."); await selectShowOtherBookmarksMenuItem(); await TestUtils.waitForCondition(() => { let otherBookmarks = newWin.document.getElementById("OtherBookmarks"); return !otherBookmarks || otherBookmarks.hidden; }, "Other Bookmarks folder failed to be hidden in other window."); ok(true, "Other Bookmarks was successfully hidden in other window."); info("Show the Other Bookmarks folder from the original window."); await selectShowOtherBookmarksMenuItem(); await TestUtils.waitForCondition(() => { let otherBookmarks = newWin.document.getElementById("OtherBookmarks"); return otherBookmarks && !otherBookmarks.hidden; }, "Other Bookmarks folder failed to be shown in other window."); ok(true, "Other Bookmarks was successfully shown in other window."); await BrowserTestUtils.closeWindow(newWin); }); // Test 'Show Other Bookmarks' isn't shown when pref is false. add_task(async function showOtherBookmarksMenuItemPrefDisabled() { await setupBookmarksToolbar(); await testIsOtherBookmarksMenuItemEnabled(false); }); // Test that node visibility for toolbar overflow is consisten when the "Other Bookmarks" // folder is shown/hidden. add_task(async function testOtherBookmarksToolbarOverFlow() { await setupBookmarksToolbar(); info( "Ensure that visible nodes when showing/hiding Other Bookmarks is consistent across separate windows." ); // Add bookmarks to other bookmarks await PlacesUtils.bookmarks.insertTree({ guid: PlacesUtils.bookmarks.unfiledGuid, children: [{ title: "firefox", url: "http://example.com" }], }); let newWin = await BrowserTestUtils.openNewBrowserWindow(); // Add bookmarks to the toolbar await PlacesUtils.bookmarks.insertTree({ guid: PlacesUtils.bookmarks.toolbarGuid, children: Array(100) .fill("") .map((_, i) => ({ title: `test ${i}`, url: `http:example.com/${i}` })), }); info("Hide the Other Bookmarks folder from the original window."); await selectShowOtherBookmarksMenuItem("#OtherBookmarks"); await BrowserTestUtils.waitForEvent( document.getElementById("PersonalToolbar"), "BookmarksToolbarVisibilityUpdated" ); ok(true, "Nodes successfully updated for both windows."); await testUpdatedNodeVisibility(newWin); info("Show the Other Bookmarks folder from the original window."); await selectShowOtherBookmarksMenuItem("#PlacesChevron"); await BrowserTestUtils.waitForEvent( document.getElementById("PersonalToolbar"), "BookmarksToolbarVisibilityUpdated" ); ok(true, "Nodes successfully updated for both windows."); await testUpdatedNodeVisibility(newWin); await BrowserTestUtils.closeWindow(newWin); }); /** * Tests whether or not the "Other Bookmarks" folder is visible. * * @param {boolean} expected * The expected state of the Other Bookmarks folder. There are 3: * - the folder node isn't initialized and is therefore not visible, * - the folder node is initialized and is hidden * - the folder node is initialized and is visible */ async function testIsOtherBookmarksHidden(expected) { info("Test whether or not the 'Other Bookmarks' folder is visible."); // Ensure the toolbar is visible. let toolbar = document.getElementById("PersonalToolbar"); await promiseSetToolbarVisibility(toolbar, true); let otherBookmarks = document.getElementById("OtherBookmarks"); await TestUtils.waitForCondition(() => { otherBookmarks = document.getElementById("OtherBookmarks"); let isHidden = !otherBookmarks || otherBookmarks.hidden; return isHidden === expected; }, "Other Bookmarks folder failed to change hidden state."); ok(true, `Other Bookmarks folder "hidden" state should be ${expected}.`); } /** * Tests number of menu items in Other Bookmarks popup. * * @param {string} selector * The selector for getting the menupopup element we want to test. * @param {number} expected * The expected number of menuitem elements inside the menupopup. */ function testNumberOfMenuPopupChildren(selector, expected) { let popup = document.querySelector(selector); let items = popup.querySelectorAll("menuitem"); is( items.length, expected, `Number of menu items for ${selector} should be ${expected}.` ); } /** * Test helper for checking the 'checked' state of the "Show Other Bookmarks" menu item * after selecting it from the context menu. * * @param {boolean} expectedCheckedState * Whether or not the menu item is checked. */ async function testOtherBookmarksCheckedState(expectedCheckedState) { info("Check 'Show Other Bookmarks' menu item state"); await openToolbarContextMenu(); let otherBookmarksMenuItem = document.querySelector( "#show-other-bookmarks_PersonalToolbar" ); is( otherBookmarksMenuItem.getAttribute("checked"), `${expectedCheckedState}`, `Other Bookmarks item's checked state should be ${expectedCheckedState}` ); await closeToolbarContextMenu(); } /** * Test helper for checking whether or not the 'Show Other Bookmarks' menu item * is enabled in the toolbar's context menu. * * @param {boolean} expected * Whether or not the menu item is enabled in the toolbar conext menu. */ async function testIsOtherBookmarksMenuItemEnabled(expected) { await openToolbarContextMenu(); let otherBookmarksMenuItem = document.querySelector( "#show-other-bookmarks_PersonalToolbar" ); is( !otherBookmarksMenuItem.disabled, expected, "'Show Other Bookmarks' menu item appearance state is correct." ); await closeToolbarContextMenu(); } /** * Helper for opening a menu popup. * * @param {string} popupSelector * The selector for the menupopup element we want to open. * @param {string} targetSelector * The selector for the element with the popup showing event. */ async function openMenuPopup(popupSelector, targetSelector) { let popup = document.querySelector(popupSelector); let target = document.querySelector(targetSelector); EventUtils.synthesizeMouseAtCenter(target, {}); await BrowserTestUtils.waitForPopupEvent(popup, "shown"); } /** * Helper for closing a menu popup. * * @param {string} popupSelector * The selector for the menupopup element we want to close. */ async function closeMenuPopup(popupSelector) { let popup = document.querySelector(popupSelector); info("Closing menu popup."); popup.hidePopup(); await BrowserTestUtils.waitForPopupEvent(popup, "hidden"); } /** * Helper for opening the toolbar context menu. * * @param {string} toolbarSelector * Optional. The selector for the toolbar context menu. * Defaults to #PlacesToolbarItems. */ async function openToolbarContextMenu(toolbarSelector = "#PlacesToolbarItems") { let contextMenu = document.getElementById("placesContext"); let toolbar = document.querySelector(toolbarSelector); let openToolbarContextMenuPromise = BrowserTestUtils.waitForPopupEvent( contextMenu, "shown" ); // Use the end of the toolbar because the beginning (and even middle, on // some resolutions) might be occluded by the empty toolbar message, which // has a different context menu. let bounds = toolbar.getBoundingClientRect(); EventUtils.synthesizeMouse(toolbar, bounds.width - 5, 5, { type: "contextmenu", }); await openToolbarContextMenuPromise; } /** * Helper for closing the toolbar context menu. */ async function closeToolbarContextMenu() { let contextMenu = document.getElementById("placesContext"); let closeToolbarContextMenuPromise = BrowserTestUtils.waitForPopupEvent( contextMenu, "hidden" ); contextMenu.hidePopup(); await closeToolbarContextMenuPromise; } /** * Helper for setting up the bookmarks toolbar state. This ensures the beginning * of a task will always have the bookmark toolbar in a state that makes the * Other Bookmarks folder testable. * * @param {object} [win] * The window object to use. */ async function setupBookmarksToolbar(win = window) { let toolbar = win.document.getElementById("PersonalToolbar"); let wasCollapsed = toolbar.collapsed; // Uncollapse the personal toolbar if needed. if (wasCollapsed) { await promiseSetToolbarVisibility(toolbar, true); } await PlacesUtils.bookmarks.eraseEverything(); } /** * Helper for selecting the "Show Other Bookmarks" menu item from the bookmarks * toolbar context menu. * * @param {string} selector * Optional. The selector for the node that triggers showing the * "Show Other Bookmarks" context menu item in the toolbar. * Defaults to #PlacesToolbarItem when `openToolbarContextMenu` is * called. */ async function selectShowOtherBookmarksMenuItem(selector) { info("Select 'Show Other Bookmarks' menu item"); await openToolbarContextMenu(selector); let otherBookmarksMenuItem = document.querySelector( "#show-other-bookmarks_PersonalToolbar" ); let contextMenu = document.getElementById("placesContext"); contextMenu.activateItem(otherBookmarksMenuItem); await BrowserTestUtils.waitForPopupEvent(contextMenu, "hidden"); await closeToolbarContextMenu(); } /** * Test helper for node visibility in the bookmarks toolbar between two windows. * * @param {Window} otherWin * The other window whose toolbar items we want to compare with. */ function testUpdatedNodeVisibility(otherWin) { // Get visible toolbar nodes for both the current and other windows. let toolbarItems = document.getElementById("PlacesToolbarItems"); let currentVisibleNodes = []; for (let node of toolbarItems.children) { if (node.style.visibility === "visible") { currentVisibleNodes.push(node); } } let otherToolbarItems = otherWin.document.getElementById("PlacesToolbarItems"); let otherVisibleNodes = []; for (let node of otherToolbarItems.children) { if (node.style.visibility === "visible") { otherVisibleNodes.push(node); } } let lastIdx = otherVisibleNodes.length - 1; is( currentVisibleNodes[lastIdx]?.bookmarkGuid, otherVisibleNodes[lastIdx]?.bookmarkGuid, "Last visible toolbar bookmark is the same in both windows." ); }