/* 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/. */ let gAccount, gFolders, gMessage, gExpectedAttachments; const { mailTestUtils } = ChromeUtils.import( "resource://testing-common/mailnews/MailTestUtils.jsm" ); const URL_BASE = "http://mochi.test:8888/browser/comm/mail/components/extensions/test/browser/data"; var tabmail = document.getElementById("tabmail"); var about3Pane = tabmail.currentAbout3Pane; var messagePane = about3Pane.messageBrowser.contentWindow.getMessagePaneBrowser(); /** * Right-click on something and wait for the context menu to appear. * For elements in the parent process only. * * @param {Element} menu - The that should appear. * @param {Element} element - The element to be clicked on. * @returns {Promise} A promise that resolves when the menu appears. */ function rightClick(menu, element, win) { let shownPromise = BrowserTestUtils.waitForEvent(menu, "popupshown"); EventUtils.synthesizeMouseAtCenter(element, { type: "contextmenu" }, win); return shownPromise; } /** * Check the parameters of a browser.onShown event was fired. * * @see mail/components/extensions/schemas/menus.json * * @param extension * @param {object} expectedInfo * @param {Array} expectedInfo.menuIds * @param {Array} expectedInfo.contexts * @param {Array?} expectedInfo.attachments * @param {object} expectedTab * @param {boolean} expectedTab.active * @param {integer} expectedTab.index * @param {boolean} expectedTab.mailTab */ async function checkShownEvent(extension, expectedInfo, expectedTab) { let [info, tab] = await extension.awaitMessage("onShown"); Assert.deepEqual(info.menuIds, expectedInfo.menuIds); Assert.deepEqual(info.contexts, expectedInfo.contexts); Assert.equal( !!info.attachments, !!expectedInfo.attachments, "attachments in info" ); if (expectedInfo.attachments) { for (let i = 0; i < expectedInfo.attachments.length; i++) { Assert.equal(info.attachments[i].name, expectedInfo.attachments[i].name); Assert.equal(info.attachments[i].size, expectedInfo.attachments[i].size); Assert.equal( info.attachments[i].partName, expectedInfo.attachments[i].partName ); Assert.equal( info.attachments[i].contentType, expectedInfo.attachments[i].contentType ); } } Assert.equal(tab.active, expectedTab.active, "tab is active"); Assert.equal(tab.index, expectedTab.index, "tab index"); Assert.equal(tab.mailTab, expectedTab.mailTab, "tab is mailTab"); } /** * Check the parameters of a browser.onClicked event was fired. * * @see mail/components/extensions/schemas/menus.json * * @param extension * @param {object} expectedInfo * @param {string?} expectedInfo.menuItemId * @param {Array?} expectedInfo.attachments * @param {object} expectedTab * @param {boolean} expectedTab.active * @param {integer} expectedTab.index * @param {boolean} expectedTab.mailTab */ async function checkClickedEvent(extension, expectedInfo, expectedTab) { let [info, tab] = await extension.awaitMessage("onClicked"); Assert.equal( !!info.attachments, !!expectedInfo.attachments, "attachments in info" ); if (expectedInfo.attachments) { for (let i = 0; i < expectedInfo.attachments.length; i++) { Assert.equal(info.attachments[i].name, expectedInfo.attachments[i].name); Assert.equal(info.attachments[i].size, expectedInfo.attachments[i].size); Assert.equal( info.attachments[i].partName, expectedInfo.attachments[i].partName ); Assert.equal( info.attachments[i].contentType, expectedInfo.attachments[i].contentType ); } } if (expectedInfo.menuItemId) { Assert.equal(info.menuItemId, expectedInfo.menuItemId, "menuItemId"); } Assert.equal(tab.active, expectedTab.active, "tab is active"); Assert.equal(tab.index, expectedTab.index, "tab index"); Assert.equal(tab.mailTab, expectedTab.mailTab, "tab is mailTab"); } function getExtensionDetails(...permissions) { return { files: { "background.js": async () => { for (let context of [ "message_attachments", "all_message_attachments", ]) { await new Promise(resolve => { browser.menus.create( { id: context, title: context, contexts: [context], }, resolve ); }); } browser.menus.onShown.addListener((...args) => { browser.test.sendMessage("onShown", args); }); browser.menus.onClicked.addListener((...args) => { browser.test.sendMessage("onClicked", args); }); browser.test.sendMessage("menus-created"); }, }, manifest: { applications: { gecko: { id: "menus@mochi.test", }, }, background: { scripts: ["background.js"] }, permissions: [...permissions, "menus"], }, useAddonManager: "temporary", }; } add_setup(async function () { await Services.search.init(); gAccount = createAccount(); addIdentity(gAccount); gFolders = gAccount.incomingServer.rootFolder.subFolders; await createMessages(gFolders[0], { count: 1, body: { contentType: "text/html", body: await fetch(`${URL_BASE}/content.html`).then(r => r.text()), }, attachments: [ { body: "I am an text attachment.", filename: "test1.txt", contentType: "text/plain", }, ], }); await createMessages(gFolders[0], { count: 1, body: { contentType: "text/html", body: await fetch(`${URL_BASE}/content.html`).then(r => r.text()), }, attachments: [ { body: "I am an text attachment.", filename: "test1.txt", contentType: "text/plain", }, { body: "I am another but larger attachment. ", filename: "test2.txt", contentType: "text/plain", }, ], }); about3Pane.restoreState({ folderPaneVisible: true, folderURI: gFolders[0].URI, messagePaneVisible: true, }); gExpectedAttachments = [ { name: "test1.txt", size: 24, contentType: "text/plain", partName: "1.2", }, { name: "test2.txt", size: 36, contentType: "text/plain", partName: "1.3", }, ]; }); // Test a click on an attachment item. async function subtest_attachmentItem( extension, win, element, expectedContext, expectedAttachments ) { let menu = element.ownerGlobal.document.getElementById( expectedContext == "message_attachments" ? "attachmentItemContext" : "attachmentListContext" ); let expectedShowData = { menuIds: [expectedContext], contexts: [expectedContext, "all"], attachments: expectedAttachments, }; let expectedClickData = { attachments: expectedAttachments, }; let expectedTab = { active: true, index: 0, mailTab: false }; let showEventPromise = checkShownEvent( extension, expectedShowData, expectedTab ); await rightClick(menu, element, win); let menuItem = menu.querySelector( `#menus_mochi_test-menuitem-_${expectedContext}` ); await showEventPromise; Assert.ok(menuItem); let clickEventPromise = checkClickedEvent( extension, expectedClickData, expectedTab ); menu.activateItem(menuItem); await clickEventPromise; // Sometimes, the popup will open then instantly disappear. It seems to // still be hiding after the previous appearance. If we wait a little bit, // this doesn't happen. // eslint-disable-next-line mozilla/no-arbitrary-setTimeout await new Promise(r => setTimeout(r, 250)); } async function subtest_attachments( extension, win, expectedContext, expectedAttachments ) { // Test clicking on the attachmentInfo element. let attachmentInfo = win.document.getElementById("attachmentInfo"); await subtest_attachmentItem( extension, win, attachmentInfo, expectedContext, expectedAttachments ); if (expectedAttachments) { win.toggleAttachmentList(true); let attachmentList = win.document.getElementById("attachmentList"); Assert.equal( attachmentList.children.length, expectedAttachments.length, "Should see the expected number of attachments." ); // Test clicking on the individual attachment elements. for (let i = 0; i < attachmentList.children.length; i++) { // Select the attachment. attachmentList.selectItem(attachmentList.children[i]); // Run context click check. await subtest_attachmentItem( extension, win, attachmentList.children[i], "message_attachments", [expectedAttachments[i]] ); } } } async function subtest_message_panes( permissions, expectedContext, expectedAttachments = null ) { let extensionDetails = getExtensionDetails(...permissions); info("Test the message pane in the 3-pane tab."); let extension = ExtensionTestUtils.loadExtension(extensionDetails); await extension.startup(); await extension.awaitMessage("menus-created"); await subtest_attachments( extension, tabmail.currentAboutMessage, expectedContext, expectedAttachments ); await extension.unload(); info("Test the message pane in a tab."); await openMessageInTab(gMessage); extension = ExtensionTestUtils.loadExtension(extensionDetails); await extension.startup(); await extension.awaitMessage("menus-created"); await subtest_attachments( extension, tabmail.currentAboutMessage, expectedContext, expectedAttachments ); await extension.unload(); tabmail.closeOtherTabs(0); info("Test the message pane in a separate window."); let displayWindow = await openMessageInWindow(gMessage); extension = ExtensionTestUtils.loadExtension(extensionDetails); await extension.startup(); await extension.awaitMessage("menus-created"); await subtest_attachments( extension, displayWindow.messageBrowser.contentWindow, expectedContext, expectedAttachments ); await extension.unload(); await BrowserTestUtils.closeWindow(displayWindow); } // Tests using a message with one attachment. add_task(async function test_message_panes() { gMessage = [...gFolders[0].messages][0]; about3Pane.threadTree.selectedIndex = 0; await promiseMessageLoaded(messagePane, gMessage); await subtest_message_panes( ["accountsRead", "messagesRead"], "message_attachments", [gExpectedAttachments[0]] ); }); add_task(async function test_message_panes_no_accounts_permission() { return subtest_message_panes(["messagesRead"], "message_attachments", [ gExpectedAttachments[0], ]); }); add_task(async function test_message_panes_no_messages_permission() { return subtest_message_panes(["accountsRead"], "message_attachments"); }); add_task(async function test_message_panes_no_permissions() { return subtest_message_panes([], "message_attachments"); });