diff options
Diffstat (limited to 'comm/mail/test/browser/composition/browser_attachment.js')
-rw-r--r-- | comm/mail/test/browser/composition/browser_attachment.js | 995 |
1 files changed, 995 insertions, 0 deletions
diff --git a/comm/mail/test/browser/composition/browser_attachment.js b/comm/mail/test/browser/composition/browser_attachment.js new file mode 100644 index 0000000000..23f8e9a089 --- /dev/null +++ b/comm/mail/test/browser/composition/browser_attachment.js @@ -0,0 +1,995 @@ +/* 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/. */ + +/** + * Tests attachment handling functionality of the message compose window. + */ + +"use strict"; + +var utils = ChromeUtils.import("resource://testing-common/mozmill/utils.jsm"); +var { + add_attachments, + close_compose_window, + delete_attachment, + open_compose_new_mail, + open_compose_with_forward, + open_compose_with_forward_as_attachments, +} = ChromeUtils.import("resource://testing-common/mozmill/ComposeHelpers.jsm"); +var { + add_message_to_folder, + be_in_folder, + create_folder, + create_message, + select_click_row, + wait_for_popup_to_open, +} = ChromeUtils.import( + "resource://testing-common/mozmill/FolderDisplayHelpers.jsm" +); +var { plan_for_modal_dialog, wait_for_modal_dialog } = ChromeUtils.import( + "resource://testing-common/mozmill/WindowHelpers.jsm" +); + +var messenger; +var folder; +var epsilon; +var filePrefix; + +var rawAttachment = + "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 b64Attachment = + "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwS" + + "FlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAA" + + "A5SURBVCiRY/z//z8DKYCJJNXkaGBgYGD4D8NQ5zUgiTVAxeBqSLaBkVRPM0KtIhrQ3km0jwe" + + "SNQAAlmAY+71EgFoAAAAASUVORK5CYII="; +var b64Size = 188; + +add_setup(async function () { + folder = await create_folder("ComposeAttachmentA"); + + 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. Since we're dealing with + * forwarded message data here, the bonus byte(s) appear twice. + */ + epsilon = AppConstants.platform == "win" ? 4 : 2; + filePrefix = AppConstants.platform == "win" ? "file:///C:/" : "file:///"; + + // create some messages that have various types of attachments + let messages = [ + // no attachment + {}, + // raw attachment + { + attachments: [{ body: rawAttachment, filename: "ubik.txt", format: "" }], + }, + // b64-encoded image attachment + { + attachments: [ + { + body: b64Attachment, + contentType: "image/png", + filename: "lines.png", + encoding: "base64", + format: "", + }, + ], + }, + ]; + + for (let i = 0; i < messages.length; i++) { + await add_message_to_folder([folder], create_message(messages[i])); + } +}); + +/** + * Make sure that the attachment's size is what we expect + * + * @param controller the controller for the compose window + * @param index the attachment to examine, as an index into the listbox + * @param expectedSize the expected size of the attachment, in bytes + */ +function check_attachment_size(controller, index, expectedSize) { + let bucket = controller.window.document.getElementById("attachmentBucket"); + let node = bucket.querySelectorAll("richlistitem.attachmentItem")[index]; + + // First, let's check that the attachment size is correct + let size = node.attachment.size; + if (Math.abs(size - expectedSize) > epsilon) { + throw new Error( + "Reported attachment size (" + + size + + ") not within epsilon " + + "of actual attachment size (" + + expectedSize + + ")" + ); + } + + // Next, make sure that the formatted size in the label is correct + let formattedSize = node.getAttribute("size"); + let expectedFormattedSize = messenger.formatFileSize(size); + if (formattedSize != expectedFormattedSize) { + throw new Error( + "Formatted attachment size (" + + formattedSize + + ") does not " + + "match expected value (" + + expectedFormattedSize + + ")" + ); + } +} + +/** + * Make sure that the attachment's size is not displayed + * + * @param controller the controller for the compose window + * @param index the attachment to examine, as an index into the listbox + */ +function check_no_attachment_size(controller, index) { + let bucket = controller.window.document.getElementById("attachmentBucket"); + let node = bucket.querySelectorAll("richlistitem.attachmentItem")[index]; + + if (node.attachment.size != -1) { + throw new Error("attachment.size attribute should be -1!"); + } + + // If there's no size, the size attribute is empty. + if (node.getAttribute("size") != "") { + throw new Error("Attachment size should not be displayed!"); + } +} + +/** + * Make sure that the total size of all attachments is what we expect. + * + * @param controller the controller for the compose window + * @param count the expected number of attachments + */ +function check_total_attachment_size(controller, count) { + let bucket = controller.window.document.getElementById("attachmentBucket"); + let nodes = bucket.querySelectorAll("richlistitem.attachmentItem"); + let sizeNode = controller.window.document.getElementById( + "attachmentBucketSize" + ); + + if (nodes.length != count) { + throw new Error( + "Saw " + nodes.length + " attachments, but expected " + count + ); + } + + let size = 0; + for (let i = 0; i < nodes.length; i++) { + let currSize = nodes[i].attachment.size; + if (currSize != -1) { + size += currSize; + } + } + + // Next, make sure that the formatted size in the label is correct + let expectedFormattedSize = messenger.formatFileSize(size); + if (sizeNode.textContent != expectedFormattedSize) { + throw new Error( + "Formatted attachment size (" + + sizeNode.textContent + + ") does not " + + "match expected value (" + + expectedFormattedSize + + ")" + ); + } +} + +add_task(function test_file_attachment() { + let cwc = open_compose_new_mail(); + + let url = filePrefix + "some/file/here.txt"; + let size = 1234; + + add_attachments(cwc, url, size); + check_attachment_size(cwc, 0, size); + check_total_attachment_size(cwc, 1); + + close_compose_window(cwc); +}); + +add_task(function test_webpage_attachment() { + let cwc = open_compose_new_mail(); + + add_attachments(cwc, "https://www.mozilla.org/"); + check_no_attachment_size(cwc, 0); + check_total_attachment_size(cwc, 1); + + close_compose_window(cwc); +}); + +add_task(function test_multiple_attachments() { + let cwc = open_compose_new_mail(); + + let files = [ + { name: "foo.txt", size: 1234 }, + { name: "bar.txt", size: 5678 }, + { name: "baz.txt", size: 9012 }, + ]; + for (let i = 0; i < files.length; i++) { + add_attachments(cwc, filePrefix + files[i].name, files[i].size); + check_attachment_size(cwc, i, files[i].size); + } + + check_total_attachment_size(cwc, files.length); + close_compose_window(cwc); +}); + +add_task(function test_delete_attachments() { + let cwc = open_compose_new_mail(); + + let files = [ + { name: "foo.txt", size: 1234 }, + { name: "bar.txt", size: 5678 }, + { name: "baz.txt", size: 9012 }, + ]; + for (let i = 0; i < files.length; i++) { + add_attachments(cwc, filePrefix + files[i].name, files[i].size); + check_attachment_size(cwc, i, files[i].size); + } + + delete_attachment(cwc, 0); + check_total_attachment_size(cwc, files.length - 1); + + close_compose_window(cwc); +}); + +function subtest_rename_attachment(cwc) { + cwc.window.document.getElementById("loginTextbox").value = "renamed.txt"; + cwc.window.document.querySelector("dialog").getButton("accept").doCommand(); +} + +add_task(function test_rename_attachment() { + let cwc = open_compose_new_mail(); + + let url = filePrefix + "some/file/here.txt"; + let size = 1234; + + add_attachments(cwc, url, size); + + // Now, rename the attachment. + let bucket = cwc.window.document.getElementById("attachmentBucket"); + let node = bucket.querySelector("richlistitem.attachmentItem"); + EventUtils.synthesizeMouseAtCenter(node, {}, node.ownerGlobal); + plan_for_modal_dialog("commonDialogWindow", subtest_rename_attachment); + cwc.window.RenameSelectedAttachment(); + wait_for_modal_dialog("commonDialogWindow"); + + Assert.equal(node.getAttribute("name"), "renamed.txt"); + + check_attachment_size(cwc, 0, size); + check_total_attachment_size(cwc, 1); + + close_compose_window(cwc); +}); + +function subtest_open_attachment(cwc) { + cwc.window.document.querySelector("dialog").getButton("cancel").doCommand(); +} + +add_task(function test_open_attachment() { + let cwc = open_compose_new_mail(); + + // set up our external file for attaching + let file = new FileUtils.File(getTestFilePath("data/attachment.txt")); + let fileHandler = Services.io + .getProtocolHandler("file") + .QueryInterface(Ci.nsIFileProtocolHandler); + let url = fileHandler.getURLSpecFromActualFile(file); + let size = file.fileSize; + + add_attachments(cwc, url, size); + + // Now, open the attachment. + let bucket = cwc.window.document.getElementById("attachmentBucket"); + let node = bucket.querySelector("richlistitem.attachmentItem"); + plan_for_modal_dialog("unknownContentTypeWindow", subtest_open_attachment); + EventUtils.synthesizeMouseAtCenter(node, { clickCount: 2 }, node.ownerGlobal); + wait_for_modal_dialog("unknownContentTypeWindow"); + + close_compose_window(cwc); +}); + +add_task(async function test_forward_raw_attachment() { + await be_in_folder(folder); + select_click_row(1); + + let cwc = open_compose_with_forward(); + check_attachment_size(cwc, 0, rawAttachment.length); + check_total_attachment_size(cwc, 1); + + close_compose_window(cwc); +}); + +add_task(async function test_forward_b64_attachment() { + await be_in_folder(folder); + select_click_row(2); + + let cwc = open_compose_with_forward(); + check_attachment_size(cwc, 0, b64Size); + check_total_attachment_size(cwc, 1); + + close_compose_window(cwc); +}); + +add_task(async function test_forward_message_as_attachment() { + await be_in_folder(folder); + let curMessage = select_click_row(0); + + let cwc = open_compose_with_forward_as_attachments(); + check_attachment_size(cwc, 0, curMessage.messageSize); + check_total_attachment_size(cwc, 1); + + close_compose_window(cwc); +}); + +add_task(async function test_forward_message_with_attachments_as_attachment() { + await be_in_folder(folder); + let curMessage = select_click_row(1); + + let cwc = open_compose_with_forward_as_attachments(); + check_attachment_size(cwc, 0, curMessage.messageSize); + check_total_attachment_size(cwc, 1); + + close_compose_window(cwc); +}); + +/** + * Check that the compose window has the attachments we expect. + * + * @param aController The controller for the compose window + * @param aNames An array of attachment names that are expected + */ +function check_attachment_names(aController, aNames) { + let bucket = aController.window.document.getElementById("attachmentBucket"); + Assert.equal(aNames.length, bucket.itemCount); + for (let i = 0; i < aNames.length; i++) { + Assert.equal(bucket.getItemAtIndex(i).getAttribute("name"), aNames[i]); + } +} + +/** + * Execute a test of attachment reordering actions and check the resulting order. + * + * @param aCwc The controller for the compose window + * @param aInitialAttachmentNames An array of attachment names specifying the + * initial set of attachments to be created + * @param aReorder_actions An array of objects specifying a reordering action: + * { select: array of attachment item indexes to select, + * button: ID of button to click in the reordering menu, + * key: keycode of key to press instead of a click, + * key_modifiers: { accelKey: bool, ctrlKey: bool + * shiftKey: bool, altKey: bool, etc.}, + * result: an array of attachment names in the new + * order that should result + * } + * @param openPanel {boolean} - Whether to open reorderAttachmentsPanel for the test + */ +async function subtest_reordering( + aCwc, + aInitialAttachmentNames, + aReorder_actions, + aOpenPanel = true +) { + let bucket = aCwc.window.document.getElementById("attachmentBucket"); + let panel; + + // Create a set of attachments for the test. + const size = 1234; + for (let name of aInitialAttachmentNames) { + add_attachments(aCwc, filePrefix + name, size); + } + await new Promise(resolve => setTimeout(resolve)); + Assert.equal(bucket.itemCount, aInitialAttachmentNames.length); + check_attachment_names(aCwc, aInitialAttachmentNames); + + if (aOpenPanel) { + // Bring up the reordering panel. + aCwc.window.showReorderAttachmentsPanel(); + await new Promise(resolve => setTimeout(resolve)); + panel = aCwc.window.document.getElementById("reorderAttachmentsPanel"); + await wait_for_popup_to_open(panel); + } + + for (let action of aReorder_actions) { + // Ensure selection. + bucket.clearSelection(); + for (let itemIndex of action.select) { + bucket.addItemToSelection(bucket.getItemAtIndex(itemIndex)); + } + // Take action. + if ("button" in action) { + EventUtils.synthesizeMouseAtCenter( + aCwc.window.document.getElementById(action.button), + {}, + aCwc.window.document.getElementById(action.button).ownerGlobal + ); + } else if ("key" in action) { + EventUtils.synthesizeKey(action.key, action.key_modifiers, aCwc.window); + } + await new Promise(resolve => setTimeout(resolve)); + // Check result. + check_attachment_names(aCwc, action.result); + } + + if (aOpenPanel) { + // Close the panel. + panel.hidePopup(); + utils.waitFor( + () => panel.state == "closed", + "Reordering panel didn't close" + ); + } + + // Clean up for a new set of attachments. + aCwc.window.RemoveAllAttachments(); +} + +/** + * Bug 663695, Bug 1417856, Bug 1426344, Bug 1425891, Bug 1427037. + * Check basic and advanced attachment reordering operations. + * This is the main function of this test. + */ +add_task(async function test_attachment_reordering() { + let cwc = open_compose_new_mail(); + let editorEl = cwc.window.GetCurrentEditorElement(); + let bucket = cwc.window.document.getElementById("attachmentBucket"); + let panel = cwc.window.document.getElementById("reorderAttachmentsPanel"); + // const openReorderPanelModifiers = + // (AppConstants.platform == "macosx") ? { controlKey: true } + // : { altKey: true }; + + // First, some checks if the 'Reorder Attachments' panel + // opens and closes correctly. + + // Create two attachments as otherwise the reordering panel won't open. + const size = 1234; + const initialAttachmentNames_0 = ["A1", "A2"]; + for (let name of initialAttachmentNames_0) { + add_attachments(cwc, filePrefix + name, size); + await new Promise(resolve => setTimeout(resolve)); + } + Assert.equal(bucket.itemCount, initialAttachmentNames_0.length); + check_attachment_names(cwc, initialAttachmentNames_0); + + // Show 'Reorder Attachments' panel via mouse clicks. + let contextMenu = cwc.window.document.getElementById( + "msgComposeAttachmentItemContext" + ); + let shownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); + EventUtils.synthesizeMouseAtCenter( + bucket.getItemAtIndex(1), + { type: "contextmenu" }, + cwc.window + ); + await shownPromise; + contextMenu.activateItem( + cwc.window.document.getElementById("composeAttachmentContext_reorderItem") + ); + await wait_for_popup_to_open(panel); + + // Click on the editor which should close the panel. + EventUtils.synthesizeMouseAtCenter(editorEl, {}, editorEl.ownerGlobal); + utils.waitFor( + () => panel.state == "closed", + "Reordering panel didn't close when editor was clicked." + ); + + // Clean up for a new set of attachments. + cwc.window.RemoveAllAttachments(); + + // Define checks for various moving operations. + // Check 1: basic, mouse-only. + const initialAttachmentNames_1 = ["a", "C", "B", "b", "bb", "x"]; + const reorderActions_1 = [ + { + select: [1, 2, 3], + button: "btn_sortAttachmentsToggle", + result: ["a", "b", "B", "C", "bb", "x"], + }, + { + select: [4], + button: "btn_moveAttachmentLeft", + result: ["a", "b", "B", "bb", "C", "x"], + }, + { + select: [5], + button: "btn_moveAttachmentFirst", + result: ["x", "a", "b", "B", "bb", "C"], + }, + { + select: [0], + button: "btn_moveAttachmentRight", + result: ["a", "x", "b", "B", "bb", "C"], + }, + { + select: [1], + button: "btn_moveAttachmentLast", + result: ["a", "b", "B", "bb", "C", "x"], + }, + { + select: [1, 3], + button: "btn_moveAttachmentBundleUp", + result: ["a", "b", "bb", "B", "C", "x"], + }, + // Bug 1417856 + { + select: [2], + button: "btn_sortAttachmentsToggle", + result: ["a", "b", "B", "bb", "C", "x"], + }, + ]; + + // Check 2: basic and advanced, mouse-only. + const initialAttachmentNames_2 = [ + "a", + "x", + "C", + "y1", + "y2", + "B", + "b", + "z", + "bb", + ]; + const reorderActions_2 = [ + // For starters: moving a single attachment around in the list. + { + select: [1], + button: "btn_moveAttachmentLeft", + result: ["x", "a", "C", "y1", "y2", "B", "b", "z", "bb"], + }, + { + select: [0], + button: "btn_moveAttachmentLast", + result: ["a", "C", "y1", "y2", "B", "b", "z", "bb", "x"], + }, + { + select: [8], + button: "btn_moveAttachmentFirst", + result: ["x", "a", "C", "y1", "y2", "B", "b", "z", "bb"], + }, + { + select: [0], + button: "btn_moveAttachmentRight", + result: ["a", "x", "C", "y1", "y2", "B", "b", "z", "bb"], + }, + + // Moving multiple, disjunct selection with inner block up/down as-is. + // This feature can be useful for multiple disjunct selection patterns + // in an alternating list of attachments like + // {photo1.jpg, description1.txt, photo2.jpg, description2.txt}, + // where the order of alternation should be inverted to become + // {description1.txt, photo1.jpg, description2.txt, photo2.txt}. + { + select: [1, 3, 4, 7], + button: "btn_moveAttachmentRight", + result: ["a", "C", "x", "B", "y1", "y2", "b", "bb", "z"], + }, + { + select: [2, 4, 5, 8], + button: "btn_moveAttachmentLeft", + result: ["a", "x", "C", "y1", "y2", "B", "b", "z", "bb"], + }, + { + select: [1, 3, 4, 7], + button: "btn_moveAttachmentLeft", + result: ["x", "a", "y1", "y2", "C", "B", "z", "b", "bb"], + }, + + // Folding multiple, disjunct selection with inner block towards top/bottom. + { + select: [0, 2, 3, 6], + button: "btn_moveAttachmentLeft", + result: ["x", "y1", "y2", "a", "C", "z", "B", "b", "bb"], + }, + { + select: [0, 1, 2, 5], + button: "btn_moveAttachmentLeft", + result: ["x", "y1", "y2", "a", "z", "C", "B", "b", "bb"], + }, + { + select: [0, 1, 2, 4], + button: "btn_moveAttachmentLeft", + result: ["x", "y1", "y2", "z", "a", "C", "B", "b", "bb"], + }, + { + select: [3, 5, 6, 8], + button: "btn_moveAttachmentRight", + result: ["x", "y1", "y2", "a", "z", "b", "C", "B", "bb"], + }, + { + select: [4, 6, 7, 8], + button: "btn_moveAttachmentRight", + result: ["x", "y1", "y2", "a", "b", "z", "C", "B", "bb"], + }, + + // Prepare scenario for and test 'Group together' (upwards). + { + select: [1, 2], + button: "btn_moveAttachmentRight", + result: ["x", "a", "y1", "y2", "b", "z", "C", "B", "bb"], + }, + { + select: [0, 2, 3, 5], + button: "btn_moveAttachmentRight", + result: ["a", "x", "b", "y1", "y2", "C", "z", "B", "bb"], + }, + { + select: [1, 3, 4, 6], + button: "btn_moveAttachmentBundleUp", + result: ["a", "x", "y1", "y2", "z", "b", "C", "B", "bb"], + }, + // 'Group together' (downwards) is not tested here because it is + // only available via keyboard shortcuts, e.g. Alt+Cursor Right. + + // Sort selected attachments only. + // Unsorted multiple selection must be collapsed upwards first if disjunct, + // then sorted ascending. + { + select: [0, 5, 6, 8], + button: "btn_sortAttachmentsToggle", + result: ["a", "b", "bb", "C", "x", "y1", "y2", "z", "B"], + }, + // Sorted multiple block selection must be sorted the other way round. + { + select: [0, 1, 2, 3], + button: "btn_sortAttachmentsToggle", + result: ["C", "bb", "b", "a", "x", "y1", "y2", "z", "B"], + }, + // Sorted, multiple, disjunct selection must just be collapsed upwards. + { + select: [3, 8], + button: "btn_sortAttachmentsToggle", + result: ["C", "bb", "b", "a", "B", "x", "y1", "y2", "z"], + }, + { + select: [0, 2, 3], + button: "btn_sortAttachmentsToggle", + result: ["C", "b", "a", "bb", "B", "x", "y1", "y2", "z"], + }, + + // Bug 1417856: Sort all attachments when 1 or no attachment selected. + { + select: [1], + button: "btn_sortAttachmentsToggle", + result: ["a", "b", "B", "bb", "C", "x", "y1", "y2", "z"], + }, + { + select: [], + button: "btn_sortAttachmentsToggle", + result: ["z", "y2", "y1", "x", "C", "bb", "B", "b", "a"], + }, + + // Collapsing multiple, disjunct selection with inner block to top/bottom. + { + select: [3, 5, 6, 8], + button: "btn_moveAttachmentFirst", + result: ["x", "bb", "B", "a", "z", "y2", "y1", "C", "b"], + }, + { + select: [0, 2, 3, 7], + button: "btn_moveAttachmentLast", + result: ["bb", "z", "y2", "y1", "b", "x", "B", "a", "C"], + }, + ]; + + // Check 3: basic and advanced, keyboard-only. + const initialAttachmentNames_3 = [ + "a", + "x", + "C", + "y1", + "y2", + "B", + "b", + "z", + "bb", + ]; + const modAlt = { altKey: true }; + const modifiers2 = + AppConstants.platform == "macosx" + ? { accelKey: true, altKey: true } + : { altKey: true }; + const reorderActions_3 = [ + // For starters: moving a single attachment around in the list. + { + select: [1], + // key_moveAttachmentLeft + key: "VK_LEFT", + key_modifiers: modAlt, + result: ["x", "a", "C", "y1", "y2", "B", "b", "z", "bb"], + }, + { + select: [0], + // key_moveAttachmentBottom + key: AppConstants.platform == "macosx" ? "VK_DOWN" : "VK_END", + key_modifiers: modifiers2, + result: ["a", "C", "y1", "y2", "B", "b", "z", "bb", "x"], + }, + { + select: [8], + // key_moveAttachmentTop + key: AppConstants.platform == "macosx" ? "VK_UP" : "VK_HOME", + key_modifiers: modifiers2, + result: ["x", "a", "C", "y1", "y2", "B", "b", "z", "bb"], + }, + { + select: [0], + // key_moveAttachmentBottom2 (secondary shortcut on MAC, same as Win primary) + key: "VK_END", + key_modifiers: modAlt, + result: ["a", "C", "y1", "y2", "B", "b", "z", "bb", "x"], + }, + { + select: [8], + // key_moveAttachmentTop2 (secondary shortcut on MAC, same as Win primary) + key: "VK_HOME", + key_modifiers: modAlt, + result: ["x", "a", "C", "y1", "y2", "B", "b", "z", "bb"], + }, + { + select: [0], + // key_moveAttachmentRight + key: "VK_RIGHT", + key_modifiers: modAlt, + result: ["a", "x", "C", "y1", "y2", "B", "b", "z", "bb"], + }, + + // Moving multiple, disjunct selection with inner block up/down as-is. + // This feature can be useful for multiple disjunct selection patterns + // in an alternating list of attachments like + // {photo1.jpg, description1.txt, photo2.jpg, description2.txt}, + // where the order of alternation should be inverted to become + // {description1.txt, photo1.jpg, description2.txt, photo2.txt}. + { + select: [1, 3, 4, 7], + // key_moveAttachmentRight + key: "VK_RIGHT", + key_modifiers: modAlt, + result: ["a", "C", "x", "B", "y1", "y2", "b", "bb", "z"], + }, + { + select: [2, 4, 5, 8], + // key_moveAttachmentLeft + key: "VK_LEFT", + key_modifiers: modAlt, + result: ["a", "x", "C", "y1", "y2", "B", "b", "z", "bb"], + }, + { + select: [1, 3, 4, 7], + // key_moveAttachmentLeft + key: "VK_LEFT", + key_modifiers: modAlt, + result: ["x", "a", "y1", "y2", "C", "B", "z", "b", "bb"], + }, + + // Folding multiple, disjunct selection with inner block towards top/bottom. + { + select: [0, 2, 3, 6], + // key_moveAttachmentLeft + key: "VK_LEFT", + key_modifiers: modAlt, + result: ["x", "y1", "y2", "a", "C", "z", "B", "b", "bb"], + }, + { + select: [0, 1, 2, 5], + // key_moveAttachmentLeft + key: "VK_LEFT", + key_modifiers: modAlt, + result: ["x", "y1", "y2", "a", "z", "C", "B", "b", "bb"], + }, + { + select: [0, 1, 2, 4], + // key_moveAttachmentLeft + key: "VK_LEFT", + key_modifiers: modAlt, + result: ["x", "y1", "y2", "z", "a", "C", "B", "b", "bb"], + }, + { + select: [3, 5, 6, 8], + // key_moveAttachmentRight + key: "VK_RIGHT", + key_modifiers: modAlt, + result: ["x", "y1", "y2", "a", "z", "b", "C", "B", "bb"], + }, + { + select: [4, 6, 7, 8], + // key_moveAttachmentRight + key: "VK_RIGHT", + key_modifiers: modAlt, + result: ["x", "y1", "y2", "a", "b", "z", "C", "B", "bb"], + }, + + // Prepare scenario for and test 'Group together' (upwards/downwards). + { + select: [1, 2], + // key_moveAttachmentRight + key: "VK_RIGHT", + key_modifiers: modAlt, + result: ["x", "a", "y1", "y2", "b", "z", "C", "B", "bb"], + }, + { + select: [0, 2, 3, 5], + // key_moveAttachmentRight + key: "VK_RIGHT", + key_modifiers: modAlt, + result: ["a", "x", "b", "y1", "y2", "C", "z", "B", "bb"], + }, + { + select: [1, 3, 4, 6], + // key_moveAttachmentBundleUp + key: "VK_UP", + key_modifiers: modAlt, + result: ["a", "x", "y1", "y2", "z", "b", "C", "B", "bb"], + }, + { + select: [5, 6], + // key_moveAttachmentLeft + key: "VK_LEFT", + key_modifiers: modAlt, + result: ["a", "x", "y1", "y2", "b", "C", "z", "B", "bb"], + }, + { + select: [0, 4, 5, 7], + // key_moveAttachmentBundleDown + key: "VK_DOWN", + key_modifiers: modAlt, + result: ["x", "y1", "y2", "z", "a", "b", "C", "B", "bb"], + }, + + // Collapsing multiple, disjunct selection with inner block to top/bottom. + { + select: [0, 4, 5, 7], + // key_moveAttachmentTop + key: AppConstants.platform == "macosx" ? "VK_UP" : "VK_HOME", + key_modifiers: modifiers2, + result: ["x", "a", "b", "B", "y1", "y2", "z", "C", "bb"], + }, + { + select: [0, 4, 5, 6], + // key_moveAttachmentBottom + key: AppConstants.platform == "macosx" ? "VK_DOWN" : "VK_END", + key_modifiers: modifiers2, + result: ["a", "b", "B", "C", "bb", "x", "y1", "y2", "z"], + }, + { + select: [0, 1, 3, 4], + // key_moveAttachmentBottom2 (secondary shortcut on MAC, same as Win primary) + key: "VK_END", + key_modifiers: modAlt, + result: ["B", "x", "y1", "y2", "z", "a", "b", "C", "bb"], + }, + { + select: [5, 6, 7, 8], + // key_moveAttachmentTop2 (secondary shortcut on MAC, same as Win primary) + key: "VK_HOME", + key_modifiers: modAlt, + result: ["a", "b", "C", "bb", "B", "x", "y1", "y2", "z"], + }, + ]; + + // Check 4: Alt+Y keyboard shortcut for sorting (Bug 1425891). + const initialAttachmentNames_4 = [ + "a", + "x", + "C", + "y1", + "y2", + "B", + "b", + "z", + "bb", + ]; + + const reorderActions_4 = [ + { + select: [1], + // key_sortAttachmentsToggle + key: "y", + key_modifiers: modAlt, + result: ["a", "b", "B", "bb", "C", "x", "y1", "y2", "z"], + }, + ]; + + // Execute the tests of reordering actions as defined above. + await subtest_reordering(cwc, initialAttachmentNames_1, reorderActions_1); + await subtest_reordering(cwc, initialAttachmentNames_2, reorderActions_2); + // Check 3 (keyboard-only) with panel open. + await subtest_reordering(cwc, initialAttachmentNames_3, reorderActions_3); + // Check 3 (keyboard-only) without panel. + await subtest_reordering( + cwc, + initialAttachmentNames_3, + reorderActions_3, + false + ); + // Check 4 (Alt+Y keyboard shortcut for sorting) without panel. + await subtest_reordering( + cwc, + initialAttachmentNames_4, + reorderActions_4, + false + ); + // Check 4 (Alt+Y keyboard shortcut for sorting) with panel open. + await subtest_reordering(cwc, initialAttachmentNames_4, reorderActions_4); + // XXX When the root problem of bug 1425891 has been found and fixed, we should + // test here if the panel stays open as it should, esp. on Windows. + + close_compose_window(cwc); +}); + +add_task(async function test_restore_attachment_bucket_height() { + let cwc = open_compose_new_mail(); + + let attachmentArea = cwc.window.document.getElementById("attachmentArea"); + let attachmentBucket = cwc.window.document.getElementById("attachmentBucket"); + + Assert.ok( + BrowserTestUtils.is_hidden(attachmentArea), + "Attachment area should be hidden initially with no attachments" + ); + + // Add 9 attachments to open a pane least 2 rows height. + let files = [ + { name: "foo.txt", size: 1234 }, + { name: "bar.txt", size: 5678 }, + { name: "baz.txt", size: 9012 }, + { name: "foo2.txt", size: 1234 }, + { name: "bar2.txt", size: 5678 }, + { name: "baz2.txt", size: 9012 }, + { name: "foo3.txt", size: 1234 }, + { name: "bar3.txt", size: 5678 }, + { name: "baz3.txt", size: 9012 }, + ]; + for (let i = 0; i < files.length; i++) { + add_attachments(cwc, filePrefix + files[i].name, files[i].size); + } + + // Store the height of the attachment bucket. + let heightBefore = attachmentBucket.getBoundingClientRect().height; + + let modifiers = + AppConstants.platform == "macosx" + ? { accelKey: true, shiftKey: true } + : { ctrlKey: true, shiftKey: true }; + + let collapsedPromise = BrowserTestUtils.waitForCondition( + () => BrowserTestUtils.is_visible(attachmentArea) && !attachmentArea.open, + "The attachment area should be visible but closed." + ); + + // Press Ctrl/Cmd+Shift+M to collapse the attachment pane. + EventUtils.synthesizeKey("M", modifiers, cwc.window); + await collapsedPromise; + + let visiblePromise = BrowserTestUtils.waitForCondition( + () => BrowserTestUtils.is_visible(attachmentArea) && attachmentArea.open, + "The attachment area should be visible and open." + ); + // Press Ctrl/Cmd+Shift+M again. + EventUtils.synthesizeKey("M", modifiers, cwc.window); + await visiblePromise; + + // The height of these elements should have been properly restored. + Assert.equal(attachmentBucket.getBoundingClientRect().height, heightBefore); + + close_compose_window(cwc); +}); |