/* 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 resulting send format of a message dependent on using HTML features
* in the composition.
*/
"use strict";
requestLongerTimeout(4);
var {
open_compose_from_draft,
open_compose_new_mail,
open_compose_with_reply,
FormatHelper,
} = ChromeUtils.import("resource://testing-common/mozmill/ComposeHelpers.jsm");
var {
be_in_folder,
empty_folder,
get_special_folder,
get_about_message,
open_message_from_file,
select_click_row,
} = ChromeUtils.import(
"resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
);
var sendFormatPreference;
var htmlAsPreference;
var draftsFolder;
var outboxFolder;
add_setup(async () => {
sendFormatPreference = Services.prefs.getIntPref("mail.default_send_format");
htmlAsPreference = Services.prefs.getIntPref("mailnews.display.html_as");
// Show all parts to a message in the message display.
// This allows us to see if a message contains both a plain text and a HTML
// part.
Services.prefs.setIntPref("mailnews.display.html_as", 4);
draftsFolder = await get_special_folder(Ci.nsMsgFolderFlags.Drafts, true);
outboxFolder = await get_special_folder(Ci.nsMsgFolderFlags.Queue, true);
});
registerCleanupFunction(async function () {
Services.prefs.setIntPref("mail.default_send_format", sendFormatPreference);
Services.prefs.setIntPref("mailnews.display.html_as", htmlAsPreference);
await empty_folder(draftsFolder);
await empty_folder(outboxFolder);
});
async function checkMsgFile(aFilePath, aConvertibility) {
let file = new FileUtils.File(getTestFilePath(`data/${aFilePath}`));
let messageController = await open_message_from_file(file);
// Creating a reply should not affect convertibility.
let composeWindow = open_compose_with_reply(messageController).window;
Assert.equal(composeWindow.gMsgCompose.bodyConvertible(), aConvertibility);
await BrowserTestUtils.closeWindow(composeWindow);
await BrowserTestUtils.closeWindow(messageController.window);
}
/**
* Tests nodeTreeConvertible() can be called from JavaScript.
*/
add_task(async function test_msg_nodeTreeConvertible() {
let msgCompose = Cc["@mozilla.org/messengercompose/compose;1"].createInstance(
Ci.nsIMsgCompose
);
let textDoc = new DOMParser().parseFromString(
"
Simple Text
",
"text/html"
);
Assert.equal(
msgCompose.nodeTreeConvertible(textDoc.documentElement),
Ci.nsIMsgCompConvertible.Plain
);
let htmlDoc = new DOMParser().parseFromString(
'Complex Text
',
"text/html"
);
Assert.equal(
msgCompose.nodeTreeConvertible(htmlDoc.documentElement),
Ci.nsIMsgCompConvertible.No
);
});
/**
* Tests that we only open one compose window for one instance of a draft.
*/
add_task(async function test_msg_convertibility() {
await checkMsgFile("./format1-plain.eml", Ci.nsIMsgCompConvertible.Plain);
// Bug 1385636
await checkMsgFile(
"./format1-altering.eml",
Ci.nsIMsgCompConvertible.Altering
);
// Bug 584313
await checkMsgFile("./format2-style-attr.eml", Ci.nsIMsgCompConvertible.No);
await checkMsgFile("./format3-style-tag.eml", Ci.nsIMsgCompConvertible.No);
});
/**
* Map from a nsIMsgCompSendFormat to the id of the corresponding menuitem in
* the Options, Send Format menu.
*
* @type {Map}
*/
var sendFormatToMenuitem = new Map([
[Ci.nsIMsgCompSendFormat.PlainText, "format_plain"],
[Ci.nsIMsgCompSendFormat.HTML, "format_html"],
[Ci.nsIMsgCompSendFormat.Both, "format_both"],
[Ci.nsIMsgCompSendFormat.Auto, "format_auto"],
]);
/**
* Verify that the correct send format menu item is checked.
*
* @param {Window} composeWindow - The compose window.
* @param {nsIMsgCompSendFormat} expectFormat - The expected checked format
* option. Either Auto, PlainText, HTML, or Both.
* @param {string} msg - A message to use in assertions.
*/
function assertSendFormatInMenu(composeWindow, expectFormat, msg) {
for (let [format, menuitemId] of sendFormatToMenuitem.entries()) {
let menuitem = composeWindow.document.getElementById(menuitemId);
let checked = expectFormat == format;
Assert.equal(
menuitem.getAttribute("checked") == "true",
checked,
`${menuitemId} should ${checked ? "not " : ""}be checked: ${msg}`
);
}
}
const PLAIN_MESSAGE_BODY = "Plain message body";
const BOLD_MESSAGE_BODY = "Bold message body";
const BOLD_MESSAGE_BODY_AS_PLAIN = `*${BOLD_MESSAGE_BODY}*`;
/**
* Set the default send format and create a new message in the compose window.
*
* @param {nsIMsgCompSendFormat} preference - The default send format to set via
* a preference before opening the window.
* @param {boolean} useBold - Whether to use bold text in the message's body.
*
* @returns {Window} - The opened compose window, pre-filled with a message.
*/
async function newMessage(preference, useBold) {
Services.prefs.setIntPref("mail.default_send_format", preference);
let composeWindow = open_compose_new_mail().window;
assertSendFormatInMenu(
composeWindow,
preference,
"Send format should initially match preference"
);
// Focus should be on "To" field.
EventUtils.sendString("recipient@server.net", composeWindow);
await TestUtils.waitForTick();
EventUtils.synthesizeKey("KEY_Enter", {}, composeWindow);
await TestUtils.waitForTick();
EventUtils.synthesizeKey("KEY_Enter", {}, composeWindow);
await TestUtils.waitForTick();
// Focus should be in the "Subject" field.
EventUtils.sendString(
`${useBold ? "rich" : "plain"} message with preference ${preference}`,
composeWindow
);
await TestUtils.waitForTick();
EventUtils.synthesizeKey("KEY_Enter", {}, composeWindow);
await TestUtils.waitForTick();
// Focus should be in the body.
let formatHelper = new FormatHelper(composeWindow);
if (useBold) {
EventUtils.synthesizeMouseAtCenter(
formatHelper.boldButton,
{},
composeWindow
);
await TestUtils.waitForTick();
await formatHelper.typeInMessage(BOLD_MESSAGE_BODY);
} else {
await formatHelper.typeInMessage(PLAIN_MESSAGE_BODY);
}
return composeWindow;
}
/**
* Set the send format to something else via the application menu.
*
* @param {Window} composeWindow - The compose window to set the format in.
* @param {nsIMsgCompSendFormat} sendFormat - The send format to set. Either
* Auto, PlainText, HTML, or Both.
*/
async function setSendFormat(composeWindow, sendFormat) {
async function openMenu(menu) {
let openPromise = BrowserTestUtils.waitForEvent(menu, "popupshown");
menu.openMenu(true);
await openPromise;
}
let optionsMenu = composeWindow.document.getElementById("optionsMenu");
let sendFormatMenu =
composeWindow.document.getElementById("outputFormatMenu");
let menuitem = composeWindow.document.getElementById(
sendFormatToMenuitem.get(sendFormat)
);
await openMenu(optionsMenu);
await openMenu(sendFormatMenu);
let closePromise = BrowserTestUtils.waitForEvent(optionsMenu, "popuphidden");
sendFormatMenu.menupopup.activateItem(menuitem);
await closePromise;
assertSendFormatInMenu(
composeWindow,
sendFormat,
"Send format should change to the selected format"
);
}
/**
* Verify the actual sent message of a composed message.
*
* @param {Window} composeWindow - The compose window that contains the message
* we want to send.
* @param {object} expectMessage - The expected sent message.
* @param {boolean} expectMessage.isBold - Whether the message uses a bold
* message, rather than the plain message.
* @param {boolean} expectMessage.plain - Whether the message has a plain part.
* @param {boolean} expectMessage.html - Whether the message has a html part.
* @param {string} msg - A message to use in assertions.
*/
async function assertSentMessage(composeWindow, expectMessage, msg) {
let { isBold, plain, html } = expectMessage;
// Send later.
let closePromise = BrowserTestUtils.windowClosed(composeWindow);
EventUtils.synthesizeKey(
"KEY_Enter",
{ accelKey: true, shiftKey: true },
composeWindow
);
await closePromise;
// Open the "sent" message.
await be_in_folder(outboxFolder);
// Should be the last message in the tree.
select_click_row(-1);
// Test that the sent content type is either text/plain, text/html or
// multipart/alternative.
// TODO: Is there a better way to expose the content-type of the displayed
// message?
let contentType =
get_about_message().currentHeaderData["content-type"].headerValue;
if (plain && html) {
Assert.ok(
contentType.startsWith("multipart/alternative"),
`Sent contentType "${contentType}" should be multipart: ${msg}`
);
} else if (plain) {
Assert.ok(
contentType.startsWith("text/plain"),
`Sent contentType "${contentType}" should be plain text only: ${msg}`
);
} else if (html) {
Assert.ok(
contentType.startsWith("text/html"),
`Sent contentType "${contentType}" should be html only: ${msg}`
);
} else {
throw new Error("Expected message is missing either plain or html parts");
}
// Assert the html and plain text parts are either hidden or shown.
// NOTE: We have set the mailnews.display.html_as preference to show all parts
// of the message, which means it will show both the plain text and html parts
// if both were sent.
let messageBody =
get_about_message().document.getElementById("messagepane").contentDocument
.body;
let plainBody = messageBody.querySelector(".moz-text-flowed");
let htmlBody = messageBody.querySelector(".moz-text-html");
Assert.equal(
!!plain,
!!plainBody,
`Message should ${plain ? "" : "not "}have a Plain part: ${msg}`
);
Assert.equal(
!!html,
!!htmlBody,
`Message should ${html ? "" : "not "}have a HTML part: ${msg}`
);
if (plain) {
Assert.ok(
BrowserTestUtils.is_visible(plainBody),
`Plain part should be visible: ${msg}`
);
Assert.equal(
plainBody.textContent.trim(),
isBold ? BOLD_MESSAGE_BODY_AS_PLAIN : PLAIN_MESSAGE_BODY,
`Plain text content should match: ${msg}`
);
}
if (html) {
Assert.ok(
BrowserTestUtils.is_visible(htmlBody),
`HTML part should be visible: ${msg}`
);
Assert.equal(
htmlBody.textContent.trim(),
isBold ? BOLD_MESSAGE_BODY : PLAIN_MESSAGE_BODY,
`HTML text content should match: ${msg}`
);
}
}
async function saveDraft(composeWindow) {
let oldDraftsCounts = draftsFolder.getTotalMessages(false);
// Save as draft.
EventUtils.synthesizeKey("s", { accelKey: true }, composeWindow);
await TestUtils.waitForCondition(
() => composeWindow.gSaveOperationInProgress,
"Should start save operation"
);
await TestUtils.waitForCondition(
() => !composeWindow.gSaveOperationInProgress && !composeWindow.gWindowLock,
"Waiting for the save operation to complete"
);
await TestUtils.waitForCondition(
() => draftsFolder.getTotalMessages(false) > oldDraftsCounts,
"message saved to drafts folder"
);
await BrowserTestUtils.closeWindow(composeWindow);
}
async function assertDraftFormat(expectSavedFormat) {
await be_in_folder(draftsFolder);
select_click_row(0);
let newComposeWindow = open_compose_from_draft().window;
assertSendFormatInMenu(
newComposeWindow,
expectSavedFormat,
"Send format of the opened draft should match the saved format"
);
return newComposeWindow;
}
add_task(async function test_preference_send_format() {
// Sending a plain message.
for (let { preference, sendsPlain, sendsHtml } of [
{
preference: Ci.nsIMsgCompSendFormat.Auto,
sendsPlain: true,
sendsHtml: false,
},
{
preference: Ci.nsIMsgCompSendFormat.PlainText,
sendsPlain: true,
sendsHtml: false,
},
{
preference: Ci.nsIMsgCompSendFormat.HTML,
sendsPlain: false,
sendsHtml: true,
},
{
preference: Ci.nsIMsgCompSendFormat.Both,
sendsPlain: true,
sendsHtml: true,
},
]) {
info(`Testing preference ${preference} with a plain message`);
let composeWindow = await newMessage(preference, false);
await assertSentMessage(
composeWindow,
{ plain: sendsPlain, html: sendsHtml, isBold: false },
`Plain message with preference ${preference}`
);
}
// Sending a bold message.
for (let { preference, sendsPlain, sendsHtml } of [
{
preference: Ci.nsIMsgCompSendFormat.Auto,
sendsPlain: true,
sendsHtml: true,
},
{
preference: Ci.nsIMsgCompSendFormat.PlainText,
sendsPlain: true,
sendsHtml: false,
},
{
preference: Ci.nsIMsgCompSendFormat.HTML,
sendsPlain: false,
sendsHtml: true,
},
{
preference: Ci.nsIMsgCompSendFormat.Both,
sendsPlain: true,
sendsHtml: true,
},
]) {
info(`Testing preference ${preference} with a bold message`);
let composeWindow = await newMessage(preference, true);
await assertSentMessage(
composeWindow,
{ plain: sendsPlain, html: sendsHtml, isBold: true },
`Bold message with preference ${preference}`
);
}
});
add_task(async function test_setting_send_format() {
for (let { preference, sendFormat, boldMessage, sendsPlain, sendsHtml } of [
{
preference: Ci.nsIMsgCompSendFormat.Auto,
boldMessage: true,
sendFormat: Ci.nsIMsgCompSendFormat.HTML,
sendsPlain: false,
sendsHtml: true,
},
{
preference: Ci.nsIMsgCompSendFormat.Auto,
boldMessage: true,
sendFormat: Ci.nsIMsgCompSendFormat.PlainText,
sendsPlain: true,
sendsHtml: false,
},
{
preference: Ci.nsIMsgCompSendFormat.PlainText,
boldMessage: false,
sendFormat: Ci.nsIMsgCompSendFormat.Both,
sendsPlain: true,
sendsHtml: true,
},
{
preference: Ci.nsIMsgCompSendFormat.HTML,
boldMessage: false,
sendFormat: Ci.nsIMsgCompSendFormat.Auto,
sendsPlain: true,
sendsHtml: false,
},
{
preference: Ci.nsIMsgCompSendFormat.Both,
boldMessage: false,
sendFormat: Ci.nsIMsgCompSendFormat.HTML,
sendsPlain: false,
sendsHtml: true,
},
]) {
info(
`Testing changing format from preference ${preference} to ${sendFormat}`
);
let composeWindow = await newMessage(preference, boldMessage);
await setSendFormat(composeWindow, sendFormat);
await assertSentMessage(
composeWindow,
{ isBold: boldMessage, plain: sendsPlain, html: sendsHtml },
`${boldMessage ? "Bold" : "Plain"} message set as ${sendFormat}`
);
}
}).__skipMe = AppConstants.platform == "macosx";
// Can't click menu bar on Mac to change the send format.
add_task(async function test_saving_draft_with_set_format() {
for (let { preference, sendFormat, sendsPlain, sendsHtml } of [
{
preference: Ci.nsIMsgCompSendFormat.Auto,
sendFormat: Ci.nsIMsgCompSendFormat.PlainText,
sendsPlain: true,
sendsHtml: false,
},
{
preference: Ci.nsIMsgCompSendFormat.PlainText,
sendFormat: Ci.nsIMsgCompSendFormat.Auto,
sendsPlain: true,
sendsHtml: true,
},
{
preference: Ci.nsIMsgCompSendFormat.Both,
sendFormat: Ci.nsIMsgCompSendFormat.HTML,
sendsPlain: false,
sendsHtml: true,
},
{
preference: Ci.nsIMsgCompSendFormat.HTML,
sendFormat: Ci.nsIMsgCompSendFormat.Both,
sendsPlain: true,
sendsHtml: true,
},
]) {
info(`Testing draft saved as ${sendFormat}`);
let composeWindow = await newMessage(preference, true);
await setSendFormat(composeWindow, sendFormat);
await saveDraft(composeWindow);
// Draft keeps the set format when opened.
composeWindow = await assertDraftFormat(sendFormat);
await assertSentMessage(
composeWindow,
{ isBold: true, plain: sendsPlain, html: sendsHtml },
`Bold draft message set as ${sendFormat}`
);
}
}).__skipMe = AppConstants.platform == "macosx";
// Can't click menu bar on Mac to change the send format.
add_task(async function test_saving_draft_with_new_preference() {
for (let { preference, newPreference, sendsPlain, sendsHtml } of [
{
preference: Ci.nsIMsgCompSendFormat.Auto,
newPreference: Ci.nsIMsgCompSendFormat.HTML,
sendsPlain: true,
sendsHtml: false,
},
{
preference: Ci.nsIMsgCompSendFormat.PlainText,
newPreference: Ci.nsIMsgCompSendFormat.Both,
sendsPlain: true,
sendsHtml: false,
},
{
preference: Ci.nsIMsgCompSendFormat.Both,
newPreference: Ci.nsIMsgCompSendFormat.Auto,
sendsPlain: true,
sendsHtml: true,
},
{
preference: Ci.nsIMsgCompSendFormat.HTML,
newPreference: Ci.nsIMsgCompSendFormat.PlainText,
sendsPlain: false,
sendsHtml: true,
},
]) {
info(`Testing changing preference from ${preference} to ${newPreference}`);
let composeWindow = await newMessage(preference, false);
await saveDraft(composeWindow);
// Re-open, with a new default preference set, to make sure the draft has
// the send format set earlier saved in its headers.
Services.prefs.setIntPref("mail.default_send_format", newPreference);
// Draft keeps the old preference.
composeWindow = await assertDraftFormat(preference);
await assertSentMessage(
composeWindow,
{ isBold: false, plain: sendsPlain, html: sendsHtml },
`Plain draft message with preference ${preference}`
);
}
});