import { setupHTMLBuilder } from "@html_builder/../tests/helpers"; import { setSelection } from "@html_editor/../tests/_helpers/selection"; import { expandToolbar } from "@html_editor/../tests/_helpers/toolbar"; import { insertText, pasteHtml } from "@html_editor/../tests/_helpers/user_actions"; import { FontPlugin } from "@html_editor/main/font/font_plugin"; import { isTextNode } from "@html_editor/utils/dom_info"; import { parseHTML } from "@html_editor/utils/html"; import { expect, test, describe } from "@odoo/hoot"; import { click, manuallyDispatchProgrammaticEvent, waitFor, queryOne, waitForNone, } from "@odoo/hoot-dom"; import { animationFrame } from "@odoo/hoot-mock"; import { contains, patchWithCleanup } from "@web/../tests/web_test_helpers"; describe.current.tags("desktop"); test("should add an icon from the media modal dialog", async () => { const { getEditor } = await setupHTMLBuilder(`
x
`); const editor = getEditor(); const p = editor.document.querySelector("p"); editor.shared.selection.focusEditable(); editor.shared.selection.setSelection({ anchorNode: p, anchorOffset: 1, focusNode: p, focusOffset: 1, }); await insertText(editor, "/image"); await animationFrame(); await contains(".o-we-command").click(); await contains(".modal .modal-body .nav-item:nth-child(3) a").click(); await contains(".modal .modal-body .fa-heart").click(); expect(p).toHaveInnerHTML(`x\u200b`); }); test("should delete text forward", async () => { const keyPress = async (editor, key) => { await manuallyDispatchProgrammaticEvent(editor.editable, "keydown", { key }); await manuallyDispatchProgrammaticEvent(editor.editable, "keyup", { key }); }; const { getEditor } = await setupHTMLBuilder(`abc
def
`); const editor = getEditor(); const p = editor.editable.querySelector("p"); editor.shared.selection.setSelection({ anchorNode: p, anchorOffset: 1 }); await keyPress(editor, "delete"); // paragraphs get merged expect(p).toHaveInnerHTML("abcdef"); await keyPress(editor, "delete"); // following character gets deleted expect(p).toHaveInnerHTML("abcef"); }); test("unsplittable node predicates should not crash when called with text node argument", async () => { const { getEditor } = await setupHTMLBuilder(`abc
`); const editor = getEditor(); const textNode = editor.editable.querySelector("p").firstChild; expect(isTextNode(textNode)).toBe(true); expect(() => editor.resources.unsplittable_node_predicates.forEach((p) => p(textNode)) ).not.toThrow(); }); test("should set contenteditable to false on .o_not_editable elements", async () => { const { getEditor } = await setupHTMLBuilder(`abc
abc
Some text.
Some more text.
| 1234 |
abc
`); const editor = getEditor(); const p = editor.editable.querySelector("p"); setSelection({ anchorNode: p, anchorOffset: 0, focusOffset: 1 }); await waitFor(".o-we-toolbar"); await expandToolbar(); return { editor, p }; }; const focusAndClick = async (selector) => { const target = await waitFor(selector); manuallyDispatchProgrammaticEvent(target, "mousedown"); manuallyDispatchProgrammaticEvent(target, "focus"); await animationFrame(); // Dropdown menu needs another animation frame to be closed after the // toolbar is closed. await animationFrame(); expect(target).toBeVisible(); manuallyDispatchProgrammaticEvent(target, "mouseup"); manuallyDispatchProgrammaticEvent(target, "click"); }; test("list dropdown should not close on click", async () => { const { editor } = await setup(); click(".o-we-toolbar .btn[name='list_selector']"); const bulletedListButtonSelector = ".dropdown-menu button[name='bulleted_list']"; await focusAndClick(bulletedListButtonSelector); await animationFrame(); expect(bulletedListButtonSelector).toBeVisible(); expect(bulletedListButtonSelector).toHaveClass("active"); expect(!!editor.editable.querySelector("ul li")).toBe(true); }); test("text alignment dropdown should not close on click", async () => { const { p } = await setup(); click(".o-we-toolbar .btn[name='text_align']"); const alignCenterButtonSelector = ".dropdown-menu button.fa-align-center"; await focusAndClick(alignCenterButtonSelector); await animationFrame(); expect(alignCenterButtonSelector).toBeVisible(); expect(alignCenterButtonSelector).toHaveClass("active"); expect(p).toHaveStyle("text-align: center"); }); test("font style dropdown should close only after click", async () => { const { editor } = await setup(); click(".o-we-toolbar .btn[name='font']"); await focusAndClick(".dropdown-menu .dropdown-item[name='h2']"); await animationFrame(); expect(!!editor.editable.querySelector("h2")).toBe(true); }); test("font size dropdown should close only after click", async () => { patchWithCleanup(FontPlugin.prototype, { get fontSizeItems() { return [{ name: "test", className: "test-font-size" }]; }, }); const { p } = await setup(); click(".o-we-toolbar .btn[name='font_size_selector']"); await focusAndClick(".dropdown-menu .dropdown-item"); await animationFrame(); expect(p.firstChild).toHaveClass("test-font-size"); }); test("font selector dropdown should not have normal as an option", async () => { await setup(); click(".o-we-toolbar .btn[name='font']"); await animationFrame(); expect(".o_font_selector_menu .o-dropdown-item[name='div']").toHaveCount(0); }); }); describe("font types", () => { test("Header 1 Display 1 to 4 are available", async () => { const { getEditor } = await setupHTMLBuilder(`abc
`); const editor = getEditor(); const p = editor.editable.querySelector("p"); setSelection({ anchorNode: p, anchorOffset: 0, focusOffset: 1 }); await waitFor(".o-we-toolbar"); click(".o-we-toolbar .btn[name='font']"); await waitFor(".o_font_selector_menu"); const expectedButtons = [ "Header 1 Display 1", "Header 1 Display 2", "Header 1 Display 3", "Header 1 Display 4", ]; expectedButtons.forEach((button) => { expect(`.o_font_selector_menu .o-dropdown-item:contains('${button}')`).toHaveCount(1); }); }); test("'Light' is available", async () => { const { getEditor } = await setupHTMLBuilder(`abc
`); const editor = getEditor(); const p = editor.editable.querySelector("p"); setSelection({ anchorNode: p, anchorOffset: 0, focusOffset: 1 }); await waitFor(".o-we-toolbar"); click(".o-we-toolbar .btn[name='font']"); await waitFor(".o_font_selector_menu"); expect(`.o_font_selector_menu .o-dropdown-item:contains('Light')`).toHaveCount(1); click(".o_font_selector_menu .o-dropdown-item:contains('Light')"); await waitForNone(".o_font_selector_menu"); expect(".o-we-toolbar .btn[name='font']").toHaveText("Light"); expect(editor.editable.querySelector("p")).toHaveClass("lead"); }); test("'Small' is available", async () => { const { getEditor } = await setupHTMLBuilder(`abc
`); const editor = getEditor(); const p = editor.editable.querySelector("p"); setSelection({ anchorNode: p, anchorOffset: 0, focusOffset: 1 }); await waitFor(".o-we-toolbar"); click(".o-we-toolbar .btn[name='font']"); await waitFor(".o_font_selector_menu"); expect(`.o_font_selector_menu .o-dropdown-item:contains('Small')`).toHaveCount(1); click(".o_font_selector_menu .o-dropdown-item:contains('Small')"); await waitForNone(".o_font_selector_menu"); expect(".o-we-toolbar .btn[name='font']").toHaveText("Small"); expect(editor.editable.querySelector("p")).toHaveClass("small"); }); });