import { parseHTML } from "@html_editor/utils/html"; import { describe, expect, test } from "@odoo/hoot"; import { tick } from "@odoo/hoot-mock"; import { setupEditor, testEditor } from "../_helpers/editor"; import { unformat } from "../_helpers/format"; import { getContent } from "../_helpers/selection"; import { dispatchClean } from "../_helpers/dispatch"; function span(text) { const span = document.createElement("span"); span.innerText = text; span.classList.add("a"); return span; } describe("collapsed selection", () => { test("should insert html in an empty paragraph / empty editable", async () => { await testEditor({ contentBefore: "
[]
\u200b[]
', contentAfter: '[]
', }); }); test("should insert html after an empty paragraph", async () => { await testEditor({ // This scenario is only possible with the allowInlineAtRoot option. contentBefore: "a[]b
", stepFunction: async (editor) => { editor.shared.dom.insert( parseHTML(editor.document, '') ); editor.shared.history.addStep(); }, contentAfterEdit: 'a\u200b[]b
', contentAfter: 'a[]b
', }); }); test("should insert html in between naked text in the editable", async () => { await testEditor({ contentBefore: "a[]b
", stepFunction: async (editor) => { editor.shared.dom.insert( parseHTML(editor.document, '') ); editor.shared.history.addStep(); }, contentAfterEdit: 'a\u200b[]b
', contentAfter: 'a[]b
', }); }); test("should insert several html nodes in between naked text in the editable", async () => { await testEditor({ contentBefore: "a[]e
b
c
d
")); editor.shared.history.addStep(); }, contentAfter: "ab
c
d[]e
", }); }); test("should keep a paragraph after a div block", async () => { await testEditor({ contentBefore: "[]
content
content
[]
abc[]", stepFunction: async (editor) => { editor.shared.dom.insert(parseHTML(editor.document, "
ghi
def")); editor.shared.history.addStep(); }, contentAfter: "
abcdef[]", }); }); test('should keep an "empty" block which contains fontawesome nodes when inserting multiple nodes', async () => { await testEditor({ contentBefore: "
ghi
content[]
", stepFunction: async (editor) => { editor.shared.dom.insert( parseHTML( editor.document, 'unwrapped
culprit
after
' ) ); editor.shared.history.addStep(); }, contentAfter: 'contentunwrapped
culprit
after[]
', }); }); test("should not unwrap single node if the selection anchorNode is the editable", async () => { await testEditor({ contentBefore: "content
", stepFunction: async (editor) => { editor.shared.selection.setCursorEnd(editor.editable, false); editor.shared.selection.focusEditable(); editor.shared.dom.insert(parseHTML(editor.document, "def
")); editor.shared.history.addStep(); }, contentAfter: "content
def[]
", }); }); test("should not unwrap nodes if the selection anchorNode is the editable", async () => { await testEditor({ contentBefore: "content
", stepFunction: async (editor) => { editor.shared.selection.setCursorEnd(editor.editable, false); editor.shared.selection.focusEditable(); await tick(); editor.shared.dom.insert(parseHTML(editor.document, "def
")); editor.shared.history.addStep(); }, contentAfter: "content
def[]
", }); }); test('should insert an "empty" block', async () => { await testEditor({ contentBefore: "abcd[]
", stepFunction: async (editor) => { editor.shared.dom.insert(parseHTML(editor.document, "efgh
")); editor.shared.history.addStep(); }, contentAfter: "abcdefgh
[]
cont[]ent
`, {}); editor.shared.dom.insert( parseHTML(editor.document, "cont
[]ent
` ); }); test("should not unwrap table in unbreakable paragraph find a suitable spot to insert table element", async () => { // P elements' content can only be "phrasing" content // Adding a table within an unbreakable p is not possible // We have to find a better spot to insert the table // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p // https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories#phrasing_content const { editor } = await setupEditor(`cont[]ent
`, {}); editor.shared.dom.insert( parseHTML(editor.document, "content[]
cont[]ent
content[]
[]
a[]
`); }); test("insert inline at the end of a paragraph", async () => { const { el, editor } = await setupEditor(`b[]
`); editor.shared.dom.insert(parseHTML(editor.document, `a`)); editor.shared.history.addStep(); expect(getContent(el)).toBe(`ba[]
`); }); test("insert inline at the start of a paragraph", async () => { const { el, editor } = await setupEditor(`[]b
`); editor.shared.dom.insert(parseHTML(editor.document, `a`)); editor.shared.history.addStep(); expect(getContent(el)).toBe(`a[]b
`); }); test("insert inline at the middle of a paragraph", async () => { const { el, editor } = await setupEditor(`b[]c
`); editor.shared.dom.insert(parseHTML(editor.document, `a`)); editor.shared.history.addStep(); expect(getContent(el)).toBe(`ba[]c
`); }); test("insert block in empty paragraph", async () => { const { el, editor } = await setupEditor(`[]
[]
b[]
`); editor.shared.dom.insert(parseHTML(editor.document, `b
[]
[]b
`); editor.shared.dom.insert(parseHTML(editor.document, `[]b
`); }); test("insert block at the middle of a paragraph", async () => { const { el, editor } = await setupEditor(`b[]c
`); editor.shared.dom.insert(parseHTML(editor.document, `b
[]c
`); }); }); describe("not collapsed selection", () => { test("should delete selection and insert html in its place", async () => { await testEditor({ contentBefore: "[a]
", stepFunction: async (editor) => { editor.shared.dom.insert( parseHTML(editor.document, '') ); }, contentAfterEdit: '\u200b[]
', contentAfter: '[]
', }); }); test("should delete selection and insert html in its place (2)", async () => { await testEditor({ contentBefore: "a[b]c
", stepFunction: async (editor) => { editor.shared.dom.insert( parseHTML(editor.document, '') ); editor.shared.history.addStep(); }, contentAfterEdit: 'a\u200b[]c
', contentAfter: 'a[]c
', }); }); test("should remove a fully selected table then insert a span before it", async () => { await testEditor({ contentBefore: unformat( `a[b
cd | ef |
gh | ij |
k]l
` ), stepFunction: (editor) => { editor.shared.dom.insert(span("TEST")); editor.shared.history.addStep(); }, contentAfter: 'aTEST[]l
', }); }); test("should only remove the text content of cells in a partly selected table", async () => { await testEditor({ contentBefore: unformat( `cd | e[f | gh |
ij | k]l | mn |
op | qr | st |
cd | TEST[] | gh |
ij | mn | |
op | qr | st |
a[b
cd | ef |
g]h | ij |
kl
` ), stepFunction: (editor) => { editor.shared.dom.insert(span("TEST")); editor.shared.history.addStep(); }, contentAfter: unformat( `aTEST[]
kl
` ), }); }); test("should remove a table and some text (even if the table is partly selected)", async () => { await testEditor({ contentBefore: unformat( `ab
cd | ef |
gh | i[j |
k]l
` ), stepFunction: (editor) => { editor.shared.dom.insert(span("TEST")); editor.shared.history.addStep(); }, contentAfter: unformat( `ab
TEST[]l
` ), }); }); test("should remove some text, a table and some more text", async () => { await testEditor({ contentBefore: unformat( `a[b
cd | ef |
gh | ij |
k]l
` ), stepFunction: (editor) => { editor.shared.dom.insert(span("TEST")); editor.shared.history.addStep(); }, contentAfter: `aTEST[]l
`, }); }); test("should remove a selection of several tables", async () => { await testEditor({ contentBefore: unformat( `cd | e[f |
gh | ij |
cd | ef |
gh | ij |
cd | e]f |
gh | ij |
TEST[]
`, }); }); test("should remove a selection including several tables", async () => { await testEditor({ contentBefore: unformat( `0[1
cd | ef |
gh | ij |
23
cd | ef |
gh | ij |
45
cd | ef |
gh | ij |
67]
` ), stepFunction: (editor) => { editor.shared.dom.insert(span("TEST")); editor.shared.history.addStep(); }, contentAfter: `0TEST[]
`, }); }); test("should remove everything, including several tables", async () => { await testEditor({ contentBefore: unformat( `[01
cd | ef |
gh | ij |
23
cd | ef |
gh | ij |
45
cd | ef |
gh | ij |
67]
` ), stepFunction: (editor) => { editor.shared.dom.insert(span("TEST")); editor.shared.history.addStep(); }, contentAfter: `TEST[]
`, }); }); test("should insert html containing ZWNBSP", async () => { await testEditor({ contentBefore: "[]
\uFEFF\uFEFFlink\uFEFF\uFEFF
\uFEFF\uFEFFlink\uFEFF\uFEFF
' ) ); editor.shared.history.addStep(); }, contentAfter: 'link[]
', }); }); });