diff options
Diffstat (limited to 'comm/mail/components/addrbook/test/browser/browser_contact_tree.js')
-rw-r--r-- | comm/mail/components/addrbook/test/browser/browser_contact_tree.js | 1261 |
1 files changed, 1261 insertions, 0 deletions
diff --git a/comm/mail/components/addrbook/test/browser/browser_contact_tree.js b/comm/mail/components/addrbook/test/browser/browser_contact_tree.js new file mode 100644 index 0000000000..f502fe855a --- /dev/null +++ b/comm/mail/components/addrbook/test/browser/browser_contact_tree.js @@ -0,0 +1,1261 @@ +/* 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/. */ + +function rightClickOnIndex(index) { + let abWindow = getAddressBookWindow(); + let cardsList = abWindow.cardsPane.cardsList; + let menu = abWindow.document.getElementById("cardContext"); + + let shownPromise = BrowserTestUtils.waitForEvent(menu, "popupshown"); + EventUtils.synthesizeMouseAtCenter( + cardsList.getRowAtIndex(index), + { type: "contextmenu" }, + abWindow + ); + return shownPromise; +} + +/** + * Tests that additions and removals are accurately displayed, or not + * displayed if they happen outside the current address book. + */ +add_task(async function test_additions_and_removals() { + async function deleteRowWithPrompt(index) { + let promptPromise = BrowserTestUtils.promiseAlertDialogOpen("accept"); + EventUtils.synthesizeMouseAtCenter( + cardsList.getRowAtIndex(index), + {}, + abWindow + ); + EventUtils.synthesizeKey("VK_DELETE", {}, abWindow); + await promptPromise; + await new Promise(r => abWindow.setTimeout(r)); + await new Promise(r => abWindow.setTimeout(r)); + } + + let bookA = createAddressBook("book A"); + let contactA1 = bookA.addCard(createContact("contact", "A1")); + let bookB = createAddressBook("book B"); + let contactB1 = bookB.addCard(createContact("contact", "B1")); + + let abWindow = await openAddressBookWindow(); + let cardsList = abWindow.cardsPane.cardsList; + + await openAllAddressBooks(); + info("Performing check #1"); + checkCardsListed(contactA1, contactB1); + + // While in bookA, add a contact and list. Check that they show up. + openDirectory(bookA); + checkCardsListed(contactA1); + let contactA2 = bookA.addCard(createContact("contact", "A2")); // Add A2. + checkCardsListed(contactA1, contactA2); + let listC = bookA.addMailList(createMailingList("list C")); // Add C. + checkDirectoryDisplayed(bookA); + checkCardsListed(contactA1, contactA2, listC); + listC.addCard(contactA1); + checkCardsListed(contactA1, contactA2, listC); + + await openAllAddressBooks(); + info("Performing check #2"); + checkCardsListed(contactA1, contactA2, contactB1, listC); + + // While in listC, add a member and remove a member. Check that they show up + // or disappear as appropriate. + openDirectory(listC); + checkCardsListed(contactA1); + listC.addCard(contactA2); + checkCardsListed(contactA1, contactA2); + await deleteRowWithPrompt(0); + checkCardsListed(contactA2); + Assert.equal(cardsList.currentIndex, 0); + + await openAllAddressBooks(); + info("Performing check #3"); + checkCardsListed(contactA1, contactA2, contactB1, listC); + + // While in bookA, delete a contact. Check it disappears. + openDirectory(bookA); + checkCardsListed(contactA1, contactA2, listC); + await deleteRowWithPrompt(0); // Delete A1. + checkCardsListed(contactA2, listC); + Assert.equal(cardsList.currentIndex, 0); + // Now do some things in an unrelated book. Check nothing changes here. + let contactB2 = bookB.addCard(createContact("contact", "B2")); // Add B2. + checkCardsListed(contactA2, listC); + let listD = bookB.addMailList(createMailingList("list D")); // Add D. + checkDirectoryDisplayed(bookA); + checkCardsListed(contactA2, listC); + listD.addCard(contactB1); + checkCardsListed(contactA2, listC); + + await openAllAddressBooks(); + info("Performing check #4"); + checkCardsListed(contactA2, contactB1, contactB2, listC, listD); + + // While in listC, do some things in an unrelated list. Check nothing + // changes here. + openDirectory(listC); + checkCardsListed(contactA2); + listD.addCard(contactB2); + checkCardsListed(contactA2); + listD.deleteCards([contactB1]); + checkCardsListed(contactA2); + bookB.deleteCards([contactB1]); + checkCardsListed(contactA2); + + await openAllAddressBooks(); + info("Performing check #5"); + checkCardsListed(contactA2, contactB2, listC, listD); + + // While in bookA, do some things in an unrelated book. Check nothing + // changes here. + openDirectory(bookA); + checkCardsListed(contactA2, listC); + bookB.deleteDirectory(listD); // Delete D. + checkDirectoryDisplayed(bookA); + checkCardsListed(contactA2, listC); + await deleteRowWithPrompt(1); // Delete C. + checkCardsListed(contactA2); + + // While in "All Address Books", make some changes and check that things + // appear or disappear as appropriate. + await openAllAddressBooks(); + info("Performing check #6"); + checkCardsListed(contactA2, contactB2); + let listE = bookB.addMailList(createMailingList("list E")); // Add E. + checkDirectoryDisplayed(null); + checkCardsListed(contactA2, contactB2, listE); + listE.addCard(contactB2); + checkCardsListed(contactA2, contactB2, listE); + listE.deleteCards([contactB2]); + checkCardsListed(contactA2, contactB2, listE); + bookB.deleteDirectory(listE); // Delete E. + checkDirectoryDisplayed(null); + checkCardsListed(contactA2, contactB2); + await deleteRowWithPrompt(1); + checkCardsListed(contactA2); + Assert.equal(cardsList.currentIndex, 0); + bookA.deleteCards([contactA2]); + checkCardsListed(); + Assert.equal(cardsList.currentIndex, -1); + + // While in "All Address Books", delete a directory that has contacts and + // mailing lists. They should disappear. + let contactA3 = bookA.addCard(createContact("contact", "A3")); // Add A3. + checkCardsListed(contactA3); + let listF = bookA.addMailList(createMailingList("list F")); // Add F. + checkCardsListed(contactA3, listF); + await promiseDirectoryRemoved(bookA.URI); + checkCardsListed(); + + abWindow.close(); + + await promiseDirectoryRemoved(bookB.URI); +}); + +/** + * Tests that added contacts are inserted in the right place in the list. + */ +add_task(async function test_insertion_order() { + await openAddressBookWindow(); + + let bookA = createAddressBook("book A"); + openDirectory(bookA); + checkCardsListed(); + let contactA2 = bookA.addCard(createContact("contact", "A2")); + checkCardsListed(contactA2); + let contactA1 = bookA.addCard(createContact("contact", "A1")); // Add first. + checkCardsListed(contactA1, contactA2); + let contactA5 = bookA.addCard(createContact("contact", "A5")); // Add last. + checkCardsListed(contactA1, contactA2, contactA5); + let contactA3 = bookA.addCard(createContact("contact", "A3")); // Add in the middle. + checkCardsListed(contactA1, contactA2, contactA3, contactA5); + + // Flip sort direction. + await showSortMenu("sort", "GeneratedName descending"); + + checkCardsListed(contactA5, contactA3, contactA2, contactA1); + let contactA4 = bookA.addCard(createContact("contact", "A4")); // Add in the middle. + checkCardsListed(contactA5, contactA4, contactA3, contactA2, contactA1); + let contactA7 = bookA.addCard(createContact("contact", "A7")); // Add first. + checkCardsListed( + contactA7, + contactA5, + contactA4, + contactA3, + contactA2, + contactA1 + ); + let contactA0 = bookA.addCard(createContact("contact", "A0")); // Add last. + checkCardsListed( + contactA7, + contactA5, + contactA4, + contactA3, + contactA2, + contactA1, + contactA0 + ); + + contactA3.displayName = "contact A6"; + contactA3.lastName = "contact A3"; + contactA3.primaryEmail = "contact.A6@invalid"; + bookA.modifyCard(contactA3); // Rename, should change position. + checkCardsListed( + contactA7, + contactA3, // Actually A6. + contactA5, + contactA4, + contactA2, + contactA1, + contactA0 + ); + + // Restore original sort direction. + await showSortMenu("sort", "GeneratedName ascending"); + + checkCardsListed( + contactA0, + contactA1, + contactA2, + contactA4, + contactA5, + contactA3, // Actually A6. + contactA7 + ); + await closeAddressBookWindow(); + + await promiseDirectoryRemoved(bookA.URI); +}); + +/** + * Tests the name column is updated when the format changes. + */ +add_task(async function test_name_column() { + const { + GENERATE_DISPLAY_NAME, + GENERATE_LAST_FIRST_ORDER, + GENERATE_FIRST_LAST_ORDER, + } = Ci.nsIAbCard; + + let book = createAddressBook("book"); + book.addCard(createContact("alpha", "tango", "kilo")); + book.addCard(createContact("bravo", "zulu", "quebec")); + book.addCard(createContact("charlie", "mike", "whiskey")); + book.addCard(createContact("delta", "foxtrot", "sierra")); + book.addCard(createContact("echo", "november", "uniform")); + + let abWindow = await openAddressBookWindow(); + let cardsList = abWindow.cardsPane.cardsList; + + // Check the format is display name, ascending. + Assert.equal( + Services.prefs.getIntPref("mail.addr_book.lastnamefirst"), + GENERATE_DISPLAY_NAME + ); + + checkNamesListed("kilo", "quebec", "sierra", "uniform", "whiskey"); + + // Select the "delta foxtrot" contact. This should remain selected throughout. + cardsList.selectedIndex = 2; + Assert.equal(cardsList.selectedIndex, 2); + + // Change the format to last, first. + await showSortMenu("format", GENERATE_LAST_FIRST_ORDER); + checkNamesListed( + "foxtrot, delta", + "mike, charlie", + "november, echo", + "tango, alpha", + "zulu, bravo" + ); + Assert.equal(cardsList.selectedIndex, 0); + Assert.deepEqual(cardsList.selectedIndices, [0]); + + // Change the format to first last. + await showSortMenu("format", GENERATE_FIRST_LAST_ORDER); + checkNamesListed( + "alpha tango", + "bravo zulu", + "charlie mike", + "delta foxtrot", + "echo november" + ); + Assert.equal(cardsList.selectedIndex, 3); + + // Flip the order to descending. + await showSortMenu("sort", "GeneratedName descending"); + + checkNamesListed( + "echo november", + "delta foxtrot", + "charlie mike", + "bravo zulu", + "alpha tango" + ); + Assert.equal(cardsList.selectedIndex, 1); + + // Change the format to last, first. + await showSortMenu("format", GENERATE_LAST_FIRST_ORDER); + checkNamesListed( + "zulu, bravo", + "tango, alpha", + "november, echo", + "mike, charlie", + "foxtrot, delta" + ); + Assert.equal(cardsList.selectedIndex, 4); + + // Change the format to display name. + await showSortMenu("format", GENERATE_DISPLAY_NAME); + checkNamesListed("whiskey", "uniform", "sierra", "quebec", "kilo"); + Assert.equal(cardsList.selectedIndex, 2); + + // Sort by email address, ascending. + await showSortMenu("sort", "EmailAddresses ascending"); + + checkNamesListed("kilo", "quebec", "whiskey", "sierra", "uniform"); + Assert.equal(cardsList.selectedIndex, 3); + + // Change the format to last, first. + await showSortMenu("format", GENERATE_LAST_FIRST_ORDER); + checkNamesListed( + "tango, alpha", + "zulu, bravo", + "mike, charlie", + "foxtrot, delta", + "november, echo" + ); + Assert.equal(cardsList.selectedIndex, 3); + + // Change the format to first last. + await showSortMenu("format", GENERATE_FIRST_LAST_ORDER); + checkNamesListed( + "alpha tango", + "bravo zulu", + "charlie mike", + "delta foxtrot", + "echo november" + ); + Assert.equal(cardsList.selectedIndex, 3); + + // Change the format to display name. + await showSortMenu("format", GENERATE_DISPLAY_NAME); + checkNamesListed("kilo", "quebec", "whiskey", "sierra", "uniform"); + Assert.equal(cardsList.selectedIndex, 3); + + // Restore original sort column and direction. + await showSortMenu("sort", "GeneratedName ascending"); + + checkNamesListed("kilo", "quebec", "sierra", "uniform", "whiskey"); + Assert.equal(cardsList.selectedIndex, 2); + + await closeAddressBookWindow(); + + await promiseDirectoryRemoved(book.URI); +}); + +/** + * Tests that sort order and name format survive closing and reopening. + */ +add_task(async function test_persistence() { + let book = createAddressBook("book"); + book.addCard(createContact("alpha", "tango", "kilo")); + book.addCard(createContact("bravo", "zulu", "quebec")); + book.addCard(createContact("charlie", "mike", "whiskey")); + book.addCard(createContact("delta", "foxtrot", "sierra")); + book.addCard(createContact("echo", "november", "uniform")); + + Services.xulStore.removeDocument("about:addressbook"); + Services.prefs.clearUserPref("mail.addr_book.lastnamefirst"); + + await openAddressBookWindow(); + checkNamesListed("kilo", "quebec", "sierra", "uniform", "whiskey"); + + info("sorting by GeneratedName, descending"); + await showSortMenu("sort", "GeneratedName descending"); + checkNamesListed("whiskey", "uniform", "sierra", "quebec", "kilo"); + + await closeAddressBookWindow(); + info("address book closed, reopening"); + await openAddressBookWindow(); + checkNamesListed("whiskey", "uniform", "sierra", "quebec", "kilo"); + + info("sorting by EmailAddresses, ascending"); + await showSortMenu("sort", "EmailAddresses ascending"); + checkNamesListed("kilo", "quebec", "whiskey", "sierra", "uniform"); + + await closeAddressBookWindow(); + info("address book closed, reopening"); + await openAddressBookWindow(); + checkNamesListed("kilo", "quebec", "whiskey", "sierra", "uniform"); + + info("setting name format to first last"); + await showSortMenu("format", Ci.nsIAbCard.GENERATE_FIRST_LAST_ORDER); + checkNamesListed( + "alpha tango", + "bravo zulu", + "charlie mike", + "delta foxtrot", + "echo november" + ); + + await closeAddressBookWindow(); + info("address book closed, reopening"); + await openAddressBookWindow(); + checkNamesListed( + "alpha tango", + "bravo zulu", + "charlie mike", + "delta foxtrot", + "echo november" + ); + + await closeAddressBookWindow(); + + Services.xulStore.removeDocument("about:addressbook"); + Services.prefs.clearUserPref("mail.addr_book.lastnamefirst"); + + await promiseDirectoryRemoved(book.URI); +}); + +/** + * Tests the context menu compose items. + */ +add_task(async function test_context_menu_compose() { + MailServices.accounts.createLocalMailAccount(); + let account = MailServices.accounts.accounts[0]; + account.addIdentity(MailServices.accounts.createIdentity()); + + registerCleanupFunction(async () => { + MailServices.accounts.removeAccount(account, true); + }); + + let book = createAddressBook("Book"); + let contactA = book.addCard(createContact("Contact", "A")); + let contactB = createContact("Contact", "B"); + contactB.setProperty("SecondEmail", "b.contact@invalid"); + contactB = book.addCard(contactB); + let contactC = createContact("Contact", "C"); + contactC.primaryEmail = null; + contactC.setProperty("SecondEmail", "c.contact@invalid"); + contactC = book.addCard(contactC); + let contactD = createContact("Contact", "D"); + contactD.primaryEmail = null; + contactD = book.addCard(contactD); + let list = book.addMailList(createMailingList("List")); + list.addCard(contactA); + list.addCard(contactB); + + let abWindow = await openAddressBookWindow(); + let abDocument = abWindow.document; + let cardsList = abWindow.cardsPane.cardsList; + + let menu = abDocument.getElementById("cardContext"); + let writeMenuItem = abDocument.getElementById("cardContextWrite"); + let writeMenu = abDocument.getElementById("cardContextWriteMenu"); + let writeMenuSeparator = abDocument.getElementById( + "cardContextWriteSeparator" + ); + + openDirectory(book); + + // Contact A, first and only email address. + + let composeWindowPromise = BrowserTestUtils.domWindowOpened(); + + await rightClickOnIndex(0); + Assert.ok(!writeMenuItem.hidden, "write menu item shown"); + Assert.ok(writeMenu.hidden, "write menu hidden"); + Assert.ok(!writeMenuSeparator.hidden, "write menu separator shown"); + menu.activateItem(writeMenuItem); + + await checkComposeWindow( + await composeWindowPromise, + "Contact A <contact.a@invalid>" + ); + + // Contact B, first email address. + + composeWindowPromise = BrowserTestUtils.domWindowOpened(); + + await rightClickOnIndex(1); + Assert.ok(writeMenuItem.hidden, "write menu item hidden"); + Assert.ok(!writeMenu.hidden, "write menu shown"); + Assert.ok(!writeMenuSeparator.hidden, "write menu separator shown"); + let shownPromise = BrowserTestUtils.waitForEvent(writeMenu, "popupshown"); + writeMenu.openMenu(true); + await shownPromise; + let subMenuItems = writeMenu.querySelectorAll("menuitem"); + Assert.equal(subMenuItems.length, 2); + Assert.equal(subMenuItems[0].label, "Contact B <contact.b@invalid>"); + Assert.equal(subMenuItems[1].label, "Contact B <b.contact@invalid>"); + + writeMenu.menupopup.activateItem(subMenuItems[0]); + + await checkComposeWindow( + await composeWindowPromise, + "Contact B <contact.b@invalid>" + ); + + // Contact B, second email address. + + composeWindowPromise = BrowserTestUtils.domWindowOpened(); + + await rightClickOnIndex(1); + Assert.ok(writeMenuItem.hidden, "write menu item hidden"); + Assert.ok(!writeMenu.hidden, "write menu shown"); + Assert.ok(!writeMenuSeparator.hidden, "write menu separator shown"); + shownPromise = BrowserTestUtils.waitForEvent(writeMenu, "popupshown"); + writeMenu.openMenu(true); + await shownPromise; + subMenuItems = writeMenu.querySelectorAll("menuitem"); + Assert.equal(subMenuItems.length, 2); + Assert.equal(subMenuItems[0].label, "Contact B <contact.b@invalid>"); + Assert.equal(subMenuItems[1].label, "Contact B <b.contact@invalid>"); + + writeMenu.menupopup.activateItem(subMenuItems[1]); + + await checkComposeWindow( + await composeWindowPromise, + "Contact B <b.contact@invalid>" + ); + + // Contact C, second and only email address. + + composeWindowPromise = BrowserTestUtils.domWindowOpened(); + + await rightClickOnIndex(2); + Assert.ok(!writeMenuItem.hidden, "write menu item shown"); + Assert.ok(writeMenu.hidden, "write menu hidden"); + Assert.ok(!writeMenuSeparator.hidden, "write menu separator shown"); + menu.activateItem(writeMenuItem); + + await checkComposeWindow( + await composeWindowPromise, + "Contact C <c.contact@invalid>" + ); + + // Contact D, no email address. + + await rightClickOnIndex(3); + Assert.ok(writeMenuItem.hidden, "write menu item hidden"); + Assert.ok(writeMenu.hidden, "write menu hidden"); + Assert.ok(writeMenuSeparator.hidden, "write menu separator hidden"); + menu.hidePopup(); + + // List. + + composeWindowPromise = BrowserTestUtils.domWindowOpened(); + + await rightClickOnIndex(4); + Assert.ok(!writeMenuItem.hidden, "write menu item shown"); + Assert.ok(writeMenu.hidden, "write menu hidden"); + Assert.ok(!writeMenuSeparator.hidden, "write menu separator shown"); + menu.activateItem(writeMenuItem); + + await checkComposeWindow(await composeWindowPromise, "List <List>"); + + // Contact A and Contact D. + + composeWindowPromise = BrowserTestUtils.domWindowOpened(); + + cardsList.selectedIndices = [0, 3]; + await rightClickOnIndex(3); + Assert.ok(!writeMenuItem.hidden, "write menu item shown"); + Assert.ok(writeMenu.hidden, "write menu hidden"); + Assert.ok(!writeMenuSeparator.hidden, "write menu separator shown"); + menu.activateItem(writeMenuItem); + + await checkComposeWindow( + await composeWindowPromise, + "Contact A <contact.a@invalid>" + ); + + // Contact B and Contact C. + + composeWindowPromise = BrowserTestUtils.domWindowOpened(); + + cardsList.selectedIndices = [1, 2]; + await rightClickOnIndex(2); + Assert.ok(!writeMenuItem.hidden, "write menu item shown"); + Assert.ok(writeMenu.hidden, "write menu hidden"); + Assert.ok(!writeMenuSeparator.hidden, "write menu separator shown"); + menu.activateItem(writeMenuItem); + + await checkComposeWindow( + await composeWindowPromise, + "Contact B <contact.b@invalid>", + "Contact C <c.contact@invalid>" + ); + + // Contact B and List. + + composeWindowPromise = BrowserTestUtils.domWindowOpened(); + + cardsList.selectedIndices = [1, 4]; + await rightClickOnIndex(4); + Assert.ok(!writeMenuItem.hidden, "write menu item shown"); + Assert.ok(writeMenu.hidden, "write menu hidden"); + Assert.ok(!writeMenuSeparator.hidden, "write menu separator shown"); + menu.activateItem(writeMenuItem); + + await checkComposeWindow( + await composeWindowPromise, + "Contact B <contact.b@invalid>", + "List <List>" + ); + + await closeAddressBookWindow(); + + await promiseDirectoryRemoved(book.URI); +}); + +/** + * Tests the context menu edit items. + */ +add_task(async function test_context_menu_edit() { + let normalBook = createAddressBook("Normal Book"); + let normalList = normalBook.addMailList(createMailingList("Normal List")); + let normalContact = normalBook.addCard(createContact("Normal", "Contact")); + normalList.addCard(normalContact); + + let readOnlyBook = createAddressBook("Read-Only Book"); + let readOnlyList = readOnlyBook.addMailList( + createMailingList("Read-Only List") + ); + let readOnlyContact = readOnlyBook.addCard( + createContact("Read-Only", "Contact") + ); + readOnlyList.addCard(readOnlyContact); + readOnlyBook.setBoolValue("readOnly", true); + + let abWindow = await openAddressBookWindow(); + let abDocument = abWindow.document; + let cardsList = abWindow.cardsPane.cardsList; + + let menu = abDocument.getElementById("cardContext"); + let editMenuItem = abDocument.getElementById("cardContextEdit"); + let exportMenuItem = abDocument.getElementById("cardContextExport"); + + async function checkEditItems(index, hidden, isMailList = false) { + await rightClickOnIndex(index); + + Assert.equal( + editMenuItem.hidden, + hidden, + `editMenuItem should be hidden=${hidden} on index ${index}` + ); + Assert.equal( + exportMenuItem.hidden, + !isMailList, + `exportMenuItem should be hidden=${!isMailList} on index ${index}` + ); + + Assert.deepEqual(document.l10n.getAttributes(editMenuItem), { + id: isMailList + ? "about-addressbook-books-context-edit-list" + : "about-addressbook-books-context-edit", + args: null, + }); + + let hiddenPromise = BrowserTestUtils.waitForEvent(menu, "popuphidden"); + menu.hidePopup(); + await hiddenPromise; + } + + info("Testing Normal Book"); + openDirectory(normalBook); + await checkEditItems(0, false); // normal contact + await checkEditItems(1, false, true); // normal list + + cardsList.selectedIndices = [0, 1]; + await checkEditItems(0, true); // normal contact + normal list + await checkEditItems(1, true); // normal contact + normal list + + info("Testing Normal List"); + openDirectory(normalList); + await checkEditItems(0, false); // normal contact + + info("Testing Read-Only Book"); + openDirectory(readOnlyBook); + await checkEditItems(0, true); // read-only contact + await checkEditItems(1, true, true); // read-only list + + info("Testing Read-Only List"); + openDirectory(readOnlyList); + await checkEditItems(0, true); // read-only contact + + info("Testing All Address Books"); + openAllAddressBooks(); + await checkEditItems(0, false); // normal contact + await checkEditItems(1, false, true); // normal list + await checkEditItems(2, true); // read-only contact + await checkEditItems(3, true, true); // read-only list + + cardsList.selectedIndices = [0, 1]; + await checkEditItems(1, true); // normal contact + normal list + + cardsList.selectedIndices = [0, 2]; + await checkEditItems(2, true); // normal contact + read-only contact + + cardsList.selectedIndices = [1, 3]; + await checkEditItems(3, true); // normal list + read-only list + + cardsList.selectedIndices = [0, 1, 2, 3]; + await checkEditItems(3, true); // everything + + await closeAddressBookWindow(); + + await promiseDirectoryRemoved(normalBook.URI); + await promiseDirectoryRemoved(readOnlyBook.URI); +}); + +/** + * Tests the context menu delete items. + */ +add_task(async function test_context_menu_delete() { + let normalBook = createAddressBook("Normal Book"); + let normalList = normalBook.addMailList(createMailingList("Normal List")); + let normalContact = normalBook.addCard(createContact("Normal", "Contact")); + normalList.addCard(normalContact); + + let readOnlyBook = createAddressBook("Read-Only Book"); + let readOnlyList = readOnlyBook.addMailList( + createMailingList("Read-Only List") + ); + let readOnlyContact = readOnlyBook.addCard( + createContact("Read-Only", "Contact") + ); + readOnlyList.addCard(readOnlyContact); + readOnlyBook.setBoolValue("readOnly", true); + + let abWindow = await openAddressBookWindow(); + let abDocument = abWindow.document; + let cardsList = abWindow.cardsPane.cardsList; + + let menu = abDocument.getElementById("cardContext"); + let deleteMenuItem = abDocument.getElementById("cardContextDelete"); + let removeMenuItem = abDocument.getElementById("cardContextRemove"); + + async function checkDeleteItems(index, deleteHidden, removeHidden, disabled) { + await rightClickOnIndex(index); + + Assert.equal( + deleteMenuItem.hidden, + deleteHidden, + `deleteMenuItem.hidden on index ${index}` + ); + Assert.equal( + deleteMenuItem.disabled, + disabled, + `deleteMenuItem.disabled on index ${index}` + ); + Assert.equal( + removeMenuItem.hidden, + removeHidden, + `removeMenuItem.hidden on index ${index}` + ); + Assert.equal( + removeMenuItem.disabled, + disabled, + `removeMenuItem.disabled on index ${index}` + ); + + let hiddenPromise = BrowserTestUtils.waitForEvent(menu, "popuphidden"); + menu.hidePopup(); + await hiddenPromise; + } + + info("Testing Normal Book"); + openDirectory(normalBook); + await checkDeleteItems(0, false, true, false); // normal contact + await checkDeleteItems(1, false, true, false); // normal list + + cardsList.selectedIndices = [0, 1]; + await checkDeleteItems(0, false, true, false); // normal contact + normal list + await checkDeleteItems(1, false, true, false); // normal contact + normal list + + info("Testing Normal List"); + openDirectory(normalList); + await checkDeleteItems(0, true, false, false); // normal contact + + info("Testing Read-Only Book"); + openDirectory(readOnlyBook); + await checkDeleteItems(0, false, true, true); // read-only contact + await checkDeleteItems(1, false, true, true); // read-only list + + info("Testing Read-Only List"); + openDirectory(readOnlyList); + await checkDeleteItems(0, true, false, true); // read-only contact + + info("Testing All Address Books"); + openAllAddressBooks(); + await checkDeleteItems(0, false, true, false); // normal contact + await checkDeleteItems(1, false, true, false); // normal list + await checkDeleteItems(2, false, true, true); // read-only contact + await checkDeleteItems(3, false, true, true); // read-only list + + cardsList.selectedIndices = [0, 1]; + await checkDeleteItems(1, false, true, false); // normal contact + normal list + + cardsList.selectedIndices = [0, 2]; + await checkDeleteItems(2, false, true, true); // normal contact + read-only contact + + cardsList.selectedIndices = [1, 3]; + await checkDeleteItems(3, false, true, true); // normal list + read-only list + + cardsList.selectedIndices = [0, 1, 2, 3]; + await checkDeleteItems(3, false, true, true); // everything + + await closeAddressBookWindow(); + + await promiseDirectoryRemoved(normalBook.URI); + await promiseDirectoryRemoved(readOnlyBook.URI); +}); + +add_task(async function test_layout() { + function checkColumns(visibleColumns, sortColumn, sortDirection) { + let visibleHeaders = cardsHeader.querySelectorAll( + `th[is="tree-view-table-header-cell"]:not([hidden])` + ); + Assert.deepEqual( + Array.from(visibleHeaders, h => h.id), + visibleColumns, + "visible columns are correct" + ); + + for (let header of visibleHeaders) { + let button = header.querySelector("button"); + Assert.equal( + button.classList.contains("ascending"), + header.id == sortColumn && sortDirection == "ascending", + `${header.id} header is ascending` + ); + Assert.equal( + button.classList.contains("descending"), + header.id == sortColumn && sortDirection == "descending", + `${header.id} header is descending` + ); + } + } + + function checkRowHeight(height) { + Assert.equal(cardsList.getRowAtIndex(0).clientHeight, height); + } + + Services.prefs.setIntPref("mail.uidensity", 0); + personalBook.addCard( + createContact("contact", "one", undefined, "first@invalid") + ); + personalBook.addCard( + createContact("contact", "two", undefined, "second@invalid") + ); + personalBook.addCard( + createContact("contact", "three", undefined, "third@invalid") + ); + personalBook.addCard( + createContact("contact", "four", undefined, "fourth@invalid") + ); + + let abWindow = await openAddressBookWindow(); + let abDocument = abWindow.document; + let cardsList = abWindow.cardsPane.cardsList; + let cardsHeader = abWindow.cardsPane.table.header; + let sharedSplitter = abDocument.getElementById("sharedSplitter"); + + // Sanity check. + + Assert.ok( + !abDocument.body.classList.contains("layout-table"), + "not table layout on opening" + ); + Assert.equal( + sharedSplitter.resizeDirection, + "horizontal", + "splitter direction is horizontal" + ); + Assert.equal( + sharedSplitter.resizeElement.id, + "cardsPane", + "splitter is affecting the cards pane" + ); + Assert.equal( + cardsList.getAttribute("rows"), + "ab-card-row", + "list row implementation used" + ); + + // Switch layout to table. + + await toggleLayout(); + + Assert.ok( + abDocument.body.classList.contains("layout-table"), + "layout changed" + ); + Assert.equal( + sharedSplitter.resizeDirection, + "vertical", + "splitter direction is vertical" + ); + Assert.equal( + sharedSplitter.resizeElement.id, + "detailsPane", + "splitter is affecting the details pane" + ); + Assert.equal( + cardsList.getAttribute("rows"), + "ab-table-card-row", + "table row implementation used" + ); + + checkColumns( + ["GeneratedName", "EmailAddresses", "PhoneNumbers", "Addresses"], + "GeneratedName", + "ascending" + ); + checkNamesListed( + "contact four", + "contact one", + "contact three", + "contact two" + ); + checkRowHeight(18); + + // Click the email addresses header to sort. + + EventUtils.synthesizeMouseAtCenter( + cardsHeader.querySelector(`[id="EmailAddressesButton"]`), + {}, + abWindow + ); + checkColumns( + ["GeneratedName", "EmailAddresses", "PhoneNumbers", "Addresses"], + "EmailAddresses", + "ascending" + ); + checkNamesListed( + "contact one", + "contact four", + "contact two", + "contact three" + ); + + // Click the email addresses header again to flip the sort. + + EventUtils.synthesizeMouseAtCenter( + cardsHeader.querySelector(`[id="EmailAddressesButton"]`), + {}, + abWindow + ); + checkColumns( + ["GeneratedName", "EmailAddresses", "PhoneNumbers", "Addresses"], + "EmailAddresses", + "descending" + ); + checkNamesListed( + "contact three", + "contact two", + "contact four", + "contact one" + ); + + // Add a column. + + await showPickerMenu("toggle", "Title"); + await TestUtils.waitForCondition( + () => !cardsHeader.querySelector(`[id="Title"]`).hidden + ); + checkColumns( + ["GeneratedName", "EmailAddresses", "PhoneNumbers", "Addresses", "Title"], + "EmailAddresses", + "descending" + ); + + // Remove a column. + + await showPickerMenu("toggle", "Addresses"); + await TestUtils.waitForCondition( + () => cardsHeader.querySelector(`[id="Addresses"]`).hidden + ); + checkColumns( + ["GeneratedName", "EmailAddresses", "PhoneNumbers", "Title"], + "EmailAddresses", + "descending" + ); + + // Change the density. + + Services.prefs.setIntPref("mail.uidensity", 1); + checkRowHeight(22); + + Services.prefs.setIntPref("mail.uidensity", 2); + checkRowHeight(32); + + // Close and reopen the Address Book and check that settings were remembered. + + await closeAddressBookWindow(); + + abWindow = await openAddressBookWindow(); + abDocument = abWindow.document; + cardsList = abWindow.cardsPane.cardsList; + cardsHeader = abWindow.cardsPane.table.header; + sharedSplitter = abDocument.getElementById("sharedSplitter"); + + Assert.ok( + abDocument.body.classList.contains("layout-table"), + "table layout preserved on reopening" + ); + Assert.equal( + sharedSplitter.resizeDirection, + "vertical", + "splitter direction preserved as vertical" + ); + Assert.equal( + sharedSplitter.resizeElement.id, + "detailsPane", + "splitter preserved affecting the details pane" + ); + Assert.equal( + cardsList.getAttribute("rows"), + "ab-table-card-row", + "table row implementation used" + ); + + checkColumns( + ["GeneratedName", "EmailAddresses", "PhoneNumbers", "Title"], + "EmailAddresses", + "descending" + ); + checkNamesListed( + "contact three", + "contact two", + "contact four", + "contact one" + ); + checkRowHeight(32); + + // Reset layout to list. + + await toggleLayout(); + + Assert.ok( + !abDocument.body.classList.contains("layout-table"), + "layout changed" + ); + Assert.equal( + sharedSplitter.resizeDirection, + "horizontal", + "splitter direction is horizontal" + ); + Assert.equal( + sharedSplitter.resizeElement.id, + "cardsPane", + "splitter is affecting the cards pane" + ); + Assert.equal( + cardsList.getAttribute("rows"), + "ab-card-row", + "list row implementation used" + ); + + await closeAddressBookWindow(); + + Services.xulStore.removeDocument("about:addressbook"); + Services.prefs.clearUserPref("mail.uidensity"); + personalBook.deleteCards(personalBook.childCards); +}); + +add_task(async function test_placeholders() { + let writableBook = createAddressBook("Writable Book"); + let readOnlyBook = createAddressBook("Read-Only Book"); + readOnlyBook.setBoolValue("readOnly", true); + + let abWindow = await openAddressBookWindow(); + let placeholderCreateContact = abWindow.document.getElementById( + "placeholderCreateContact" + ); + + info("checking all address books"); + await openAllAddressBooks(); + checkPlaceholders(["placeholderEmptyBook", "placeholderCreateContact"]); + + info("checking writable book"); + await openDirectory(writableBook); + checkPlaceholders(["placeholderEmptyBook", "placeholderCreateContact"]); + + let writableList = writableBook.addMailList( + createMailingList("Writable List") + ); + checkPlaceholders(); + + info("checking writable list"); + await openDirectory(writableList); + checkPlaceholders(["placeholderEmptyBook"]); + + info("checking writable book"); + await openDirectory(writableBook); + writableBook.deleteDirectory(writableList); + checkPlaceholders(["placeholderEmptyBook", "placeholderCreateContact"]); + + info("checking read-only book"); + await openDirectory(readOnlyBook); + checkPlaceholders(["placeholderEmptyBook"]); + + // This wouldn't happen but we need to check the state in a read-only list. + readOnlyBook.setBoolValue("readOnly", false); + let readOnlyList = readOnlyBook.addMailList( + createMailingList("Read-Only List") + ); + readOnlyBook.setBoolValue("readOnly", true); + checkPlaceholders(); + + info("checking read-only list"); + await openDirectory(readOnlyList); + checkPlaceholders(["placeholderEmptyBook"]); + + info("checking read-only book"); + await openDirectory(readOnlyBook); + readOnlyBook.setBoolValue("readOnly", false); + readOnlyBook.deleteDirectory(readOnlyList); + readOnlyBook.setBoolValue("readOnly", true); + checkPlaceholders(["placeholderEmptyBook"]); + + info("checking button opens a new contact to edit"); + await openAllAddressBooks(); + checkPlaceholders(["placeholderEmptyBook", "placeholderCreateContact"]); + EventUtils.synthesizeMouseAtCenter(placeholderCreateContact, {}, abWindow); + + await TestUtils.waitForCondition( + () => abWindow.detailsPane.isEditing, + "entering editing mode" + ); + + await closeAddressBookWindow(); + await promiseDirectoryRemoved(writableBook.URI); + await promiseDirectoryRemoved(readOnlyBook.URI); +}); + +/** + * Checks that mailling lists address books are shown in the table layout. + */ +add_task(async function test_list_table_layout() { + let book = createAddressBook("Book"); + book.addCard(createContact("contact", "one")); + let list = createMailingList("list one"); + book.addMailList(list); + + let abWindow = await openAddressBookWindow(); + let cardsList = abWindow.cardsPane.cardsList; + let cardsHeader = abWindow.cardsPane.table.header; + + // Switch layout to table. + + await toggleLayout(); + + await showPickerMenu("toggle", "addrbook"); + await TestUtils.waitForCondition( + () => !cardsHeader.querySelector(`[id="addrbook"]`).hidden + ); + + // Check for the contact that the column is shown. + Assert.ok( + !cardsList.getRowAtIndex(0).querySelector(".addrbook-column").hidden, + "Address book column is shown." + ); + + Assert.ok( + cardsList + .getRowAtIndex(0) + .querySelector(".addrbook-column") + .textContent.includes("Book"), + "Address book column has the correct name for a contact." + ); + + Assert.ok( + cardsList + .getRowAtIndex(0) + .querySelector(".addrbook-column") + .textContent.includes("Book"), + "Address book column has the correct name for a list." + ); + + Services.xulStore.removeDocument("about:addressbook"); + await closeAddressBookWindow(); + await promiseDirectoryRemoved(book.URI); +}); + +/** + * Tests the option of showing the address book for All Address Book for the + * list view (vertical layout). + */ +add_task(async function test_list_all_address_book() { + let firstBook = createAddressBook("First Book"); + let secondBook = createAddressBook("Second Book"); + firstBook.addCard(createContact("contact", "one")); + secondBook.addCard(createContact("contact", "two")); + let list = createMailingList("list two"); + secondBook.addMailList(list); + + let abWindow = await openAddressBookWindow(); + let cardsList = abWindow.cardsPane.cardsList; + let cardsHeader = abWindow.cardsPane.table.header; + + info("Check that no address book suffix is present."); + Assert.ok( + !cardsList.getRowAtIndex(0).querySelector(".address-book-name"), + "No address book suffix is present." + ); + Assert.ok( + !cardsList.getRowAtIndex(1).querySelector(".address-book-name"), + "No address book suffix is present." + ); + Assert.ok( + !cardsList.getRowAtIndex(2).querySelector(".address-book-name"), + "No address book suffix is present." + ); + + info("Toggle the option to show address books."); + await showSortMenu("toggle", "addrbook"); + await TestUtils.waitForCondition( + () => !cardsHeader.querySelector(`[id="addrbook"]`).hidden + ); + + Assert.ok( + cardsList + .getRowAtIndex(0) + .querySelector(".address-book-name") + .textContent.includes("First Book"), + "Address book suffix is present." + ); + Assert.ok( + cardsList + .getRowAtIndex(1) + .querySelector(".address-book-name") + .textContent.includes("Second Book"), + "Address book suffix is present." + ); + Assert.ok( + cardsList + .getRowAtIndex(2) + .querySelector(".address-book-name") + .textContent.includes("Second Book"), + "Address book suffix is present for a list." + ); + + info(`Select another address book and check that no address book suffix is + present for another book besides All Address Book`); + await openDirectory(secondBook); + Assert.ok( + !cardsList.getRowAtIndex(0).querySelector(".address-book-name"), + "Address book suffix is only present in All Address Book." + ); + + Services.xulStore.removeDocument("about:addressbook"); + await closeAddressBookWindow(); + await promiseDirectoryRemoved(firstBook.URI); + await promiseDirectoryRemoved(secondBook.URI); +}); |