summaryrefslogtreecommitdiffstats
path: root/comm/mail/test/browser/composition/browser_text_styling.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/test/browser/composition/browser_text_styling.js')
-rw-r--r--comm/mail/test/browser/composition/browser_text_styling.js609
1 files changed, 609 insertions, 0 deletions
diff --git a/comm/mail/test/browser/composition/browser_text_styling.js b/comm/mail/test/browser/composition/browser_text_styling.js
new file mode 100644
index 0000000000..88c31c3040
--- /dev/null
+++ b/comm/mail/test/browser/composition/browser_text_styling.js
@@ -0,0 +1,609 @@
+/* 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/. */
+
+/**
+ * Test styling messages.
+ */
+
+requestLongerTimeout(3);
+
+var { close_compose_window, open_compose_new_mail, FormatHelper } =
+ ChromeUtils.import("resource://testing-common/mozmill/ComposeHelpers.jsm");
+
+add_task(async function test_style_buttons() {
+ let controller = open_compose_new_mail();
+ let formatHelper = new FormatHelper(controller.window);
+
+ let buttonSet = [
+ { name: "bold", tag: "B", node: formatHelper.boldButton },
+ { name: "italic", tag: "I", node: formatHelper.italicButton },
+ { name: "underline", tag: "U", node: formatHelper.underlineButton },
+ ];
+
+ // Without focus on message.
+ for (let button of buttonSet) {
+ Assert.ok(
+ button.node.disabled,
+ `${button.name} button should be disabled with no focus`
+ );
+ }
+
+ formatHelper.focusMessage();
+
+ // With focus on message.
+ for (let button of buttonSet) {
+ Assert.ok(
+ !button.node.disabled,
+ `${button.name} button should be enabled with focus`
+ );
+ }
+
+ async function selectTextAndToggleButton(
+ start,
+ end,
+ button,
+ enables,
+ message
+ ) {
+ await formatHelper.selectTextRange(start, end);
+ await formatHelper.assertShownStyles(
+ enables ? null : [button.name],
+ `${message}: Before toggle`
+ );
+ button.node.click();
+ await formatHelper.assertShownStyles(
+ enables ? [button.name] : null,
+ `${message}: Before toggle`
+ );
+ }
+
+ for (let button of buttonSet) {
+ let name = button.name;
+ let tags = new Set();
+ tags.add(button.tag);
+
+ await formatHelper.assertShownStyles(
+ null,
+ `No shown styles at the start (${name})`
+ );
+ button.node.click();
+ await formatHelper.assertShownStyles(
+ [name],
+ `${name} is shown after clicking`
+ );
+ let text = `test-${button.name}`;
+ await formatHelper.typeInMessage(text);
+ await formatHelper.assertShownStyles(
+ [name],
+ `${name} is shown after clicking and typing`
+ );
+ formatHelper.assertMessageParagraph(
+ [{ tags, text }],
+ `Clicking ${name} button and typing`
+ );
+
+ // Stop styling on click.
+ let addedText = "not-styled";
+ button.node.click();
+ await formatHelper.assertShownStyles(
+ null,
+ `No longer ${name} after re-clicking`
+ );
+ await formatHelper.typeInMessage(addedText);
+ await formatHelper.assertShownStyles(
+ null,
+ `No longer ${name} after re-clicking and typing`
+ );
+ formatHelper.assertMessageParagraph(
+ [{ tags, text }, addedText],
+ `Unclicking ${name} button and typing`
+ );
+
+ await formatHelper.deleteTextRange(
+ text.length,
+ text.length + addedText.length
+ );
+ formatHelper.assertMessageParagraph(
+ [{ tags, text }],
+ `Removed non-${name} region`
+ );
+
+ // Undo in region.
+ await selectTextAndToggleButton(
+ 1,
+ 3,
+ button,
+ false,
+ `Unchecking ${button.name} button for some region`
+ );
+ formatHelper.assertMessageParagraph(
+ [{ tags, text: "t" }, "es", { tags, text: `t-${button.name}` }],
+ `After unchecking ${button.name} button for some region`
+ );
+
+ // Redo over region.
+ await selectTextAndToggleButton(
+ 1,
+ 3,
+ button,
+ true,
+ `Rechecking ${button.name} button for some region`
+ );
+ formatHelper.assertMessageParagraph(
+ [{ tags, text }],
+ `After rechecking ${button.name} button for some region`
+ );
+
+ // Undo over whole text
+ await selectTextAndToggleButton(
+ 0,
+ text.length,
+ button,
+ false,
+ `Unchecking ${button.name} button for whole text`
+ );
+ formatHelper.assertMessageParagraph(
+ [text],
+ `After unchecking ${button.name} button for whole text`
+ );
+
+ await formatHelper.emptyParagraph();
+ }
+
+ close_compose_window(controller);
+});
+
+add_task(async function test_multi_style_with_buttons() {
+ let controller = open_compose_new_mail();
+ let formatHelper = new FormatHelper(controller.window);
+
+ let boldButton = formatHelper.boldButton;
+ let italicButton = formatHelper.italicButton;
+ let underlineButton = formatHelper.underlineButton;
+
+ formatHelper.focusMessage();
+
+ let parts = ["bold", " and italic", " and underline"];
+
+ boldButton.click();
+ await formatHelper.typeInMessage(parts[0]);
+ formatHelper.assertMessageParagraph(
+ [{ tags: ["B"], text: parts[0] }],
+ "Added bold"
+ );
+ await formatHelper.assertShownStyles(
+ ["bold"],
+ "After clicking bold and typing"
+ );
+
+ italicButton.click();
+ await formatHelper.typeInMessage(parts[1]);
+ formatHelper.assertMessageParagraph(
+ [
+ { tags: ["B"], text: parts[0] },
+ { tags: ["B", "I"], text: parts[1] },
+ ],
+ "Added italic"
+ );
+ await formatHelper.assertShownStyles(
+ ["bold", "italic"],
+ "After clicking italic and typing"
+ );
+
+ underlineButton.click();
+ await formatHelper.typeInMessage(parts[2]);
+ formatHelper.assertMessageParagraph(
+ [
+ { tags: ["B"], text: parts[0] },
+ { tags: ["B", "I"], text: parts[1] },
+ { tags: ["B", "I", "U"], text: parts[2] },
+ ],
+ "Added underline"
+ );
+ await formatHelper.assertShownStyles(
+ ["bold", "italic", "underline"],
+ "After clicking underline and typing"
+ );
+
+ await formatHelper.selectTextRange(2, parts[0].length + parts[1].length + 2);
+ await formatHelper.assertShownStyles(
+ ["bold"],
+ "Only bold when selecting all bold, mixed italic and mixed underline"
+ );
+
+ // Remove bold over selection.
+ boldButton.click();
+ formatHelper.assertMessageParagraph(
+ [
+ { tags: ["B"], text: parts[0].slice(0, 2) },
+ parts[0].slice(2),
+ { tags: ["I"], text: parts[1] },
+ { tags: ["I", "U"], text: parts[2].slice(0, 2) },
+ { tags: ["B", "I", "U"], text: parts[2].slice(2) },
+ ],
+ "Removed bold in middle"
+ );
+
+ close_compose_window(controller);
+});
+
+add_task(async function test_text_styling_whilst_typing() {
+ let controller = open_compose_new_mail();
+ let formatHelper = new FormatHelper(controller.window);
+
+ formatHelper.focusMessage();
+
+ await formatHelper.assertShownStyles(null, "None checked");
+
+ for (let style of formatHelper.styleDataMap.values()) {
+ let tags = new Set();
+ tags.add(style.tag);
+ let name = style.name;
+
+ // Start styling.
+ await formatHelper.selectStyle(style);
+
+ // See Bug 1716840.
+ // await formatHelper.assertShownStyles(style, `${name} selected`);
+ let text = `test-${name}`;
+ await formatHelper.typeInMessage(text);
+ await formatHelper.assertShownStyles(style, `${name} selected and typing`);
+ formatHelper.assertMessageParagraph([{ tags, text }], `Selecting ${name}`);
+
+ // Stop styling.
+ await formatHelper.selectStyle(style);
+ // See Bug 1716840.
+ // await formatHelper.assertShownStyles(null, `${name} unselected`);
+ let addedText = "not-styled";
+ await formatHelper.typeInMessage(addedText);
+ formatHelper.assertMessageParagraph(
+ [{ tags, text }, addedText],
+ `Unselecting ${name}`
+ );
+ await formatHelper.assertShownStyles(null, `${name} unselected and typing`);
+
+ // Select these again to unselect for next loop cycle. Needs to be done
+ // before empty paragraph since they happen only after "typing".
+ if (style.linked) {
+ await formatHelper.selectStyle(style.linked);
+ }
+ if (style.implies) {
+ await formatHelper.selectStyle(style.implies);
+ }
+ await formatHelper.emptyParagraph();
+
+ // Select again to unselect for next loop cycle.
+ await formatHelper.selectStyle(style);
+ }
+
+ close_compose_window(controller);
+});
+
+add_task(async function test_text_styling_update_on_selection_change() {
+ let controller = open_compose_new_mail();
+ let formatHelper = new FormatHelper(controller.window);
+
+ formatHelper.focusMessage();
+
+ for (let style of formatHelper.styleDataMap.values()) {
+ let tags = new Set();
+ tags.add(style.tag);
+ let name = style.name;
+
+ // Start styling.
+ await formatHelper.selectStyle(style);
+ let text = `test-${name}`;
+ await formatHelper.typeInMessage(text);
+ // Stop styling.
+ await formatHelper.selectStyle(style);
+ let addedText = "not-styled";
+ await formatHelper.typeInMessage("not-styled");
+ formatHelper.assertMessageParagraph(
+ [{ tags, text }, addedText],
+ `Unselecting ${name} and typing`
+ );
+
+ await formatHelper.assertShownStyles(null, `${name} unselected at end`);
+
+ // Test selections.
+ for (let [start, end, forward, expect] of [
+ // Make sure we toggle, so the test does not capture the previous state.
+ [0, null, true, true], // At start.
+ [0, text.length + 1, true, false], // Mixed is unchecked.
+ [text.length, null, true, true], // On boundary travelling forward.
+ [text.length, null, false, false], // On boundary travelling backward.
+ [2, 4, true, true], // In the styled region.
+ [text.length, text.length + 1, true, false], // In the unstyled region.
+ ]) {
+ await formatHelper.selectTextRange(start, end, forward);
+ if (expect) {
+ expect = style;
+ } else {
+ expect = null;
+ }
+ await formatHelper.assertShownStyles(
+ expect,
+ `Selecting with ${name} style, from ${start} to ${end} ` +
+ `${forward ? "forwards" : "backwards"}`
+ );
+ }
+ if (style.linked) {
+ await formatHelper.selectStyle(style.linked);
+ }
+ if (style.implies) {
+ await formatHelper.selectStyle(style.implies);
+ }
+ await formatHelper.emptyParagraph();
+ // Select again to unselect for next loop cycle.
+ await formatHelper.selectStyle(style);
+ }
+
+ close_compose_window(controller);
+});
+
+add_task(async function test_text_styling_on_selections() {
+ let controller = open_compose_new_mail();
+ let formatHelper = new FormatHelper(controller.window);
+
+ formatHelper.focusMessage();
+
+ let start;
+ let end = 0;
+ let parts = [];
+ let fullText = "";
+ for (let text of ["test for ", "styling some", " selections"]) {
+ start = end;
+ end += text.length;
+ parts.push({ text, start, end });
+ fullText += text;
+ }
+ await formatHelper.typeInMessage(fullText);
+ formatHelper.assertMessageParagraph([fullText], "No styling at start");
+
+ for (let style of formatHelper.styleDataMap.values()) {
+ let tags = new Set();
+ tags.add(style.tag);
+ let name = style.name;
+
+ formatHelper.assertMessageParagraph(
+ [fullText],
+ `No ${name} style at start`
+ );
+
+ await formatHelper.selectTextRange(parts[1].start, parts[1].end);
+ await formatHelper.selectStyle(style);
+ formatHelper.assertMessageParagraph(
+ [parts[0].text, { tags, text: parts[1].text }, parts[2].text],
+ `${name} in the middle`
+ );
+
+ await formatHelper.selectTextRange(parts[0].start, parts[2].end);
+ await formatHelper.selectStyle(style);
+ formatHelper.assertMessageParagraph(
+ [{ tags, text: fullText }],
+ `${name} on all`
+ );
+
+ // Undo in region.
+ await formatHelper.selectTextRange(parts[1].start, parts[1].end);
+ await formatHelper.selectStyle(style);
+ formatHelper.assertMessageParagraph(
+ [
+ { tags, text: parts[0].text },
+ parts[1].text,
+ { tags, text: parts[2].text },
+ ],
+ `${name} not in the middle`
+ );
+
+ // Redo over region.
+ await formatHelper.selectTextRange(parts[1].start, parts[1].end);
+ await formatHelper.selectStyle(style);
+ formatHelper.assertMessageParagraph(
+ [{ tags, text: fullText }],
+ `${name} on all again`
+ );
+
+ // Reset by unselecting all again.
+ await formatHelper.selectTextRange(parts[0].start, parts[2].end);
+ await formatHelper.selectStyle(style);
+ }
+
+ formatHelper.assertMessageParagraph([fullText], "No style at end");
+
+ close_compose_window(controller);
+});
+
+add_task(async function test_induced_text_styling() {
+ let controller = open_compose_new_mail();
+ let formatHelper = new FormatHelper(controller.window);
+
+ formatHelper.focusMessage();
+
+ for (let style of formatHelper.styleDataMap.values()) {
+ if (!style.implies && !style.linked) {
+ continue;
+ }
+ let tags = new Set();
+ tags.add(style.tag);
+ let name = style.name;
+
+ // Start styling.
+ await formatHelper.selectStyle(style);
+ let text = `test-${name}`;
+ await formatHelper.typeInMessage(text);
+ await formatHelper.assertShownStyles(style, `${name} initial text`);
+ formatHelper.assertMessageParagraph(
+ [{ tags, text }],
+ `${name} initial text`
+ );
+
+ if (style.implies) {
+ // Unselecting implied styles will be ignored.
+ let desc = `${style.implies.name} implied by ${name}`;
+ await formatHelper.selectTextRange(0, text.length);
+ await formatHelper.assertShownStyles(
+ style,
+ `Before trying to deselect ${desc}`
+ );
+
+ await formatHelper.selectStyle(style.implies);
+ formatHelper.assertMessageParagraph(
+ [{ tags, text }],
+ `After trying to deselect ${desc}`
+ );
+ await formatHelper.assertShownStyles(
+ style,
+ `After trying to deselect ${desc}`
+ );
+ }
+ if (style.linked) {
+ // Unselecting the linked style also unselects the current one.
+ let desc = `${style.linked.name} linked from ${name}`;
+ await formatHelper.selectTextRange(0, text.length);
+ await formatHelper.assertShownStyles(style, `Before unselecting ${desc}`);
+ await formatHelper.selectStyle(style.linked);
+
+ formatHelper.assertMessageParagraph([text], `After unselecting ${desc}`);
+ await formatHelper.assertShownStyles(null, `After unselecting ${desc}`);
+ }
+
+ await formatHelper.emptyParagraph();
+ // Select again to unselect for next loop cycle.
+ if (style.linked) {
+ await formatHelper.selectStyle(style.linked);
+ }
+ if (style.implies) {
+ await formatHelper.selectStyle(style.implies);
+ }
+ await formatHelper.emptyParagraph();
+ }
+
+ close_compose_window(controller);
+});
+
+add_task(async function test_fixed_width_text_styling_font_change() {
+ let controller = open_compose_new_mail();
+ let formatHelper = new FormatHelper(controller.window);
+
+ formatHelper.focusMessage();
+
+ await formatHelper.assertShownFont("", "Variable width to start");
+ for (let style of formatHelper.styleDataMap.values()) {
+ if (
+ style.name !== "tt" &&
+ style.linked?.name !== "tt" &&
+ style.implies?.name !== "tt"
+ ) {
+ continue;
+ }
+
+ let tags = new Set();
+ tags.add(style.tag);
+ let name = style.name;
+
+ // Start styling.
+ await formatHelper.selectStyle(style);
+ // See Bug 1716840.
+ // await formatHelper.assertShownFont(
+ // "monospace",
+ // `monospace when ${name} selected`
+ // );
+
+ await formatHelper.typeInMessage(`test-${name}`);
+ await formatHelper.assertShownFont(
+ "monospace",
+ `monospace when ${name} selected and typing`
+ );
+
+ // Stop styling.
+ await formatHelper.selectStyle(style);
+ // See Bug 1716840.
+ // await formatHelper.assertShownFont(
+ // "",
+ // `Variable Width when ${name} unselected`
+ // );
+
+ await formatHelper.typeInMessage("test-none");
+ await formatHelper.assertShownFont(
+ "",
+ `Variable Width when ${name} unselected and typing`
+ );
+
+ await formatHelper.selectTextRange(1, 3);
+ await formatHelper.assertShownFont(
+ "monospace",
+ `monospace when ${name} region highlighted`
+ );
+ // Select the same font does nothing
+ await formatHelper.selectFont("monospace");
+ formatHelper.assertMessageParagraph(
+ [{ tags, text: `test-${name}` }, "test-none"],
+ `No change when ${name} region has monospace selected`
+ );
+
+ // Try to change the font selection to variable width.
+ await formatHelper.selectFont("");
+ if (name === "tt") {
+ // "tt" style is removed.
+ formatHelper.assertMessageParagraph(
+ [{ tags, text: "t" }, "es", { tags, text: `t-${name}` }, "test-none"],
+ `variable width when ${name} region has font unset`
+ );
+ await formatHelper.assertShownFont(
+ "",
+ `Variable Width when ${name} region has font unset`
+ );
+ // Reset by selecting the style.
+ // Note: Reselecting the "monospace" font will not add the tt style back.
+ await formatHelper.selectStyle(style);
+ }
+ // Otherwise, the style is unchanged.
+ formatHelper.assertMessageParagraph(
+ [{ tags, text: `test-${name}` }, "test-none"],
+ `Still ${name} style in region`
+ );
+ await formatHelper.assertShownFont(
+ "monospace",
+ `Still monospace for ${name} region`
+ );
+
+ // Change the font to something else.
+ let font = formatHelper.commonFonts[0];
+ await formatHelper.selectFont(font);
+ // Doesn't remove the style, but adds the font.
+ formatHelper.assertMessageParagraph(
+ [
+ { tags, text: "t" },
+ // See Bug 1718779
+ // Whilst the font covers this region, it is actually suppressed by the
+ // styling tags. In this case, the ordering of the <font> and, e.g.,
+ // <tt> element matters.
+ { tags, font, text: "es" },
+ { tags, text: `t-${name}` },
+ "test-none",
+ ],
+ `"${font}" when ${name} region has font set`
+ );
+ // See Bug 1718779
+ // The desired font is shown at first, but then switches to Fixed Width
+ // again.
+ // await formatHelper.assertShownFont(
+ // font,
+ // `"${font}" when ${name} region has font set`
+ //);
+
+ await formatHelper.emptyParagraph();
+ // Select again to unselect for next loop cycle.
+ if (style.linked) {
+ await formatHelper.selectStyle(style.linked);
+ }
+ if (style.implies) {
+ await formatHelper.selectStyle(style.implies);
+ }
+ await formatHelper.emptyParagraph();
+ }
+
+ close_compose_window(controller);
+});