test_ext_options_ui.html (15649B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>PageAction Test</title> 6 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> 7 <script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script> 8 <script type="text/javascript" src="head.js"></script> 9 <link rel="stylesheet" href="chrome://mochikit/contents/tests/SimpleTest/test.css"/> 10 </head> 11 <body> 12 13 <script type="text/javascript"> 14 "use strict"; 15 16 async function waitAboutAddonsRendered(addonId) { 17 await ContentTaskUtils.waitForCondition(() => { 18 return content.document.querySelector(`div.addon-item[addonID="${addonId}"]`); 19 }, `wait Addon Item for ${addonId} to be rendered`); 20 } 21 22 async function navigateToAddonDetails(addonId) { 23 const item = content.document.querySelector(`div.addon-item[addonID="${addonId}"]`); 24 const rect = item.getBoundingClientRect(); 25 const x = rect.left + rect.width / 2; 26 const y = rect.top + rect.height / 2; 27 content.window.synthesizeMouseEvent("mousedown", x, y, {}, { toWindow: true }); 28 content.window.synthesizeMouseEvent("mouseup", x, y, {}, { toWindow: true }); 29 } 30 31 async function waitAddonOptionsPage([addonId, expectedText]) { 32 await ContentTaskUtils.waitForCondition(() => { 33 const optionsIframe = content.document.querySelector(`#addon-options`); 34 return optionsIframe && optionsIframe.contentDocument.readyState === "complete" && 35 optionsIframe.contentDocument.body.innerText.includes(expectedText); 36 }, `wait Addon Options ${expectedText} for ${addonId} to be loaded`); 37 38 const optionsIframe = content.document.querySelector(`#addon-options`); 39 40 return { 41 iframeHeight: optionsIframe.style.height, 42 documentHeight: optionsIframe.contentDocument.documentElement.scrollHeight, 43 bodyHeight: optionsIframe.contentDocument.body.scrollHeight, 44 }; 45 } 46 47 async function clickOnLinkInOptionsPage(selector) { 48 const optionsIframe = content.document.querySelector(`#addon-options`); 49 optionsIframe.contentDocument.querySelector(selector).click(); 50 } 51 52 async function clickAddonOptionButton() { 53 content.document.querySelector(`button#open-addon-options`).click(); 54 } 55 56 async function navigateBack() { 57 content.window.history.back(); 58 } 59 60 function waitDOMContentLoaded(checkUrlCb) { 61 const {BrowserApp} = Services.wm.getMostRecentWindow("navigator:browser"); 62 63 return new Promise(resolve => { 64 const listener = (event) => { 65 if (checkUrlCb(event.target.defaultView.location.href)) { 66 BrowserApp.deck.removeEventListener("DOMContentLoaded", listener); 67 resolve(); 68 } 69 }; 70 71 BrowserApp.deck.addEventListener("DOMContentLoaded", listener); 72 }); 73 } 74 75 function waitAboutAddonsLoaded() { 76 return waitDOMContentLoaded(url => url === "about:addons"); 77 } 78 79 function clickAddonDisable() { 80 content.document.querySelector("#disable-btn").click(); 81 } 82 83 function clickAddonEnable() { 84 content.document.querySelector("#enable-btn").click(); 85 } 86 87 add_task(async function test_options_ui_iframe_height() { 88 const addonID = "test-options-ui@mozilla.org"; 89 90 const extension = ExtensionTestUtils.loadExtension({ 91 useAddonManager: "temporary", 92 manifest: { 93 browser_specific_settings: { 94 gecko: {id: addonID}, 95 }, 96 name: "Options UI Extension", 97 description: "Longer addon description", 98 options_ui: { 99 page: "options.html", 100 }, 101 }, 102 files: { 103 // An option page with the document element bigger than the body. 104 "options.html": `<!DOCTYPE html> 105 <html> 106 <head> 107 <meta charset="utf-8"> 108 <style> 109 html { height: 500px; border: 1px solid black; } 110 body { height: 200px; } 111 </style> 112 </head> 113 <body> 114 <h1>Options page 1</h1> 115 <a href="options2.html">go to page 2</a> 116 </body> 117 </html> 118 `, 119 // A second option page with the body element bigger than the document. 120 "options2.html": `<!DOCTYPE html> 121 <html> 122 <head> 123 <meta charset="utf-8"> 124 <style> 125 html { height: 200px; border: 1px solid black; } 126 body { height: 350px; } 127 </style> 128 </head> 129 <body> 130 <h1>Options page 2</h1> 131 </body> 132 </html> 133 `, 134 }, 135 }); 136 137 await extension.startup(); 138 139 const {BrowserApp} = Services.wm.getMostRecentWindow("navigator:browser"); 140 141 const onceAboutAddonsLoaded = waitAboutAddonsLoaded(); 142 143 BrowserApp.addTab("about:addons", { 144 selected: true, 145 parentId: BrowserApp.selectedTab.id, 146 }); 147 148 await onceAboutAddonsLoaded; 149 150 is(BrowserApp.selectedTab.currentURI.spec, "about:addons", 151 "about:addons is the currently selected tab"); 152 153 await SpecialPowers.spawn(BrowserApp.selectedTab.browser, [addonID], waitAboutAddonsRendered); 154 155 await SpecialPowers.spawn(BrowserApp.selectedTab.browser, [addonID], navigateToAddonDetails); 156 157 const optionsSizes = await SpecialPowers.spawn( 158 BrowserApp.selectedTab.browser, [[addonID, "Options page 1"]], waitAddonOptionsPage 159 ); 160 161 ok(parseInt(optionsSizes.iframeHeight, 10) >= 500, 162 "The addon options iframe is at least 500px"); 163 164 is(optionsSizes.iframeHeight, optionsSizes.documentHeight + "px", 165 "The addon options iframe has the expected height"); 166 167 await SpecialPowers.spawn(BrowserApp.selectedTab.browser, ["a"], clickOnLinkInOptionsPage); 168 169 const options2Sizes = await SpecialPowers.spawn( 170 BrowserApp.selectedTab.browser, [[addonID, "Options page 2"]], waitAddonOptionsPage 171 ); 172 173 // The second option page has a body bigger than the document element 174 // and we expect the iframe to be bigger than that. 175 ok(parseInt(options2Sizes.iframeHeight, 10) > 200, 176 `The iframe is bigger then 200px (${options2Sizes.iframeHeight})`); 177 178 // The second option page has a body smaller than the document element of the first 179 // page and we expect the iframe to be smaller than for the previous options page. 180 ok(parseInt(options2Sizes.iframeHeight, 10) < 500, 181 `The iframe is smaller then 500px (${options2Sizes.iframeHeight})`); 182 183 is(options2Sizes.iframeHeight, options2Sizes.documentHeight + "px", 184 "The second addon options page has the expected height"); 185 186 await SpecialPowers.spawn(BrowserApp.selectedTab.browser, [], navigateBack); 187 188 const backToOptionsSizes = await SpecialPowers.spawn( 189 BrowserApp.selectedTab.browser, [[addonID, "Options page 1"]], waitAddonOptionsPage 190 ); 191 192 // After going back to the first options page, 193 // we expect the iframe to have the same size of the previous load. 194 is(backToOptionsSizes.iframeHeight, optionsSizes.iframeHeight, 195 `When navigating back, the old iframe size is restored (${backToOptionsSizes.iframeHeight})`); 196 197 BrowserApp.closeTab(BrowserApp.selectedTab); 198 199 await extension.unload(); 200 }); 201 202 add_task(async function test_options_ui_open_aboutaddons_details() { 203 const addonID = "test-options-ui-open-addon-details@mozilla.org"; 204 205 function background() { 206 browser.test.onMessage.addListener(msg => { 207 if (msg !== "runtime.openOptionsPage") { 208 browser.test.fail(`Received unexpected test message: ${msg}`); 209 return; 210 } 211 212 browser.runtime.openOptionsPage(); 213 }); 214 } 215 216 function optionsScript() { 217 browser.test.sendMessage("options-page-loaded", window.location.href); 218 } 219 220 const extension = ExtensionTestUtils.loadExtension({ 221 useAddonManager: "temporary", 222 background, 223 manifest: { 224 browser_specific_settings: { 225 gecko: {id: addonID}, 226 }, 227 name: "Options UI open addon details Extension", 228 description: "Longer addon description", 229 options_ui: { 230 page: "options.html", 231 }, 232 }, 233 files: { 234 "options.js": optionsScript, 235 "options.html": `<!DOCTYPE html> 236 <html> 237 <head> 238 <meta charset="utf-8"> 239 </head> 240 <body> 241 <h1>Options page</h1> 242 <script src="options.js"><\/script> 243 </body> 244 </html> 245 `, 246 }, 247 }); 248 249 await extension.startup(); 250 251 const {BrowserApp} = Services.wm.getMostRecentWindow("navigator:browser"); 252 253 const onceAboutAddonsLoaded = waitAboutAddonsLoaded(); 254 255 BrowserApp.addTab("about:addons", { 256 selected: true, 257 parentId: BrowserApp.selectedTab.id, 258 }); 259 260 await onceAboutAddonsLoaded; 261 262 is(BrowserApp.selectedTab.currentURI.spec, "about:addons", 263 "about:addons is the currently selected tab"); 264 265 info("Wait runtime.openOptionsPage to open the about:addond details in the existent tab"); 266 extension.sendMessage("runtime.openOptionsPage"); 267 await extension.awaitMessage("options-page-loaded"); 268 269 is(BrowserApp.selectedTab.currentURI.spec, "about:addons", 270 "about:addons is still the currently selected tab once the options has been loaded"); 271 272 BrowserApp.closeTab(BrowserApp.selectedTab); 273 274 await extension.unload(); 275 }); 276 277 add_task(async function test_options_ui_open_in_tab() { 278 const addonID = "test-options-ui@mozilla.org"; 279 280 function background() { 281 browser.test.onMessage.addListener(msg => { 282 if (msg !== "runtime.openOptionsPage") { 283 browser.test.fail(`Received unexpected test message: ${msg}`); 284 return; 285 } 286 287 browser.runtime.openOptionsPage(); 288 }); 289 } 290 291 function optionsScript() { 292 browser.test.sendMessage("options-page-loaded", window.location.href); 293 } 294 295 const extension = ExtensionTestUtils.loadExtension({ 296 useAddonManager: "temporary", 297 background, 298 manifest: { 299 browser_specific_settings: { 300 gecko: {id: addonID}, 301 }, 302 name: "Options UI open_in_tab Extension", 303 description: "Longer addon description", 304 options_ui: { 305 page: "options.html", 306 open_in_tab: true, 307 }, 308 }, 309 files: { 310 "options.js": optionsScript, 311 "options.html": `<!DOCTYPE html> 312 <html> 313 <head> 314 <meta charset="utf-8"> 315 </head> 316 <body> 317 <h1>Options page</h1> 318 <script src="options.js"><\/script> 319 </body> 320 </html> 321 `, 322 }, 323 }); 324 325 await extension.startup(); 326 327 const {BrowserApp} = Services.wm.getMostRecentWindow("navigator:browser"); 328 329 const onceAboutAddonsLoaded = waitAboutAddonsLoaded(); 330 331 BrowserApp.selectOrAddTab("about:addons", { 332 selected: true, 333 parentId: BrowserApp.selectedTab.id, 334 }); 335 336 await onceAboutAddonsLoaded; 337 338 const aboutAddonsTab = BrowserApp.selectedTab; 339 340 is(aboutAddonsTab.currentURI.spec, "about:addons", 341 "about:addons is the currently selected tab"); 342 343 await SpecialPowers.spawn(aboutAddonsTab.browser, [addonID], waitAboutAddonsRendered); 344 await SpecialPowers.spawn(aboutAddonsTab.browser, [addonID], navigateToAddonDetails); 345 346 const onceAddonOptionsLoaded = waitDOMContentLoaded(url => url.endsWith("options.html")); 347 348 info("Click the Options button in the addon details"); 349 await SpecialPowers.spawn(aboutAddonsTab.browser, [], clickAddonOptionButton); 350 351 info("Waiting that the addon options are loaded in a new tab"); 352 await onceAddonOptionsLoaded; 353 354 const addonOptionsTab = BrowserApp.selectedTab; 355 356 ok(aboutAddonsTab.id !== addonOptionsTab.id, 357 "The Addon Options page has been loaded in a new tab"); 358 359 let optionsURL = await extension.awaitMessage("options-page-loaded"); 360 361 is(addonOptionsTab.currentURI.spec, optionsURL, 362 "Got the expected extension url opened in the addon options tab"); 363 364 const waitTabClosed = (nativeTab) => { 365 return new Promise(resolve => { 366 const {BrowserApp} = Services.wm.getMostRecentWindow("navigator:browser"); 367 const expectedBrowser = nativeTab.browser; 368 369 const tabCloseListener = (event) => { 370 const browser = event.target; 371 if (browser !== expectedBrowser) { 372 return; 373 } 374 375 BrowserApp.deck.removeEventListener("TabClose", tabCloseListener); 376 resolve(); 377 }; 378 379 BrowserApp.deck.addEventListener("TabClose", tabCloseListener); 380 }); 381 }; 382 383 const onceOptionsTabClosed = waitTabClosed(addonOptionsTab); 384 const onceAboutAddonsClosed = waitTabClosed(aboutAddonsTab); 385 386 info("Close the opened about:addons and options tab"); 387 BrowserApp.closeTab(addonOptionsTab); 388 BrowserApp.closeTab(aboutAddonsTab); 389 390 info("Wait the tabs to be closed"); 391 await Promise.all([onceOptionsTabClosed, onceAboutAddonsClosed]); 392 393 const oldSelectedTab = BrowserApp.selectedTab; 394 info("Call runtime.openOptionsPage"); 395 extension.sendMessage("runtime.openOptionsPage"); 396 397 info("Wait runtime.openOptionsPage to open the options in a new tab"); 398 optionsURL = await extension.awaitMessage("options-page-loaded"); 399 is(BrowserApp.selectedTab.currentURI.spec, optionsURL, 400 "runtime.openOptionsPage has opened the expected extension page"); 401 ok(BrowserApp.selectedTab !== oldSelectedTab, 402 "runtime.openOptionsPage has opened a new tab"); 403 404 BrowserApp.closeTab(BrowserApp.selectedTab); 405 406 await extension.unload(); 407 }); 408 409 add_task(async function test_options_ui_on_disable_and_enable() { 410 // Temporarily disabled for races. 411 /* eslint-disable no-unreachable */ 412 return; 413 414 const addonID = "test-options-ui-disable-enable@mozilla.org"; 415 416 function optionsScript() { 417 browser.test.sendMessage("options-page-loaded", window.location.href); 418 } 419 420 const extension = ExtensionTestUtils.loadExtension({ 421 useAddonManager: "temporary", 422 manifest: { 423 browser_specific_settings: { 424 gecko: {id: addonID}, 425 }, 426 name: "Options UI open addon details Extension", 427 description: "Longer addon description", 428 options_ui: { 429 page: "options.html", 430 }, 431 }, 432 files: { 433 "options.js": optionsScript, 434 "options.html": `<!DOCTYPE html> 435 <html> 436 <head> 437 <meta charset="utf-8"> 438 </head> 439 <body> 440 <h1>Options page</h1> 441 <script src="options.js"><\/script> 442 </body> 443 </html> 444 `, 445 }, 446 }); 447 448 await extension.startup(); 449 450 const {BrowserApp} = Services.wm.getMostRecentWindow("navigator:browser"); 451 452 const onceAboutAddonsLoaded = waitAboutAddonsLoaded(); 453 454 BrowserApp.addTab("about:addons", { 455 selected: true, 456 parentId: BrowserApp.selectedTab.id, 457 }); 458 459 await onceAboutAddonsLoaded; 460 461 const aboutAddonsTab = BrowserApp.selectedTab; 462 463 is(aboutAddonsTab.currentURI.spec, "about:addons", 464 "about:addons is the currently selected tab"); 465 466 info("Wait the addon details to have been loaded"); 467 await SpecialPowers.spawn(aboutAddonsTab.browser, [addonID], waitAboutAddonsRendered); 468 await SpecialPowers.spawn(aboutAddonsTab.browser, [addonID], navigateToAddonDetails); 469 470 info("Wait the addon options page to have been loaded"); 471 await extension.awaitMessage("options-page-loaded"); 472 473 info("Click the addon disable button"); 474 await SpecialPowers.spawn(aboutAddonsTab.browser, [], clickAddonDisable); 475 476 // NOTE: Currently after disabling the addon the extension.awaitMessage seems 477 // to fail be able to receive events coming from the browser.test.sendMessage API 478 // (nevertheless `await extension.unload()` seems to be able to remove the extension), 479 // falling back to wait for the options page to be loaded here. 480 const onceAddonOptionsLoaded = waitDOMContentLoaded(url => url.endsWith("options.html")); 481 482 info("Click the addon enable button"); 483 await SpecialPowers.spawn(aboutAddonsTab.browser, [], clickAddonEnable); 484 485 info("Wait the addon options page to have been loaded after clicking the addon enable button"); 486 await onceAddonOptionsLoaded; 487 488 BrowserApp.closeTab(BrowserApp.selectedTab); 489 490 await extension.unload(); 491 }); 492 493 </script> 494 495 </body> 496 </html>