/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; async function runTests(options) { async function background(getTests) { async function checkDetails(expecting, details) { let title = await browser.sidebarAction.getTitle(details); browser.test.assertEq( expecting.title, title, "expected value from getTitle in " + JSON.stringify(details) ); let panel = await browser.sidebarAction.getPanel(details); browser.test.assertEq( expecting.panel, panel, "expected value from getPanel in " + JSON.stringify(details) ); } let tabs = []; let windows = []; let tests = getTests(tabs, windows); { let tabId = 0xdeadbeef; let calls = [ () => browser.sidebarAction.setTitle({ tabId, title: "foo" }), () => browser.sidebarAction.setIcon({ tabId, path: "foo.png" }), () => browser.sidebarAction.setPanel({ tabId, panel: "foo.html" }), ]; for (let call of calls) { await browser.test.assertRejects( new Promise(resolve => resolve(call())), RegExp(`Invalid tab ID: ${tabId}`), "Expected invalid tab ID error" ); } } // Runs the next test in the `tests` array, checks the results, // and passes control back to the outer test scope. function nextTest() { let test = tests.shift(); test(async (expectTab, expectWindow, expectGlobal, expectDefault) => { expectGlobal = { ...expectDefault, ...expectGlobal }; expectWindow = { ...expectGlobal, ...expectWindow }; expectTab = { ...expectWindow, ...expectTab }; // Check that the API returns the expected values, and then // run the next test. let [{ windowId, id: tabId }] = await browser.tabs.query({ active: true, currentWindow: true, }); await checkDetails(expectTab, { tabId }); await checkDetails(expectWindow, { windowId }); await checkDetails(expectGlobal, {}); // Check that the actual icon has the expected values, then // run the next test. browser.test.sendMessage("nextTest", expectTab, windowId, tests.length); }); } browser.test.onMessage.addListener(msg => { if (msg != "runNextTest") { browser.test.fail("Expecting 'runNextTest' message"); } nextTest(); }); let [{ id, windowId }] = await browser.tabs.query({ active: true, currentWindow: true, }); tabs.push(id); windows.push(windowId); browser.test.sendMessage("background-page-ready"); } let extension = ExtensionTestUtils.loadExtension({ manifest: options.manifest, useAddonManager: "temporary", files: options.files || {}, background: `(${background})(${options.getTests})`, }); let sidebarActionId; function checkDetails(details, windowId) { let { document } = Services.wm.getOuterWindowWithId(windowId); if (!sidebarActionId) { sidebarActionId = `${makeWidgetId(extension.id)}-sidebar-action`; } let menuId = `menu_${sidebarActionId}`; let menu = document.getElementById(menuId); ok(menu, "menu exists"); let title = details.title || options.manifest.name; is(getListStyleImage(menu), details.icon, "icon URL is correct"); is(menu.getAttribute("label"), title, "image label is correct"); } let awaitFinish = new Promise(resolve => { extension.onMessage("nextTest", (expecting, windowId, testsRemaining) => { checkDetails(expecting, windowId); if (testsRemaining) { extension.sendMessage("runNextTest"); } else { resolve(); } }); }); // Wait for initial sidebar load. SidebarUI.browser.addEventListener( "load", async () => { // Wait for the background page listeners to be ready and // then start the tests. await extension.awaitMessage("background-page-ready"); extension.sendMessage("runNextTest"); }, { capture: true, once: true } ); await extension.startup(); await awaitFinish; await extension.unload(); } let sidebar = ` A Test Sidebar `; add_task(async function testTabSwitchContext() { await runTests({ manifest: { sidebar_action: { default_icon: "default.png", default_panel: "__MSG_panel__", default_title: "Default __MSG_title__", }, default_locale: "en", permissions: ["tabs"], }, files: { "default.html": sidebar, "global.html": sidebar, "2.html": sidebar, "_locales/en/messages.json": { panel: { message: "default.html", description: "Panel", }, title: { message: "Title", description: "Title", }, }, "default.png": imageBuffer, "global.png": imageBuffer, "1.png": imageBuffer, "2.png": imageBuffer, }, getTests: function (tabs) { let details = [ { icon: browser.runtime.getURL("default.png"), panel: browser.runtime.getURL("default.html"), title: "Default Title", }, { icon: browser.runtime.getURL("1.png") }, { icon: browser.runtime.getURL("2.png"), panel: browser.runtime.getURL("2.html"), title: "Title 2", }, { icon: browser.runtime.getURL("global.png"), panel: browser.runtime.getURL("global.html"), title: "Global Title", }, { icon: browser.runtime.getURL("1.png"), panel: browser.runtime.getURL("2.html"), }, ]; return [ async expect => { browser.test.log("Initial state, expect default properties."); expect(null, null, null, details[0]); }, async expect => { browser.test.log( "Change the icon in the current tab. Expect default properties excluding the icon." ); await browser.sidebarAction.setIcon({ tabId: tabs[0], path: "1.png", }); expect(details[1], null, null, details[0]); }, async expect => { browser.test.log("Create a new tab. Expect default properties."); let tab = await browser.tabs.create({ active: true, url: "about:blank?0", }); tabs.push(tab.id); expect(null, null, null, details[0]); }, async expect => { browser.test.log("Change properties. Expect new properties."); let tabId = tabs[1]; await Promise.all([ browser.sidebarAction.setIcon({ tabId, path: "2.png" }), browser.sidebarAction.setPanel({ tabId, panel: "2.html" }), browser.sidebarAction.setTitle({ tabId, title: "Title 2" }), ]); expect(details[2], null, null, details[0]); }, expect => { browser.test.log("Navigate to a new page. Expect no changes."); // TODO: This listener should not be necessary, but the |tabs.update| // callback currently fires too early in e10s windows. browser.tabs.onUpdated.addListener(function listener(tabId, changed) { if (tabId == tabs[1] && changed.url) { browser.tabs.onUpdated.removeListener(listener); expect(details[2], null, null, details[0]); } }); browser.tabs.update(tabs[1], { url: "about:blank?1" }); }, async expect => { browser.test.log( "Switch back to the first tab. Expect previously set properties." ); await browser.tabs.update(tabs[0], { active: true }); expect(details[1], null, null, details[0]); }, async expect => { browser.test.log( "Change global values, expect those changes reflected." ); await Promise.all([ browser.sidebarAction.setIcon({ path: "global.png" }), browser.sidebarAction.setPanel({ panel: "global.html" }), browser.sidebarAction.setTitle({ title: "Global Title" }), ]); expect(details[1], null, details[3], details[0]); }, async expect => { browser.test.log( "Switch back to tab 2. Expect former tab values, and new global values from previous step." ); await browser.tabs.update(tabs[1], { active: true }); expect(details[2], null, details[3], details[0]); }, async expect => { browser.test.log( "Delete tab, switch back to tab 1. Expect previous results again." ); await browser.tabs.remove(tabs[1]); expect(details[1], null, details[3], details[0]); }, async expect => { browser.test.log("Create a new tab. Expect new global properties."); let tab = await browser.tabs.create({ active: true, url: "about:blank?2", }); tabs.push(tab.id); expect(null, null, details[3], details[0]); }, async expect => { browser.test.log("Delete tab."); await browser.tabs.remove(tabs[2]); expect(details[1], null, details[3], details[0]); }, async expect => { browser.test.log("Change tab panel."); let tabId = tabs[0]; await browser.sidebarAction.setPanel({ tabId, panel: "2.html" }); expect(details[4], null, details[3], details[0]); }, async expect => { browser.test.log("Revert tab panel."); let tabId = tabs[0]; await browser.sidebarAction.setPanel({ tabId, panel: null }); expect(details[1], null, details[3], details[0]); }, ]; }, }); }); add_task(async function testDefaultTitle() { await runTests({ manifest: { name: "Foo Extension", sidebar_action: { default_icon: "icon.png", default_panel: "sidebar.html", }, permissions: ["tabs"], }, files: { "sidebar.html": sidebar, "icon.png": imageBuffer, }, getTests: function (tabs) { let details = [ { title: "Foo Extension", panel: browser.runtime.getURL("sidebar.html"), icon: browser.runtime.getURL("icon.png"), }, { title: "Foo Title" }, { title: "Bar Title" }, ]; return [ async expect => { browser.test.log("Initial state. Expect default extension title."); expect(null, null, null, details[0]); }, async expect => { browser.test.log("Change the tab title. Expect new title."); browser.sidebarAction.setTitle({ tabId: tabs[0], title: "Foo Title", }); expect(details[1], null, null, details[0]); }, async expect => { browser.test.log("Change the global title. Expect same properties."); browser.sidebarAction.setTitle({ title: "Bar Title" }); expect(details[1], null, details[2], details[0]); }, async expect => { browser.test.log("Clear the tab title. Expect new global title."); browser.sidebarAction.setTitle({ tabId: tabs[0], title: null }); expect(null, null, details[2], details[0]); }, async expect => { browser.test.log("Clear the global title. Expect default title."); browser.sidebarAction.setTitle({ title: null }); expect(null, null, null, details[0]); }, async expect => { browser.test.assertRejects( browser.sidebarAction.setPanel({ panel: "about:addons" }), /Access denied for URL about:addons/, "unable to set panel to about:addons" ); expect(null, null, null, details[0]); }, ]; }, }); }); add_task(async function testPropertyRemoval() { await runTests({ manifest: { name: "Foo Extension", sidebar_action: { default_icon: "default.png", default_panel: "default.html", default_title: "Default Title", }, permissions: ["tabs"], }, files: { "default.html": sidebar, "global.html": sidebar, "global2.html": sidebar, "window.html": sidebar, "tab.html": sidebar, "default.png": imageBuffer, "global.png": imageBuffer, "global2.png": imageBuffer, "window.png": imageBuffer, "tab.png": imageBuffer, }, getTests: function (tabs, windows) { let defaultIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg"; let details = [ { icon: browser.runtime.getURL("default.png"), panel: browser.runtime.getURL("default.html"), title: "Default Title", }, { icon: browser.runtime.getURL("global.png"), panel: browser.runtime.getURL("global.html"), title: "global", }, { icon: browser.runtime.getURL("window.png"), panel: browser.runtime.getURL("window.html"), title: "window", }, { icon: browser.runtime.getURL("tab.png"), panel: browser.runtime.getURL("tab.html"), title: "tab", }, { icon: defaultIcon, title: "" }, { icon: browser.runtime.getURL("global2.png"), panel: browser.runtime.getURL("global2.html"), title: "global2", }, ]; return [ async expect => { browser.test.log("Initial state, expect default properties."); expect(null, null, null, details[0]); }, async expect => { browser.test.log("Set global values, expect the new values."); browser.sidebarAction.setIcon({ path: "global.png" }); browser.sidebarAction.setPanel({ panel: "global.html" }); browser.sidebarAction.setTitle({ title: "global" }); expect(null, null, details[1], details[0]); }, async expect => { browser.test.log("Set window values, expect the new values."); let windowId = windows[0]; browser.sidebarAction.setIcon({ windowId, path: "window.png" }); browser.sidebarAction.setPanel({ windowId, panel: "window.html" }); browser.sidebarAction.setTitle({ windowId, title: "window" }); expect(null, details[2], details[1], details[0]); }, async expect => { browser.test.log("Set tab values, expect the new values."); let tabId = tabs[0]; browser.sidebarAction.setIcon({ tabId, path: "tab.png" }); browser.sidebarAction.setPanel({ tabId, panel: "tab.html" }); browser.sidebarAction.setTitle({ tabId, title: "tab" }); expect(details[3], details[2], details[1], details[0]); }, async expect => { browser.test.log("Set empty tab values."); let tabId = tabs[0]; browser.sidebarAction.setIcon({ tabId, path: "" }); browser.sidebarAction.setPanel({ tabId, panel: "" }); browser.sidebarAction.setTitle({ tabId, title: "" }); expect(details[4], details[2], details[1], details[0]); }, async expect => { browser.test.log("Remove tab values, expect window values."); let tabId = tabs[0]; browser.sidebarAction.setIcon({ tabId, path: null }); browser.sidebarAction.setPanel({ tabId, panel: null }); browser.sidebarAction.setTitle({ tabId, title: null }); expect(null, details[2], details[1], details[0]); }, async expect => { browser.test.log("Remove window values, expect global values."); let windowId = windows[0]; browser.sidebarAction.setIcon({ windowId, path: null }); browser.sidebarAction.setPanel({ windowId, panel: null }); browser.sidebarAction.setTitle({ windowId, title: null }); expect(null, null, details[1], details[0]); }, async expect => { browser.test.log("Change global values, expect the new values."); browser.sidebarAction.setIcon({ path: "global2.png" }); browser.sidebarAction.setPanel({ panel: "global2.html" }); browser.sidebarAction.setTitle({ title: "global2" }); expect(null, null, details[5], details[0]); }, async expect => { browser.test.log("Remove global values, expect defaults."); browser.sidebarAction.setIcon({ path: null }); browser.sidebarAction.setPanel({ panel: null }); browser.sidebarAction.setTitle({ title: null }); expect(null, null, null, details[0]); }, ]; }, }); }); add_task(async function testMultipleWindows() { await runTests({ manifest: { name: "Foo Extension", sidebar_action: { default_icon: "default.png", default_panel: "default.html", default_title: "Default Title", }, permissions: ["tabs"], }, files: { "default.html": sidebar, "window1.html": sidebar, "window2.html": sidebar, "default.png": imageBuffer, "window1.png": imageBuffer, "window2.png": imageBuffer, }, getTests: function (tabs, windows) { let details = [ { icon: browser.runtime.getURL("default.png"), panel: browser.runtime.getURL("default.html"), title: "Default Title", }, { icon: browser.runtime.getURL("window1.png"), panel: browser.runtime.getURL("window1.html"), title: "window1", }, { icon: browser.runtime.getURL("window2.png"), panel: browser.runtime.getURL("window2.html"), title: "window2", }, { title: "tab" }, ]; return [ async expect => { browser.test.log("Initial state, expect default properties."); expect(null, null, null, details[0]); }, async expect => { browser.test.log("Set window values, expect the new values."); let windowId = windows[0]; browser.sidebarAction.setIcon({ windowId, path: "window1.png" }); browser.sidebarAction.setPanel({ windowId, panel: "window1.html" }); browser.sidebarAction.setTitle({ windowId, title: "window1" }); expect(null, details[1], null, details[0]); }, async expect => { browser.test.log("Create a new tab, expect window values."); let tab = await browser.tabs.create({ active: true }); tabs.push(tab.id); expect(null, details[1], null, details[0]); }, async expect => { browser.test.log("Set a tab title, expect it."); await browser.sidebarAction.setTitle({ tabId: tabs[1], title: "tab", }); expect(details[3], details[1], null, details[0]); }, async expect => { browser.test.log("Open a new window, expect default values."); let { id } = await browser.windows.create(); windows.push(id); expect(null, null, null, details[0]); }, async expect => { browser.test.log("Set window values, expect the new values."); let windowId = windows[1]; browser.sidebarAction.setIcon({ windowId, path: "window2.png" }); browser.sidebarAction.setPanel({ windowId, panel: "window2.html" }); browser.sidebarAction.setTitle({ windowId, title: "window2" }); expect(null, details[2], null, details[0]); }, async expect => { browser.test.log( "Move tab from old window to the new one. Tab-specific data" + " is preserved but inheritance is from the new window" ); await browser.tabs.move(tabs[1], { windowId: windows[1], index: -1 }); await browser.tabs.update(tabs[1], { active: true }); expect(details[3], details[2], null, details[0]); }, async expect => { browser.test.log("Close the initial tab of the new window."); let [{ id }] = await browser.tabs.query({ windowId: windows[1], index: 0, }); await browser.tabs.remove(id); expect(details[3], details[2], null, details[0]); }, async expect => { browser.test.log( "Move the previous tab to a 3rd window, the 2nd one will close." ); await browser.windows.create({ tabId: tabs[1] }); expect(details[3], null, null, details[0]); }, async expect => { browser.test.log("Close the tab, go back to the 1st window."); await browser.tabs.remove(tabs[1]); expect(null, details[1], null, details[0]); }, async expect => { browser.test.log( "Assert failures for bad parameters. Expect no change" ); let calls = { setIcon: { path: "default.png" }, setPanel: { panel: "default.html" }, setTitle: { title: "Default Title" }, getPanel: {}, getTitle: {}, }; for (let [method, arg] of Object.entries(calls)) { browser.test.assertThrows( () => browser.sidebarAction[method]({ ...arg, windowId: -3 }), /-3 is too small \(must be at least -2\)/, method + " with invalid windowId" ); await browser.test.assertRejects( browser.sidebarAction[method]({ ...arg, tabId: tabs[0], windowId: windows[0], }), /Only one of tabId and windowId can be specified/, method + " with both tabId and windowId" ); } expect(null, details[1], null, details[0]); }, ]; }, }); });