summaryrefslogtreecommitdiffstats
path: root/comm/mail/test/browser/composition/browser_addressWidgets.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/test/browser/composition/browser_addressWidgets.js')
-rw-r--r--comm/mail/test/browser/composition/browser_addressWidgets.js773
1 files changed, 773 insertions, 0 deletions
diff --git a/comm/mail/test/browser/composition/browser_addressWidgets.js b/comm/mail/test/browser/composition/browser_addressWidgets.js
new file mode 100644
index 0000000000..4e5db02a72
--- /dev/null
+++ b/comm/mail/test/browser/composition/browser_addressWidgets.js
@@ -0,0 +1,773 @@
+/* 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 proper enabling of addressing widgets.
+ */
+
+"use strict";
+
+var { click_menus_in_sequence } = ChromeUtils.import(
+ "resource://testing-common/mozmill/WindowHelpers.jsm"
+);
+
+var { close_compose_window, open_compose_new_mail } = ChromeUtils.import(
+ "resource://testing-common/mozmill/ComposeHelpers.jsm"
+);
+var { be_in_folder, FAKE_SERVER_HOSTNAME } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+var cwc = null; // compose window controller
+var accountPOP3 = null;
+var accountNNTP = null;
+var originalAccountCount;
+
+add_setup(function () {
+ // Ensure we're in the tinderbox account as that has the right identities set
+ // up for this test.
+ let server = MailServices.accounts.findServer(
+ "tinderbox",
+ FAKE_SERVER_HOSTNAME,
+ "pop3"
+ );
+ accountPOP3 = MailServices.accounts.FindAccountForServer(server);
+
+ // There may be pre-existing accounts from other tests.
+ originalAccountCount = MailServices.accounts.allServers.length;
+});
+
+/**
+ * Check if the address type items are in the wished state.
+ *
+ * @param {Window} win - The window to search in.
+ * @param {string[]} itemsEnabled - List of item values that should be visible.
+ */
+function check_address_types_state(win, itemsEnabled) {
+ for (let item of win.document.querySelectorAll(
+ "#extraAddressRowsMenu > menuitem"
+ )) {
+ let buttonId = item.dataset.buttonId;
+ let showRowEl;
+ if (buttonId) {
+ let button = win.document.getElementById(buttonId);
+ if (item.dataset.preferButton == "true") {
+ showRowEl = button;
+ Assert.ok(item.hidden, `${item.id} menuitem should be hidden`);
+ } else {
+ showRowEl = item;
+ Assert.ok(button.hidden, `${button.id} button should be hidden`);
+ }
+ } else {
+ showRowEl = item;
+ }
+
+ let type = item.id.replace(/ShowAddressRowMenuItem$/, "");
+
+ let expectShown = itemsEnabled.includes(type);
+ let row = win.document.querySelector(
+ `.address-row[data-recipienttype="${type}"]`
+ );
+ if (expectShown) {
+ // Either the row or the element that shows it should be visible, but not
+ // both.
+ if (row.classList.contains("hidden")) {
+ Assert.ok(
+ !showRowEl.hidden,
+ `${showRowEl.id} should be visible when the row is hidden`
+ );
+ } else {
+ Assert.ok(
+ showRowEl.hidden,
+ `${showRowEl.id} should be hidden when the row is visible`
+ );
+ }
+ } else {
+ // Both the row and the element that shows it should be hidden.
+ Assert.ok(row.classList.contains("hidden"), `${row.id} should be hidden`);
+ Assert.ok(showRowEl.hidden, `${showRowEl.id} should be hidden`);
+ }
+ }
+}
+
+/**
+ * With only a POP3 account, no News related address types should be enabled.
+ */
+function check_mail_address_types(win) {
+ check_address_types_state(win, [
+ "addr_to",
+ "addr_cc",
+ "addr_reply",
+ "addr_bcc",
+ ]);
+}
+
+/**
+ * With a NNTP account, all address types should be enabled.
+ */
+function check_nntp_address_types(win) {
+ check_address_types_state(win, [
+ "addr_to",
+ "addr_cc",
+ "addr_reply",
+ "addr_bcc",
+ "addr_newsgroups",
+ "addr_followup",
+ ]);
+}
+
+/**
+ * With an NNTP account, the 'To' addressing row should be hidden.
+ */
+function check_collapsed_pop_recipient(cwc) {
+ Assert.ok(
+ cwc.window.document
+ .getElementById("addressRowTo")
+ .classList.contains("hidden")
+ );
+}
+
+function add_NNTP_account() {
+ // Create a NNTP server
+ let nntpServer = MailServices.accounts
+ .createIncomingServer(null, "example.nntp.invalid", "nntp")
+ .QueryInterface(Ci.nsINntpIncomingServer);
+
+ let identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox2@example.invalid";
+
+ accountNNTP = MailServices.accounts.createAccount();
+ accountNNTP.incomingServer = nntpServer;
+ accountNNTP.addIdentity(identity);
+ // Now there should be 1 more account.
+ Assert.equal(
+ MailServices.accounts.allServers.length,
+ originalAccountCount + 1
+ );
+}
+
+function remove_NNTP_account() {
+ // Remove our NNTP account to leave the profile clean.
+ MailServices.accounts.removeAccount(accountNNTP);
+ // There should be only the original accounts left.
+ Assert.equal(MailServices.accounts.allServers.length, originalAccountCount);
+}
+
+/**
+ * Bug 399446 & bug 922614
+ * Test that the allowed address types depend on the account type
+ * we are sending from.
+ */
+add_task(async function test_address_types() {
+ // Be sure there is no NNTP account yet.
+ for (let account of MailServices.accounts.accounts) {
+ Assert.notEqual(
+ account.incomingServer.type,
+ "nntp",
+ "There is a NNTP account existing unexpectedly"
+ );
+ }
+
+ // Open compose window on the existing POP3 account.
+ await be_in_folder(accountPOP3.incomingServer.rootFolder);
+ cwc = open_compose_new_mail();
+ check_mail_address_types(cwc.window);
+ close_compose_window(cwc);
+
+ add_NNTP_account();
+
+ // From now on, we should always get all possible address types offered,
+ // regardless of which account is used of composing (bug 922614).
+ await be_in_folder(accountNNTP.incomingServer.rootFolder);
+ cwc = open_compose_new_mail();
+ check_nntp_address_types(cwc.window);
+ check_collapsed_pop_recipient(cwc);
+ close_compose_window(cwc);
+
+ // Now try the same accounts but choosing them in the From dropdown
+ // inside compose window.
+ await be_in_folder(accountPOP3.incomingServer.rootFolder);
+ cwc = open_compose_new_mail();
+ check_nntp_address_types(cwc.window);
+
+ let NNTPidentity = accountNNTP.defaultIdentity.key;
+ EventUtils.synthesizeMouseAtCenter(
+ cwc.window.document.getElementById("msgIdentity"),
+ {},
+ cwc.window.document.getElementById("msgIdentity").ownerGlobal
+ );
+ await click_menus_in_sequence(
+ cwc.window.document.getElementById("msgIdentityPopup"),
+ [{ identitykey: NNTPidentity }]
+ );
+ check_nntp_address_types(cwc.window);
+
+ // Switch back to the POP3 account.
+ let POP3identity = accountPOP3.defaultIdentity.key;
+ EventUtils.synthesizeMouseAtCenter(
+ cwc.window.document.getElementById("msgIdentity"),
+ {},
+ cwc.window.document.getElementById("msgIdentity").ownerGlobal
+ );
+ await click_menus_in_sequence(
+ cwc.window.document.getElementById("msgIdentityPopup"),
+ [{ identitykey: POP3identity }]
+ );
+ check_nntp_address_types(cwc.window);
+
+ close_compose_window(cwc);
+
+ remove_NNTP_account();
+
+ // Now the NNTP account is lost, so we should be back to mail only addresses.
+ await be_in_folder(accountPOP3.incomingServer.rootFolder);
+ cwc = open_compose_new_mail();
+ check_mail_address_types(cwc.window);
+ close_compose_window(cwc);
+});
+
+add_task(async function test_address_suppress_leading_comma_space() {
+ await be_in_folder(accountPOP3.incomingServer.rootFolder);
+ let controller = open_compose_new_mail();
+
+ let addrInput = controller.window.document.getElementById("toAddrInput");
+ Assert.ok(addrInput);
+ Assert.equal(addrInput.value, "");
+
+ // Create a pill.
+ addrInput.value = "person@org";
+ // Comma triggers the pill creation.
+ // Note: the address input should already have focus.
+ EventUtils.synthesizeKey(",", {}, controller.window);
+
+ let addrPill = await TestUtils.waitForCondition(
+ () =>
+ controller.window.document.querySelector(
+ "#toAddrContainer > .address-pill"
+ ),
+ "Pill creation"
+ );
+ Assert.equal(addrInput.value, "");
+ let pillInput = addrPill.querySelector("input");
+ Assert.ok(pillInput);
+
+ // Asserts that the input has the correct exceptional behaviour for 'comma'
+ // and 'space'.
+ async function assertKeyInput(input) {
+ // Since we will be partially testing for a lack of response to the " " and
+ // "," key presses, we first run the tests with the "a" key press to assure
+ // us that the tests would otherwise capture the normal behaviour. This will
+ // also shows us that the comma and space behaviour is exceptional.
+ for (let key of ["a", " ", ","]) {
+ // Clear input.
+ input.value = "";
+ await TestUtils.waitForTick();
+
+ // Type the key in an empty input.
+ let eventPromise = BrowserTestUtils.waitForEvent(input, "keydown");
+ EventUtils.synthesizeKey(key, {}, controller.window);
+ await eventPromise;
+
+ if (key === " " || key === ",") {
+ // Key is suppressed, so the input remains empty.
+ Assert.equal(input.value, "");
+ } else {
+ // Normal behaviour: key is added to the input.
+ Assert.equal(input.value, key);
+ }
+
+ // If the input is not empty, we should still have the normal behaviour.
+ input.value = "z";
+ input.selectionStart = 1;
+ input.SelectionEnd = 1;
+ await TestUtils.waitForTick();
+
+ await BrowserTestUtils.synthesizeKey(
+ key,
+ {},
+ controller.window.browsingContext
+ );
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ Assert.equal(input.value, "z" + key);
+
+ // Test typing the key to replace all the trimmed input.
+ // Sample text with two spaces as start and end. Also includes a 2
+ // character emoji.
+ let someText = " some, text📜 ";
+ for (let selection of [
+ { start: 0, end: 0 },
+ { start: 1, end: 0 },
+ { start: 0, end: 1 },
+ { start: 2, end: 2 },
+ ]) {
+ input.value = someText;
+ input.selectionStart = selection.start;
+ input.selectionEnd = someText.length - selection.end;
+ await TestUtils.waitForTick();
+
+ // Type the key to replace the text.
+ await BrowserTestUtils.synthesizeKey(
+ key,
+ {},
+ controller.window.browsingContext
+ );
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ if (key === " " || key === ",") {
+ // Key is suppressed and input is empty.
+ Assert.equal(input.value, "");
+ } else {
+ // Normal behaviour: key replaces the selected text.
+ Assert.equal(
+ input.value,
+ someText.slice(0, selection.start) +
+ key +
+ someText.slice(someText.length - selection.end)
+ );
+ }
+ }
+
+ // If we do not replace all the trimmed input, we should still have
+ // normal behaviour.
+ input.value = " text ";
+ input.selectionStart = 1;
+ // Select up to 'x'.
+ input.selectionEnd = 5;
+ await TestUtils.waitForTick();
+
+ await BrowserTestUtils.synthesizeKey(
+ key,
+ {},
+ controller.window.browsingContext
+ );
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ Assert.equal(input.value, " " + key + "t ");
+ }
+ }
+
+ // Assert that the address input has the correct behaviour for key presses.
+ // Note: the address input should still have focus.
+ await assertKeyInput(addrInput);
+
+ // Now test the behaviour when editing a pill.
+ // First, we need to get into editing mode by clicking the pill twice.
+ EventUtils.synthesizeMouseAtCenter(
+ addrPill,
+ { clickCount: 1 },
+ controller.window
+ );
+ let clickPromise = BrowserTestUtils.waitForEvent(addrPill, "click");
+ // We do not want a double click, but two separate clicks.
+ EventUtils.synthesizeMouseAtCenter(
+ addrPill,
+ { clickCount: 1 },
+ controller.window
+ );
+ await clickPromise;
+
+ Assert.ok(!pillInput.hidden);
+
+ // Assert that editing a pill has the same behaviour as the address input.
+ await assertKeyInput(pillInput);
+
+ close_compose_window(controller);
+});
+
+add_task(async function test_pill_creation_in_all_fields() {
+ await be_in_folder(accountPOP3.incomingServer.rootFolder);
+ let cwc = open_compose_new_mail();
+
+ let addresses = ["person@org", "foo@address.valid", "invalid", "foo@address"];
+ let subjectField = cwc.window.document.getElementById("msgSubject");
+
+ // Helper method to create multiple pills in a field.
+ async function assertPillsCreationInField(input) {
+ Assert.ok(input);
+ Assert.equal(input.value, "");
+
+ // Write an address in the field.
+ input.value = addresses[0];
+ // Enter triggers the pill creation.
+ EventUtils.synthesizeKey("VK_RETURN", {}, cwc.window);
+ // Assert the pill was created.
+ await TestUtils.waitForCondition(
+ () =>
+ input
+ .closest(".address-container")
+ .querySelectorAll("mail-address-pill").length == 1,
+ "Pills created"
+ );
+ // Assert the pill has the correct address.
+ Assert.equal(
+ input
+ .closest(".address-container")
+ .querySelectorAll("mail-address-pill")[0].emailAddress,
+ addresses[0]
+ );
+
+ // Write another address in the field.
+ input.value = addresses[1];
+ // Tab triggers the pill creation.
+ EventUtils.synthesizeKey("VK_TAB", {}, cwc.window);
+ // Assert the pill was created.
+ await TestUtils.waitForCondition(
+ () =>
+ input
+ .closest(".address-container")
+ .querySelectorAll("mail-address-pill").length == 2,
+ "Pills created"
+ );
+ // Assert the pill has the correct address.
+ Assert.equal(
+ input
+ .closest(".address-container")
+ .querySelectorAll("mail-address-pill")[1].emailAddress,
+ addresses[1]
+ );
+
+ // Write an invalid email address in the To field.
+ input.value = addresses[2];
+ // Enter triggers the pill creation.
+ EventUtils.synthesizeKey("VK_RETURN", {}, cwc.window);
+ // Assert that an invalid address pill was created.
+ await TestUtils.waitForCondition(
+ () =>
+ input
+ .closest(".address-container")
+ .querySelectorAll("mail-address-pill.invalid-address").length == 1,
+ "Invalid pill created"
+ );
+ // Assert the pill has the correct address.
+ Assert.equal(
+ input
+ .closest(".address-container")
+ .querySelector("mail-address-pill.invalid-address").emailAddress,
+ addresses[2]
+ );
+
+ // Write another address in the field.
+ input.value = addresses[3];
+ // Focusing on another element triggers the pill creation.
+ subjectField.focus();
+ // Assert the pill was created.
+ await TestUtils.waitForCondition(
+ () =>
+ input
+ .closest(".address-container")
+ .querySelectorAll("mail-address-pill").length == 4,
+ "Pills created"
+ );
+ // Assert the pill has the correct address.
+ Assert.equal(
+ input
+ .closest(".address-container")
+ .querySelectorAll("mail-address-pill")[3].emailAddress,
+ addresses[3]
+ );
+ }
+
+ // The To field is visible and focused by default when the compose window is
+ // first opened.
+ // Test pill creation for the To input field.
+ let toInput = cwc.window.document.getElementById("toAddrInput");
+ await assertPillsCreationInField(toInput);
+
+ // Click on the Cc recipient label.
+ let ccInput = cwc.window.document.getElementById("ccAddrInput");
+ EventUtils.synthesizeMouseAtCenter(
+ cwc.window.document.getElementById("addr_ccShowAddressRowButton"),
+ {},
+ cwc.window
+ );
+ // The Cc field should now be visible.
+ Assert.ok(
+ !ccInput.closest(".address-row").classList.contains("hidden"),
+ "The Cc field is visible"
+ );
+ // Test pill creation for the Cc input field.
+ await assertPillsCreationInField(ccInput);
+
+ // Click on the Bcc recipient label.
+ let bccInput = cwc.window.document.getElementById("bccAddrInput");
+ EventUtils.synthesizeMouseAtCenter(
+ cwc.window.document.getElementById("addr_bccShowAddressRowButton"),
+ {},
+ cwc.window
+ );
+ // The Bcc field should now be visible.
+ Assert.ok(
+ !bccInput.closest(".address-row").classList.contains("hidden"),
+ "The Bcc field is visible"
+ );
+ // Test pill creation for the Bcc input field.
+ await assertPillsCreationInField(bccInput);
+
+ // Focus on the Bcc field and hold press the Backspace key.
+ bccInput.focus();
+ EventUtils.synthesizeKey("KEY_Backspace", { repeat: 5 }, cwc.window);
+
+ // All pills should be deleted, but the focus should remain on the Bcc field.
+ Assert.equal(
+ bccInput.closest(".address-container").querySelectorAll("mail-address-pill")
+ .length,
+ 0,
+ "All pills in the Bcc field have been removed."
+ );
+ Assert.ok(
+ !bccInput.closest(".address-row").classList.contains("hidden"),
+ "The Bcc field is still visible"
+ );
+
+ // Press and hold Backspace again.
+ EventUtils.synthesizeKey("KEY_Backspace", { repeat: 2 }, cwc.window);
+
+ // Confirm the Bcc field is closed and the focus moved to the Cc field.
+ Assert.ok(
+ bccInput.closest(".address-row").classList.contains("hidden"),
+ "The Bcc field was closed"
+ );
+ Assert.equal(cwc.window.document.activeElement, ccInput);
+
+ // Now we're on the Cc field. Press and hold Backspace to delete all pills.
+ EventUtils.synthesizeKey("KEY_Backspace", { repeat: 5 }, cwc.window);
+
+ // All pills should be deleted, but the focus should remain on the Cc field.
+ Assert.equal(
+ ccInput.closest(".address-container").querySelectorAll("mail-address-pill")
+ .length,
+ 0,
+ "All pills in the Cc field have been removed."
+ );
+ Assert.ok(
+ !ccInput.closest(".address-row").classList.contains("hidden"),
+ "The Cc field is still visible"
+ );
+
+ // Press and hold Backspace again.
+ EventUtils.synthesizeKey("KEY_Backspace", { repeat: 2 }, cwc.window);
+
+ // Confirm the Cc field is closed and the focus moved to the To field.
+ Assert.ok(
+ ccInput.closest(".address-row").classList.contains("hidden"),
+ "The Cc field was closed"
+ );
+ Assert.equal(cwc.window.document.activeElement, toInput);
+
+ // Now we're on the To field. Press and hold Backspace to delete all pills.
+ EventUtils.synthesizeKey("KEY_Backspace", { repeat: 5 }, cwc.window);
+
+ // All pills should be deleted, but the focus should remain on the To field.
+ Assert.equal(
+ toInput.closest(".address-container").querySelectorAll("mail-address-pill")
+ .length,
+ 0,
+ "All pills in the To field have been removed."
+ );
+ Assert.ok(
+ !toInput.closest(".address-row").classList.contains("hidden"),
+ "The To field is still visible"
+ );
+
+ // Press and hold Backspace again.
+ EventUtils.synthesizeKey("KEY_Backspace", { repeat: 2 }, cwc.window);
+
+ // Long backspace keypress on the To field shouldn't do anything if the field
+ // is empty. Confirm the To field is still visible and the focus stays on the
+ // To field.
+ Assert.ok(
+ !toInput.closest(".address-row").classList.contains("hidden"),
+ "The To field is still visible"
+ );
+ Assert.equal(cwc.window.document.activeElement, toInput);
+
+ close_compose_window(cwc);
+});
+
+add_task(async function test_addressing_fields_shortcuts() {
+ await be_in_folder(accountPOP3.incomingServer.rootFolder);
+ let cwc = open_compose_new_mail();
+
+ let addrToInput = cwc.window.document.getElementById("toAddrInput");
+ // The To input field should be empty.
+ Assert.equal(addrToInput.value, "");
+ // The To input field should be the currently focused element.
+ Assert.equal(cwc.window.document.activeElement, addrToInput);
+
+ const modifiers =
+ AppConstants.platform == "macosx"
+ ? { accelKey: true, shiftKey: true }
+ : { ctrlKey: true, shiftKey: true };
+
+ let addrCcInput = cwc.window.document.getElementById("ccAddrInput");
+ let ccRowShownPromise = BrowserTestUtils.waitForCondition(
+ () => !addrCcInput.closest(".address-row").classList.contains("hidden"),
+ "The Cc addressing row is not visible."
+ );
+ // Press the Ctrl/Cmd+Shift+C.
+ EventUtils.synthesizeKey("C", modifiers, cwc.window);
+ // The Cc addressing row should be visible.
+ await ccRowShownPromise;
+ // The Cc input field should be currently focused.
+ Assert.equal(cwc.window.document.activeElement, addrCcInput);
+
+ let addrBccInput = cwc.window.document.getElementById("bccAddrInput");
+ let bccRowShownPromise = BrowserTestUtils.waitForCondition(
+ () => !addrBccInput.closest(".address-row").classList.contains("hidden"),
+ "The Bcc addressing row is not visible."
+ );
+ // Press the Ctrl/Cmd+Shift+B.
+ EventUtils.synthesizeKey("B", modifiers, cwc.window);
+ await bccRowShownPromise;
+ // The Bcc input field should be currently focused.
+ Assert.equal(cwc.window.document.activeElement, addrBccInput);
+
+ // Press the Ctrl/Cmd+Shift+T.
+ EventUtils.synthesizeKey("T", modifiers, cwc.window);
+ // The To input field should be the currently focused element.
+ Assert.equal(cwc.window.document.activeElement, addrToInput);
+
+ // Press the Ctrl/Cmd+Shift+C.
+ EventUtils.synthesizeKey("C", modifiers, cwc.window);
+ // The Cc input field should be currently focused.
+ Assert.equal(cwc.window.document.activeElement, addrCcInput);
+
+ // Press the Ctrl/Cmd+Shift+B.
+ EventUtils.synthesizeKey("B", modifiers, cwc.window);
+ // The Bcc input field should be currently focused.
+ Assert.equal(cwc.window.document.activeElement, addrBccInput);
+
+ close_compose_window(cwc);
+});
+
+add_task(async function test_pill_deletion_and_focus() {
+ await be_in_folder(accountPOP3.incomingServer.rootFolder);
+ let cwc = open_compose_new_mail();
+
+ // When the compose window is opened, the focus should be on the To field.
+ let toInput = cwc.window.document.getElementById("toAddrInput");
+ Assert.equal(cwc.window.document.activeElement, toInput);
+
+ const modifiers =
+ AppConstants.platform == "macosx" ? { accelKey: true } : { ctrlKey: true };
+ const addresses = "person@org, foo@address.valid, invalid, foo@address";
+
+ // Test the To field.
+ test_deletion_and_focus_on_input(cwc, toInput, addresses, modifiers);
+
+ // Reveal and test the Cc field.
+ EventUtils.synthesizeMouseAtCenter(
+ cwc.window.document.getElementById("addr_ccShowAddressRowButton"),
+ {},
+ cwc.window
+ );
+ test_deletion_and_focus_on_input(
+ cwc,
+ cwc.window.document.getElementById("ccAddrInput"),
+ addresses,
+ modifiers
+ );
+
+ // Reveal and test the Bcc field.
+ EventUtils.synthesizeMouseAtCenter(
+ cwc.window.document.getElementById("addr_bccShowAddressRowButton"),
+ {},
+ cwc.window
+ );
+ test_deletion_and_focus_on_input(
+ cwc,
+ cwc.window.document.getElementById("bccAddrInput"),
+ addresses,
+ modifiers
+ );
+
+ close_compose_window(cwc);
+});
+
+function test_deletion_and_focus_on_input(cwc, input, addresses, modifiers) {
+ // Focus on the input before adding anything to be sure keyboard shortcut are
+ // triggered from the right element.
+ input.focus();
+
+ // Fill the input field with a long of string of comma separated addresses.
+ input.value = addresses;
+
+ // Enter triggers the pill creation.
+ EventUtils.synthesizeKey("VK_RETURN", {}, cwc.window);
+
+ let container = input.closest(".address-container");
+ // We should now have 4 pills.
+ Assert.equal(
+ container.querySelectorAll("mail-address-pill").length,
+ 4,
+ "All pills in the field have been created."
+ );
+
+ // One pill should be flagged as invalid.
+ Assert.equal(
+ container.querySelectorAll("mail-address-pill.invalid-address").length,
+ 1,
+ "One created pill is invalid."
+ );
+
+ // After pills creation, the same field should be still focused.
+ Assert.equal(cwc.window.document.activeElement, input);
+
+ // Keypress left arrow should focus and select the last created pill.
+ EventUtils.synthesizeKey("KEY_ArrowLeft", {}, cwc.window);
+ Assert.equal(
+ container.querySelectorAll("mail-address-pill[selected]").length,
+ 1,
+ "One pill is currently selected."
+ );
+
+ // Pressing delete should delete the selected pill and move the focus back to
+ // the input.
+ EventUtils.synthesizeKey("KEY_Delete", {}, cwc.window);
+ Assert.equal(
+ container.querySelectorAll("mail-address-pill").length,
+ 3,
+ "One pill correctly deleted."
+ );
+ Assert.equal(cwc.window.document.activeElement, input);
+
+ // Keypress left arrow to select the last available pill.
+ EventUtils.synthesizeKey("KEY_ArrowLeft", {}, cwc.window);
+ Assert.equal(
+ container.querySelectorAll("mail-address-pill[selected]").length,
+ 1,
+ "One pill is currently selected."
+ );
+
+ // BackSpace should delete the pill and focus on the previous adjacent pill.
+ EventUtils.synthesizeKey("KEY_Backspace", {}, cwc.window);
+ Assert.equal(
+ container.querySelectorAll("mail-address-pill").length,
+ 2,
+ "One pill correctly deleted."
+ );
+ let selectedPill = container.querySelector("mail-address-pill[selected]");
+ Assert.equal(cwc.window.document.activeElement, selectedPill);
+
+ // Pressing CTRL+A should select all pills.
+ EventUtils.synthesizeKey("a", modifiers, cwc.window);
+ Assert.equal(
+ container.querySelectorAll("mail-address-pill[selected]").length,
+ 2,
+ "All remaining 2 pills are currently selected."
+ );
+
+ // BackSpace should delete all pills and focus on empty inptu field.
+ EventUtils.synthesizeKey("KEY_Backspace", {}, cwc.window);
+ Assert.equal(
+ container.querySelectorAll("mail-address-pill").length,
+ 0,
+ "All pills have been deleted."
+ );
+ Assert.equal(cwc.window.document.activeElement, input);
+}