CustomizableUITestUtils.sys.mjs (5354B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 /** 5 * Shared functions generally available for tests involving PanelMultiView and 6 * the CustomizableUI elements in the browser window. 7 */ 8 9 import { Assert } from "resource://testing-common/Assert.sys.mjs"; 10 11 import { BrowserTestUtils } from "resource://testing-common/BrowserTestUtils.sys.mjs"; 12 import { TestUtils } from "resource://testing-common/TestUtils.sys.mjs"; 13 14 const lazy = {}; 15 16 ChromeUtils.defineESModuleGetters(lazy, { 17 CustomizableUI: 18 "moz-src:///browser/components/customizableui/CustomizableUI.sys.mjs", 19 }); 20 21 export class CustomizableUITestUtils { 22 /** 23 * Constructs an instance that operates with the specified browser window. 24 */ 25 constructor(window) { 26 this.window = window; 27 this.document = window.document; 28 this.PanelUI = window.PanelUI; 29 } 30 31 /** 32 * Opens a closed PanelMultiView via the specified function while waiting for 33 * the main view with the specified ID to become fully interactive. 34 */ 35 async openPanelMultiView(panel, mainView, openFn) { 36 if (panel.state == "open") { 37 // Some tests may intermittently leave the panel open. We report this, but 38 // don't fail so we don't introduce new intermittent test failures. 39 Assert.ok( 40 true, 41 "A previous test left the panel open. This should be" + 42 " fixed, but we can still do a best-effort recovery and" + 43 " assume that the requested view will be made visible." 44 ); 45 await openFn(); 46 return; 47 } 48 49 if (panel.state == "hiding") { 50 // There may still be tests that don't wait after invoking a command that 51 // causes the main menu panel to close. Depending on timing, the panel may 52 // or may not be fully closed when the following test runs. We handle this 53 // case gracefully so we don't risk introducing new intermittent test 54 // failures that may show up at a later time. 55 Assert.ok( 56 true, 57 "A previous test requested the panel to close but" + 58 " didn't wait for the operation to complete. While" + 59 " the test should be fixed, we can still continue." 60 ); 61 } else { 62 Assert.equal(panel.state, "closed", "The panel is closed to begin with."); 63 } 64 65 let promiseShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); 66 await openFn(); 67 await promiseShown; 68 } 69 70 /** 71 * Closes an open PanelMultiView via the specified function while waiting for 72 * the operation to complete. 73 */ 74 async hidePanelMultiView(panel, closeFn) { 75 Assert.ok(panel.state == "open", "The panel is open to begin with."); 76 77 let promiseHidden = BrowserTestUtils.waitForEvent(panel, "popuphidden"); 78 await closeFn(); 79 await promiseHidden; 80 } 81 82 /** 83 * Opens the main menu and waits for it to become fully interactive. 84 */ 85 async openMainMenu() { 86 await this.openPanelMultiView( 87 this.PanelUI.panel, 88 this.PanelUI.mainView, 89 () => this.PanelUI.show() 90 ); 91 } 92 93 /** 94 * Closes the main menu and waits for the operation to complete. 95 */ 96 async hideMainMenu() { 97 await this.hidePanelMultiView(this.PanelUI.panel, () => 98 this.PanelUI.hide() 99 ); 100 } 101 102 /** 103 * Add the search bar into the nav bar and verify it does not overflow. 104 * 105 * @returns {Promise} 106 * @resolves The search bar element. 107 * @rejects If search bar is not found, or overflows. 108 */ 109 async addSearchBar() { 110 lazy.CustomizableUI.addWidgetToArea( 111 "search-container", 112 lazy.CustomizableUI.AREA_NAVBAR, 113 lazy.CustomizableUI.getPlacementOfWidget("urlbar-container").position + 1 114 ); 115 116 // addWidgetToArea adds the search bar into the nav bar first. If the 117 // search bar overflows, OverflowableToolbar for the nav bar moves the 118 // search bar into the overflow panel in its overflow event handler 119 // asynchronously. 120 // 121 // We should first wait for the layout flush to make sure either the search 122 // bar fits into the nav bar, or overflow event gets dispatched and the 123 // overflow event handler is called. 124 await this.window.promiseDocumentFlushed(() => {}); 125 126 // Check if the OverflowableToolbar is handling the overflow event. 127 let navbar = this.window.document.getElementById( 128 lazy.CustomizableUI.AREA_NAVBAR 129 ); 130 await TestUtils.waitForCondition(() => { 131 return !navbar.overflowable.isHandlingOverflow(); 132 }); 133 134 let searchbar = this.window.document.getElementById( 135 Services.prefs.getBoolPref("browser.search.widget.new") 136 ? "searchbar-new" 137 : "searchbar" 138 ); 139 if (!searchbar) { 140 throw new Error("The search bar should exist."); 141 } 142 143 // If the search bar overflows, it's placed inside the overflow panel. 144 // 145 // We cannot use navbar's property to check if overflow happens, since it 146 // can be different widget than the search bar that overflows. 147 if (searchbar.closest("#widget-overflow")) { 148 throw new Error( 149 "The search bar should not overflow from the nav bar. " + 150 "This test fails if the screen resolution is small and " + 151 "the search bar overflows from the nav bar." 152 ); 153 } 154 155 return searchbar; 156 } 157 158 removeSearchBar() { 159 lazy.CustomizableUI.removeWidgetFromArea("search-container"); 160 } 161 }