browser_aboutdebugging_addons_debug_reload.js (9256B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 "use strict"; 4 5 /* import-globals-from helper-addons.js */ 6 Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-addons.js", this); 7 8 // There are shutdown issues for which multiple rejections are left uncaught. 9 // See bug 1018184 for resolving these issues. 10 const { PromiseTestUtils } = ChromeUtils.importESModule( 11 "resource://testing-common/PromiseTestUtils.sys.mjs" 12 ); 13 PromiseTestUtils.allowMatchingRejectionsGlobally(/File closed/); 14 15 const { AddonTestUtils } = ChromeUtils.importESModule( 16 "resource://testing-common/AddonTestUtils.sys.mjs" 17 ); 18 AddonTestUtils.initMochitest(this); 19 20 // Avoid test timeouts that can occur while waiting for the "addon-console-works" message. 21 requestLongerTimeout(2); 22 23 const ADDON_ID = "test-devtools-webextension@mozilla.org"; 24 const ADDON_NAME = "test-devtools-webextension"; 25 26 const L10N = new LocalizationHelper( 27 "devtools/client/locales/toolbox.properties" 28 ); 29 30 // When an extension with sidebar_action gets installed, the sidebar launcher may open. 31 // Ensure it gets closed to avoid side-effects in later tests. 32 const initialSidebarState = { ...SidebarController.getUIState(), command: "" }; 33 registerCleanupFunction(async function () { 34 info("Restoring to initial sidebar state"); 35 await SidebarController.initializeUIState(initialSidebarState); 36 }); 37 38 // Check that addon browsers can be reloaded via the toolbox reload shortcuts 39 add_task(async function testWebExtensionToolboxReload() { 40 await enableExtensionDebugging(); 41 const { document, tab, window } = await openAboutDebugging(); 42 await selectThisFirefoxPage(document, window.AboutDebugging.store); 43 44 // Build and install the add-on manually (without installTemporaryExtensionFromXPI) 45 // in order to be able to make changes to the add-on files before reloads 46 const manifest = JSON.stringify({ 47 manifest_version: 2, 48 version: "1.0", 49 name: ADDON_NAME, 50 background: { 51 scripts: ["background.js"], 52 }, 53 sidebar_action: { 54 default_title: "Sidebar", 55 default_icon: { 56 64: "icon.png", 57 }, 58 default_panel: "sidebar.html", 59 }, 60 }); 61 const files = { 62 "manifest.json": manifest, 63 "background.js": ` 64 fetch("data:text/html,fooo"); 65 console.log("background script executed " + Math.random()); 66 throw new Error("background exception") 67 `, 68 "sidebar.html": `<!DOCTYPE html> 69 <html> 70 <head> 71 <meta charset="utf-8"> 72 </head> 73 <body> 74 Sidebar 75 </body> 76 </html> 77 `, 78 }; 79 const tempAddonDir = AddonTestUtils.tempDir.clone(); 80 const addonDir = await AddonTestUtils.promiseWriteFilesToExtension( 81 tempAddonDir.path, 82 ADDON_ID, 83 files, 84 true 85 ); 86 87 const addon = await AddonManager.installTemporaryAddon(addonDir); 88 89 // Select the debugger right away to avoid any noise coming from the inspector. 90 await pushPref("devtools.toolbox.selectedTool", "webconsole"); 91 const { devtoolsDocument, devtoolsWindow } = await openAboutDevtoolsToolbox( 92 document, 93 tab, 94 window, 95 ADDON_NAME 96 ); 97 const toolbox = getToolbox(devtoolsWindow); 98 99 ok( 100 devtoolsDocument.querySelector(".qa-reload-button"), 101 "Reload button is visible" 102 ); 103 ok( 104 !devtoolsDocument.querySelector(".qa-back-button"), 105 "Back button is hidden" 106 ); 107 ok( 108 !devtoolsDocument.querySelector(".qa-forward-button"), 109 "Forward button is hidden" 110 ); 111 ok( 112 !devtoolsDocument.querySelector(".debug-target-url-form"), 113 "URL form is hidden" 114 ); 115 ok( 116 devtoolsDocument.getElementById("toolbox-meatball-menu-noautohide"), 117 "Disable popup autohide button is displayed" 118 ); 119 ok( 120 !devtoolsDocument.getElementById( 121 "toolbox-meatball-menu-pseudo-locale-accented" 122 ), 123 "Accented locale is not displayed (only on browser toolbox)" 124 ); 125 126 // Ensure opening the netmonitor to handle requests before reloading 127 await toolbox.selectTool("netmonitor"); 128 129 const webconsole = await toolbox.selectTool("webconsole"); 130 const { hud } = webconsole; 131 132 info("Wait for the initial background message to appear in the console"); 133 const initialMessage = await waitFor(() => 134 findMessageByType(hud, "background script executed", ".console-api") 135 ); 136 ok(initialMessage, "Found the expected message from the background script"); 137 138 await waitFor( 139 () => findMessageByType(hud, "background exception", ".error"), 140 "Found the background page exception" 141 ); 142 143 let loadedTargets = 0; 144 await toolbox.commands.resourceCommand.watchResources( 145 [toolbox.commands.resourceCommand.TYPES.DOCUMENT_EVENT], 146 { 147 ignoreExistingResources: true, 148 onAvailable(resources) { 149 for (const resource of resources) { 150 if (resource.name == "dom-complete") { 151 loadedTargets++; 152 } 153 } 154 }, 155 } 156 ); 157 158 info("Reload the addon using a toolbox reload shortcut"); 159 toolbox.win.focus(); 160 synthesizeKeyShortcut(L10N.getStr("toolbox.reload.key"), toolbox.win); 161 162 info("Wait until a new background log message is logged"); 163 const secondMessage = await waitFor(() => { 164 const newMessage = findMessageByType( 165 hud, 166 "background script executed", 167 ".console-api" 168 ); 169 if (newMessage && newMessage !== initialMessage) { 170 return newMessage; 171 } 172 return false; 173 }); 174 175 await waitFor( 176 () => loadedTargets == 2, 177 "Wait for background and popup targets to be reloaded" 178 ); 179 let menuList = toolbox.doc.getElementById("toolbox-frame-menu"); 180 await waitFor( 181 () => menuList.querySelectorAll(".command").length == 3, 182 "Wait for fallback, background and sidebar documents to visible in the iframe dropdown" 183 ); 184 185 info("Reload via the debug target info bar button"); 186 loadedTargets = 0; 187 clickReload(devtoolsDocument); 188 189 info("Wait until yet another background log message is logged"); 190 await waitFor(() => { 191 const newMessage = findMessageByType( 192 hud, 193 "background script executed", 194 ".console-api" 195 ); 196 return ( 197 newMessage && 198 newMessage !== initialMessage && 199 newMessage !== secondMessage 200 ); 201 }); 202 203 await waitFor( 204 () => loadedTargets == 2, 205 "Wait for background and popup targets to be reloaded" 206 ); 207 menuList = toolbox.doc.getElementById("toolbox-frame-menu"); 208 await waitFor( 209 () => menuList.querySelectorAll(".command").length == 3, 210 "Wait for fallback, background and sidebar documents to visible in the iframe dropdown" 211 ); 212 213 // Verify that we got the data: URI request made from the background page 214 const netmonitor = await toolbox.selectTool("netmonitor"); 215 is( 216 netmonitor.panelWin.store.getState().requests.requests.length, 217 1, 218 "There is a the request for the data: URI done from the background page" 219 ); 220 221 await toolbox.selectTool("webconsole"); 222 223 info("Introduce a typo in the manifest before reloading"); 224 loadedTargets = 0; 225 // Add an unexpected character at the end of the JSON 226 files["manifest.json"] += `x`; 227 await AddonTestUtils.promiseWriteFilesToExtension( 228 tempAddonDir.path, 229 ADDON_ID, 230 files, 231 true 232 ); 233 clickReload(devtoolsDocument); 234 235 const notification = await waitFor( 236 () => toolbox.doc.querySelector(`.notification[data-key="reload-error"]`), 237 "Wait for the error to be shown" 238 ); 239 Assert.stringContains( 240 notification.textContent, 241 "unexpected non-whitespace character after JSON data", 242 "The error message is correct" 243 ); 244 is(loadedTargets, 0, "No target is loaded when the add-on fails loading"); 245 246 info("Restore to the valid manifest and reload again"); 247 files["manifest.json"] = manifest; 248 // Empty the background page to prevent throwing the exception 249 files["background.js"] = `console.log("background log");`; 250 await AddonTestUtils.promiseWriteFilesToExtension( 251 tempAddonDir.path, 252 ADDON_ID, 253 files, 254 true 255 ); 256 clickReload(devtoolsDocument); 257 258 await waitFor( 259 () => !toolbox.doc.querySelector(`.notification[data-key="reload-error"]`), 260 "Wait for the error to be hidden" 261 ); 262 await waitFor( 263 () => loadedTargets == 2, 264 "Wait for background and popup targets to be reloaded" 265 ); 266 await waitFor( 267 () => findMessageByType(hud, "background log", ".console-api"), 268 "Found the background page console api call" 269 ); 270 ok( 271 !findMessageByType(hud, "background exception", ".error"), 272 "The background page exception is no longer visible" 273 ); 274 is( 275 netmonitor.panelWin.store.getState().requests.requests.length, 276 0, 277 "The netmonitor is also cleared" 278 ); 279 280 info("Enable log persistance in the console"); 281 devtoolsDocument 282 .querySelector(".webconsole-console-settings-menu-item-persistentLogs") 283 .click(); 284 await waitUntil( 285 () => hud.ui.wrapper.getStore().getState().ui.persistLogs === true 286 ); 287 288 clickReload(devtoolsDocument); 289 290 info("Wait for the reloaded message in the console"); 291 await waitFor(() => findMessageByType(hud, "Reloaded", ".navigationMarker")); 292 293 await closeWebExtAboutDevtoolsToolbox(devtoolsWindow, window); 294 await addon.uninstall(); 295 await removeTab(tab); 296 }); 297 298 function clickReload(devtoolsDocument) { 299 devtoolsDocument.querySelector(".qa-reload-button").click(); 300 }