diff options
Diffstat (limited to 'comm/mail/test/browser/attachment/browser_attachmentMenus.js')
-rw-r--r-- | comm/mail/test/browser/attachment/browser_attachmentMenus.js | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/comm/mail/test/browser/attachment/browser_attachmentMenus.js b/comm/mail/test/browser/attachment/browser_attachmentMenus.js new file mode 100644 index 0000000000..cc24e6ccc9 --- /dev/null +++ b/comm/mail/test/browser/attachment/browser_attachmentMenus.js @@ -0,0 +1,565 @@ +/* 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/. */ + +"use strict"; + +var folder; +var messenger; +var epsilon; + +var { + create_body_part, + create_deleted_attachment, + create_detached_attachment, + create_enclosure_attachment, +} = ChromeUtils.import( + "resource://testing-common/mozmill/AttachmentHelpers.jsm" +); +var { + add_message_to_folder, + be_in_folder, + close_popup, + create_folder, + create_message, + get_about_message, + mc, + select_click_row, + wait_for_popup_to_open, +} = ChromeUtils.import( + "resource://testing-common/mozmill/FolderDisplayHelpers.jsm" +); + +var aboutMessage = get_about_message(); + +var textAttachment = + "Can't make the frug contest, Helen; stomach's upset. I'll fix you, " + + "Ubik! Ubik drops you back in the thick of things fast. Taken as " + + "directed, Ubik speeds relief to head and stomach. Remember: Ubik is " + + "only seconds away. Avoid prolonged use."; + +var detachedName = "./attachment.txt"; +var missingName = "./nonexistent.txt"; +var deletedName = "deleted.txt"; + +// create some messages that have various types of attachments +var messages = [ + { + name: "regular_attachment", + attachments: [{ body: textAttachment, filename: "ubik.txt", format: "" }], + menuStates: [{ open: true, save: true, detach: true, delete_: true }], + allMenuStates: { open: true, save: true, detach: true, delete_: true }, + }, + { + name: "detached_attachment", + bodyPart: null, + menuStates: [{ open: true, save: true, detach: false, delete_: false }], + allMenuStates: { open: true, save: true, detach: false, delete_: false }, + }, + { + name: "detached_attachment_with_missing_file", + bodyPart: null, + menuStates: [{ open: false, save: false, detach: false, delete_: false }], + allMenuStates: { open: false, save: false, detach: false, delete_: false }, + }, + { + name: "deleted_attachment", + bodyPart: null, + menuStates: [{ open: false, save: false, detach: false, delete_: false }], + allMenuStates: { open: false, save: false, detach: false, delete_: false }, + }, + { + name: "multiple_attachments", + attachments: [ + { body: textAttachment, filename: "ubik.txt", format: "" }, + { body: textAttachment, filename: "ubik2.txt", format: "" }, + ], + menuStates: [ + { open: true, save: true, detach: true, delete_: true }, + { open: true, save: true, detach: true, delete_: true }, + ], + allMenuStates: { open: true, save: true, detach: true, delete_: true }, + }, + { + name: "multiple_attachments_one_detached", + bodyPart: null, + attachments: [{ body: textAttachment, filename: "ubik.txt", format: "" }], + menuStates: [ + { open: true, save: true, detach: false, delete_: false }, + { open: true, save: true, detach: true, delete_: true }, + ], + allMenuStates: { open: true, save: true, detach: true, delete_: true }, + }, + { + name: "multiple_attachments_one_detached_with_missing_file", + bodyPart: null, + attachments: [{ body: textAttachment, filename: "ubik.txt", format: "" }], + menuStates: [ + { open: false, save: false, detach: false, delete_: false }, + { open: true, save: true, detach: true, delete_: true }, + ], + allMenuStates: { open: true, save: true, detach: true, delete_: true }, + }, + { + name: "multiple_attachments_one_deleted", + bodyPart: null, + attachments: [{ body: textAttachment, filename: "ubik.txt", format: "" }], + menuStates: [ + { open: false, save: false, detach: false, delete_: false }, + { open: true, save: true, detach: true, delete_: true }, + ], + allMenuStates: { open: true, save: true, detach: true, delete_: true }, + }, + { + name: "multiple_attachments_all_detached", + bodyPart: null, + menuStates: [ + { open: true, save: true, detach: false, delete_: false }, + { open: true, save: true, detach: false, delete_: false }, + ], + allMenuStates: { open: true, save: true, detach: false, delete_: false }, + }, + { + name: "multiple_attachments_all_detached_with_missing_files", + bodyPart: null, + menuStates: [ + { open: false, save: false, detach: false, delete_: false }, + { open: false, save: false, detach: false, delete_: false }, + ], + allMenuStates: { open: false, save: false, detach: false, delete_: false }, + }, + { + name: "multiple_attachments_all_deleted", + bodyPart: null, + menuStates: [ + { open: false, save: false, detach: false, delete_: false }, + { open: false, save: false, detach: false, delete_: false }, + ], + allMenuStates: { open: false, save: false, detach: false, delete_: false }, + }, + { + name: "link_enclosure_valid", + bodyPart: null, + menuStates: [{ open: true, save: true, detach: false, delete_: false }], + allMenuStates: { open: true, save: true, detach: false, delete_: false }, + }, + { + name: "link_enclosure_invalid", + bodyPart: null, + menuStates: [{ open: false, save: false, detach: false, delete_: false }], + allMenuStates: { open: false, save: false, detach: false, delete_: false }, + }, + { + name: "link_multiple_enclosures", + bodyPart: null, + menuStates: [ + { open: true, save: true, detach: false, delete_: false }, + { open: true, save: true, detach: false, delete_: false }, + ], + allMenuStates: { open: true, save: true, detach: false, delete_: false }, + }, + { + name: "link_multiple_enclosures_one_invalid", + bodyPart: null, + menuStates: [ + { open: true, save: true, detach: false, delete_: false }, + { open: false, save: false, detach: false, delete_: false }, + ], + allMenuStates: { open: true, save: true, detach: false, delete_: false }, + }, + { + name: "link_multiple_enclosures_all_invalid", + bodyPart: null, + menuStates: [ + { open: false, save: false, detach: false, delete_: false }, + { open: false, save: false, detach: false, delete_: false }, + ], + allMenuStates: { open: false, save: false, detach: false, delete_: false }, + }, +]; + +add_setup(async function () { + messenger = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger); + + /* Today's gory details (thanks to Jonathan Protzenko): libmime somehow + * counts the trailing newline for an attachment MIME part. Most of the time, + * assuming attachment has N bytes (no matter what's inside, newlines or + * not), libmime will return N + 1 bytes. On Linux and Mac, this always + * holds. However, on Windows, if the attachment is not encoded (that is, is + * inline text), libmime will return N + 2 bytes. + */ + epsilon = "@mozilla.org/windows-registry-key;1" in Cc ? 2 : 1; + + // set up our detached/deleted attachments + var detachedFile = new FileUtils.File( + getTestFilePath(`data/${detachedName}`) + ); + var detached = create_body_part("Here is a file", [ + create_detached_attachment(detachedFile, "text/plain"), + ]); + var multiple_detached = create_body_part("Here are some files", [ + create_detached_attachment(detachedFile, "text/plain"), + create_detached_attachment(detachedFile, "text/plain"), + ]); + + var missingFile = new FileUtils.File(getTestFilePath(`data/${missingName}`)); + var missing = create_body_part( + "Here is a file (but you deleted the external file, you silly oaf!)", + [create_detached_attachment(missingFile, "text/plain")] + ); + var multiple_missing = create_body_part( + "Here are some files (but you deleted the external files, you silly oaf!)", + [ + create_detached_attachment(missingFile, "text/plain"), + create_detached_attachment(missingFile, "text/plain"), + ] + ); + + var deleted = create_body_part("Here is a file that you deleted", [ + create_deleted_attachment(deletedName, "text/plain"), + ]); + var multiple_deleted = create_body_part( + "Here are some files that you deleted", + [ + create_deleted_attachment(deletedName, "text/plain"), + create_deleted_attachment(deletedName, "text/plain"), + ] + ); + + var enclosure_valid_url = create_body_part("My blog has the best enclosure", [ + create_enclosure_attachment( + "purr.mp3", + "audio/mpeg", + "https://example.com", + 12345678 + ), + ]); + var enclosure_invalid_url = create_body_part( + "My blog has the best enclosure with a dead link", + [ + create_enclosure_attachment( + "meow.mp3", + "audio/mpeg", + "https://example.com/invalid" + ), + ] + ); + var multiple_enclosures = create_body_part( + "My blog has the best 2 cat sound enclosures", + [ + create_enclosure_attachment( + "purr.mp3", + "audio/mpeg", + "https://example.com", + 1234567 + ), + create_enclosure_attachment( + "meow.mp3", + "audio/mpeg", + "https://example.com", + 987654321 + ), + ] + ); + var multiple_enclosures_one_link_invalid = create_body_part( + "My blog has the best 2 cat sound enclosures but one is invalid", + [ + create_enclosure_attachment( + "purr.mp3", + "audio/mpeg", + "https://example.com", + 1234567 + ), + create_enclosure_attachment( + "meow.mp3", + "audio/mpeg", + "https://example.com/invalid" + ), + ] + ); + var multiple_enclosures_all_links_invalid = create_body_part( + "My blog has 2 enclosures with 2 bad links", + [ + create_enclosure_attachment( + "purr.mp3", + "audio/mpeg", + "https://example.com/invalid" + ), + create_enclosure_attachment( + "meow.mp3", + "audio/mpeg", + "https://example.com/invalid" + ), + ] + ); + + folder = await create_folder("AttachmentMenusA"); + for (let i = 0; i < messages.length; i++) { + // First, add any missing info to the message object. + switch (messages[i].name) { + case "detached_attachment": + case "multiple_attachments_one_detached": + messages[i].bodyPart = detached; + break; + case "multiple_attachments_all_detached": + messages[i].bodyPart = multiple_detached; + break; + case "detached_attachment_with_missing_file": + case "multiple_attachments_one_detached_with_missing_file": + messages[i].bodyPart = missing; + break; + case "multiple_attachments_all_detached_with_missing_files": + messages[i].bodyPart = multiple_missing; + break; + case "deleted_attachment": + case "multiple_attachments_one_deleted": + messages[i].bodyPart = deleted; + break; + case "multiple_attachments_all_deleted": + messages[i].bodyPart = multiple_deleted; + break; + case "link_enclosure_valid": + messages[i].bodyPart = enclosure_valid_url; + break; + case "link_enclosure_invalid": + messages[i].bodyPart = enclosure_invalid_url; + break; + case "link_multiple_enclosures": + messages[i].bodyPart = multiple_enclosures; + break; + case "link_multiple_enclosures_one_invalid": + messages[i].bodyPart = multiple_enclosures_one_link_invalid; + break; + case "link_multiple_enclosures_all_invalid": + messages[i].bodyPart = multiple_enclosures_all_links_invalid; + break; + } + + await add_message_to_folder([folder], create_message(messages[i])); + } +}); + +/** + * Ensure that the specified element is visible/hidden + * + * @param id the id of the element to check + * @param visible true if the element should be visible, false otherwise + */ +function assert_shown(id, visible) { + Assert.notEqual( + aboutMessage.document.getElementById(id).hidden, + visible, + `"${id}" should be ${visible ? "visible" : "hidden"}` + ); +} + +/** + * Ensure that the specified element is enabled/disabled + * + * @param id the id of the element to check + * @param enabled true if the element should be enabled, false otherwise + */ +function assert_enabled(id, enabled) { + Assert.notEqual( + aboutMessage.document.getElementById(id).disabled, + enabled, + `"${id}" should be ${enabled ? "enabled" : "disabled"}` + ); +} + +/** + * Check that the menu states in the "save" toolbar button are correct. + * + * @param expected a dictionary containing the expected states + */ +async function check_toolbar_menu_states_single(expected) { + assert_shown("attachmentSaveAllSingle", true); + assert_shown("attachmentSaveAllMultiple", false); + + if (expected.save === false) { + assert_enabled("attachmentSaveAllSingle", false); + } else { + assert_enabled("attachmentSaveAllSingle", true); + let dm = aboutMessage.document.querySelector( + "#attachmentSaveAllSingle .toolbarbutton-menubutton-dropmarker" + ); + EventUtils.synthesizeMouseAtCenter(dm, { clickCount: 1 }, aboutMessage); + await wait_for_popup_to_open( + aboutMessage.document.getElementById("attachmentSaveAllSingleMenu") + ); + + try { + assert_enabled("button-openAttachment", expected.open); + assert_enabled("button-saveAttachment", expected.save); + assert_enabled("button-detachAttachment", expected.detach); + assert_enabled("button-deleteAttachment", expected.delete_); + } finally { + await close_popup( + aboutMessage, + aboutMessage.document.getElementById("attachmentSaveAllSingleMenu") + ); + } + } +} + +/** + * Check that the menu states in the "save all" toolbar button are correct. + * + * @param expected a dictionary containing the expected states + */ +async function check_toolbar_menu_states_multiple(expected) { + assert_shown("attachmentSaveAllSingle", false); + assert_shown("attachmentSaveAllMultiple", true); + + if (expected.save === false) { + assert_enabled("attachmentSaveAllMultiple", false); + } else { + assert_enabled("attachmentSaveAllMultiple", true); + let dm = aboutMessage.document.querySelector( + "#attachmentSaveAllMultiple .toolbarbutton-menubutton-dropmarker" + ); + EventUtils.synthesizeMouseAtCenter(dm, { clickCount: 1 }, aboutMessage); + await wait_for_popup_to_open( + aboutMessage.document.getElementById("attachmentSaveAllMultipleMenu") + ); + + try { + assert_enabled("button-openAllAttachments", expected.open); + assert_enabled("button-saveAllAttachments", expected.save); + assert_enabled("button-detachAllAttachments", expected.detach); + assert_enabled("button-deleteAllAttachments", expected.delete_); + } finally { + await close_popup( + mc, + aboutMessage.document.getElementById("attachmentSaveAllMultipleMenu") + ); + } + } +} + +/** + * Check that the menu states in the single item context menu are correct + * + * @param expected a dictionary containing the expected states + */ +async function check_menu_states_single(index, expected) { + let attachmentList = aboutMessage.document.getElementById("attachmentList"); + let node = attachmentList.getItemAtIndex(index); + + let contextMenu = aboutMessage.document.getElementById( + "attachmentItemContext" + ); + let shownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); + attachmentList.selectItem(node); + EventUtils.synthesizeMouseAtCenter( + node, + { type: "contextmenu" }, + aboutMessage + ); + await shownPromise; + + try { + assert_shown("context-openAttachment", true); + assert_shown("context-saveAttachment", true); + assert_shown("context-menu-separator", true); + assert_shown("context-detachAttachment", true); + assert_shown("context-deleteAttachment", true); + + assert_enabled("context-openAttachment", expected.open); + assert_enabled("context-saveAttachment", expected.save); + assert_enabled("context-detachAttachment", expected.detach); + assert_enabled("context-deleteAttachment", expected.delete_); + } finally { + let hiddenPromise = BrowserTestUtils.waitForEvent( + contextMenu, + "popuphidden" + ); + contextMenu.hidePopup(); + await hiddenPromise; + await new Promise(resolve => requestAnimationFrame(resolve)); + } +} + +/** + * Check that the menu states in the all items context menu are correct + * + * @param expected a dictionary containing the expected states + */ +async function check_menu_states_all(expected) { + // Using a rightClick here is unsafe, because we need to hit the empty area + // beside the attachment items and that seems to be different per platform. + // Using DOM methods to open the popup works fine. + let contextMenu = aboutMessage.document.getElementById( + "attachmentListContext" + ); + let shownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); + aboutMessage.document + .getElementById("attachmentListContext") + .openPopup(aboutMessage.document.getElementById("attachmentList")); + await shownPromise; + + try { + assert_shown("context-openAllAttachments", true); + assert_shown("context-saveAllAttachments", true); + assert_shown("context-menu-separator-all", true); + assert_shown("context-detachAllAttachments", true); + assert_shown("context-deleteAllAttachments", true); + + assert_enabled("context-openAllAttachments", expected.open); + assert_enabled("context-saveAllAttachments", expected.save); + assert_enabled("context-detachAllAttachments", expected.detach); + assert_enabled("context-deleteAllAttachments", expected.delete_); + } finally { + await close_popup( + aboutMessage, + aboutMessage.document.getElementById("attachmentListContext") + ); + } +} + +async function help_test_attachment_menus(index) { + await be_in_folder(folder); + select_click_row(index); + let expectedStates = messages[index].menuStates; + + let aboutMessage = get_about_message(); + aboutMessage.toggleAttachmentList(true); + + for (let attachment of aboutMessage.currentAttachments) { + // Ensure all attachments are resolved; other than external they already + // should be. + await attachment.isEmpty(); + } + + if (expectedStates.length == 1) { + await check_toolbar_menu_states_single(messages[index].allMenuStates); + } else { + await check_toolbar_menu_states_multiple(messages[index].allMenuStates); + } + + await check_menu_states_all(messages[index].allMenuStates); + for (let i = 0; i < expectedStates.length; i++) { + await check_menu_states_single(i, expectedStates[i]); + } +} + +// Generate a test for each message in |messages|. +for (let i = 0; i < messages.length; i++) { + add_task(function () { + return help_test_attachment_menus(i); + }); +} + +add_task(() => { + Assert.report( + false, + undefined, + undefined, + "Test ran to completion successfully" + ); +}); + +registerCleanupFunction(() => { + // Remove created folders. + folder.deleteSelf(null); +}); |