diff options
Diffstat (limited to 'comm/mail/test/browser/composition/browser_text_styling.js')
-rw-r--r-- | comm/mail/test/browser/composition/browser_text_styling.js | 609 |
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); +}); |