import { describe, test } from "@odoo/hoot"; import { press } from "@odoo/hoot-dom"; import { tick } from "@odoo/hoot-mock"; import { testEditor } from "../_helpers/editor"; import { insertText, splitBlock } from "../_helpers/user_actions"; import { unformat } from "../_helpers/format"; import { MAIN_PLUGINS } from "@html_editor/plugin_sets"; import { QWebPlugin } from "@html_editor/others/qweb_plugin"; describe("Selection collapsed", () => { describe("Basic", () => { test("should duplicate an empty paragraph", async () => { await testEditor({ contentBefore: "

[]

", stepFunction: splitBlock, contentAfter: "


[]

", }); // TODO this cannot actually be tested currently as a // backspace/delete in that case is not even detected // (no input event to rollback) // await testEditor({ // contentBefore: '

[
]

', // stepFunction: splitBlock, // contentAfter: '


[]

', // }); await testEditor({ contentBefore: "


[]

", stepFunction: splitBlock, contentAfter: "


[]

", }); }); test("should insert an empty paragraph before a paragraph", async () => { await testEditor({ contentBefore: "

[]abc

", stepFunction: splitBlock, contentAfter: "


[]abc

", }); await testEditor({ contentBefore: "

[] abc

", stepFunction: splitBlock, // JW cAfter: '


[]abc

', contentAfter: "


[] abc

", }); }); test("should split a paragraph in two", async () => { await testEditor({ contentBefore: "

ab[]cd

", stepFunction: splitBlock, contentAfter: "

ab

[]cd

", }); await testEditor({ contentBefore: "

ab []cd

", stepFunction: splitBlock, // The space is converted to a non-breaking // space so it is visible. contentAfter: "

ab 

[]cd

", }); await testEditor({ contentBefore: "

ab[] cd

", stepFunction: splitBlock, // The space is converted to a non-breaking // space so it is visible. contentAfter: "

ab

[] cd

", }); }); test("should insert an empty paragraph after a paragraph", async () => { await testEditor({ contentBefore: "

abc[]

", stepFunction: splitBlock, contentAfter: "

abc

[]

", }); await testEditor({ contentBefore: "

abc[]

", stepFunction: splitBlock, contentAfter: "

abc

[]

", }); }); test("should split block without afecting the uploaded document link", async () => { await testEditor({ contentBefore: `

abc[]def

`, stepFunction: splitBlock, contentAfter: `

abc

[]def

`, }); }); test("should split block without afecting the uploaded document link (2)", async () => { await testEditor({ contentBefore: `

abc[]

`, stepFunction: splitBlock, contentAfter: `

abc

[]

`, }); }); test("should not split block with conditional template", async () => { await testEditor({ contentBefore: unformat(`

[]

`), stepFunction: splitBlock, contentAfter: unformat(`


[]

`), config: { Plugins: [...MAIN_PLUGINS, QWebPlugin] }, }); }); }); describe("Pre", () => { test("should insert a line break within the pre", async () => { await testEditor({ contentBefore: "
ab[]cd
", stepFunction: splitBlock, contentAfter: "
ab
[]cd
", }); }); test("should insert a line break within the pre containing inline element", async () => { await testEditor({ contentBefore: "
ab[]cd
", stepFunction: splitBlock, contentAfter: "
ab
[]c
d
", }); }); test("should insert a line break within the pre containing inline elementd", async () => { await testEditor({ contentBefore: "
ab[]cd
", stepFunction: splitBlock, contentAfter: "
ab
[]c
d
", }); }); test("should insert a new paragraph after the pre", async () => { await testEditor({ contentBefore: "
abc[]
", stepFunction: splitBlock, contentAfter: "
abc

[]

", }); }); test("should insert a new paragraph after the pre containing inline element", async () => { await testEditor({ contentBefore: "
abc[]
", stepFunction: splitBlock, contentAfter: "
abc

[]

", }); }); test("should insert a new paragraph after the pre containing inline elements", async () => { await testEditor({ contentBefore: "
abc[]
", stepFunction: splitBlock, contentAfter: "
abc

[]

", }); }); test("should be able to break out of an empty pre", async () => { await testEditor({ contentBefore: "
[]
", stepFunction: splitBlock, contentAfter: "

[]

", }); }); test("should insert a new line within the pre", async () => { await testEditor({ contentBefore: "

abc

def[]

", stepFunction: splitBlock, contentAfter: "

abc

def

[]

", }); }); test("should insert a new line after pre", async () => { await testEditor({ contentBefore: "

abc

def

[]

", stepFunction: splitBlock, contentAfter: "

abc

def

[]

", }); }); }); describe("Blockquote", () => { test("should insert a new paragraph after the blockquote", async () => { await testEditor({ contentBefore: "
abc[]
", stepFunction: splitBlock, contentAfter: "
abc

[]

", }); }); test("should insert a new paragraph after the blockquote containing inline element", async () => { await testEditor({ contentBefore: "
abc[]
", stepFunction: splitBlock, contentAfter: "
abc

[]

", }); }); test("should be able to break out of an empty blockquote", async () => { await testEditor({ contentBefore: "
[]
", stepFunction: splitBlock, contentAfter: "

[]

", }); }); test("should insert a new line within the blockquote", async () => { await testEditor({ contentBefore: "

abc

def[]

", stepFunction: splitBlock, contentAfter: "

abc

def

[]

", }); }); test("should insert a new line after blockquote", async () => { await testEditor({ contentBefore: "

abc

def

[]

", stepFunction: splitBlock, contentAfter: "

abc

def

[]

", }); }); }); describe("Consecutive", () => { test("should duplicate an empty paragraph twice", async () => { await testEditor({ contentBefore: "

[]

", stepFunction: async (editor) => { splitBlock(editor); splitBlock(editor); }, contentAfter: "



[]

", }); // TODO this cannot actually be tested currently as a // backspace/delete in that case is not even detected // (no input event to rollback) // await testEditor({ // contentBefore: '

[
]

', // stepFunction: async (editor) => { // splitBlock(editor); // splitBlock(editor); // }, // contentAfter: '



[]

', // }); await testEditor({ contentBefore: "


[]

", stepFunction: async (editor) => { splitBlock(editor); splitBlock(editor); }, contentAfter: "



[]

", }); }); test("should insert two empty paragraphs before a paragraph", async () => { await testEditor({ contentBefore: "

[]abc

", stepFunction: async (editor) => { splitBlock(editor); splitBlock(editor); }, contentAfter: "



[]abc

", }); }); test("should split a paragraph in three", async () => { await testEditor({ contentBefore: "

ab[]cd

", stepFunction: async (editor) => { splitBlock(editor); splitBlock(editor); }, contentAfter: "

ab


[]cd

", }); }); test("should split a paragraph in four", async () => { await testEditor({ contentBefore: "

ab[]cd

", stepFunction: async (editor) => { splitBlock(editor); splitBlock(editor); splitBlock(editor); }, contentAfter: "

ab



[]cd

", }); }); test("should insert two empty paragraphs after a paragraph", async () => { await testEditor({ contentBefore: "

abc[]

", stepFunction: async (editor) => { splitBlock(editor); splitBlock(editor); }, contentAfter: "

abc


[]

", }); }); }); describe("Format", () => { test("should split a paragraph before a format node", async () => { await testEditor({ contentBefore: "

abc[]def

", stepFunction: splitBlock, contentAfter: "

abc

[]def

", }); await testEditor({ // That selection is equivalent to [] contentBefore: "

abc[]def

", stepFunction: splitBlock, contentAfter: "

abc

[]def

", }); await testEditor({ contentBefore: "

abc []def

", stepFunction: splitBlock, // The space is converted to a non-breaking // space so it is visible (because it's after a //
). contentAfter: "

abc 

[]def

", }); await testEditor({ contentBefore: "

abc[] def

", stepFunction: splitBlock, // The space is converted to a non-breaking // space so it is visible (because it's before a //
). // JW cAfter: '

abc

[] def

', contentAfter: "

abc

[] def

", }); }); test("should split a paragraph after a format node", async () => { await testEditor({ contentBefore: "

abc[]def

", stepFunction: splitBlock, contentAfter: "

abc

[]def

", }); await testEditor({ // That selection is equivalent to
[] contentBefore: "

abc[]def

", stepFunction: splitBlock, contentAfter: "

abc

[]def

", }); await testEditor({ contentBefore: "

abc[] def

", stepFunction: splitBlock, // The space is converted to a non-breaking // space so it is visible. contentAfter: "

abc

[] def

", }); await testEditor({ contentBefore: "

abc []def

", stepFunction: splitBlock, // The space is converted to a non-breaking // space so it is visible (because it's before a //
). contentAfter: "

abc 

[]def

", }); }); test("should split a paragraph at the beginning of a format node", async () => { await testEditor({ contentBefore: "

[]abc

", stepFunction: splitBlock, contentAfter: "


[]abc

", }); await testEditor({ // That selection is equivalent to [] contentBefore: "

[]abc

", stepFunction: splitBlock, contentAfter: "


[]abc

", }); await testEditor({ contentBefore: "

[] abc

", stepFunction: splitBlock, // The space should have been parsed away. // JW cAfter: '


[]abc

', contentAfter: "


[] abc

", }); }); test("should split a paragraph within a format node", async () => { await testEditor({ contentBefore: "

ab[]cd

", stepFunction: splitBlock, contentAfter: "

ab

[]cd

", }); await testEditor({ contentBefore: "

ab []cd

", stepFunction: splitBlock, // The space is converted to a non-breaking // space so it is visible. contentAfter: "

ab 

[]cd

", }); await testEditor({ contentBefore: "

ab[] cd

", stepFunction: splitBlock, // The space is converted to a non-breaking // space so it is visible. contentAfter: "

ab

[] cd

", }); }); test("should split a paragraph at the end of a format node", async () => { await testEditor({ contentBefore: "

abc[]

", stepFunction: splitBlock, contentAfter: "

abc

[]

", }); await testEditor({ // That selection is equivalent to
[] contentBefore: "

abc[]

", stepFunction: splitBlock, contentAfter: "

abc

[]

", }); await testEditor({ contentBefore: "

abc[]

", stepFunction: splitBlock, // The space should have been parsed away. contentAfter: "

abc

[]

", }); }); async function splitBlockA(editor) { // splitBlock in an tag will open the linkPopover which will take the focus. // So we need to put the selection back into the editor splitBlock(editor); editor.shared.selection.focusEditable(); await tick(); } // @todo: re-evaluate this possibly outdated comment: // skipping these tests cause with the link isolation the cursor can be put // inside/outside the link so the user can choose where to insert the line break // see `anchor.nodeName === "A" && brEls.includes(anchor.firstChild)` in line_break_plugin.js test("should insert line breaks outside the edges of an anchor in unbreakable", async () => { await testEditor({ contentBefore: "
ab[]cd
", stepFunction: splitBlockA, contentAfter: "
ab
[]cd
", }); await testEditor({ contentBefore: "
a[]b
", stepFunction: splitBlockA, contentAfter: "
a
[]b
", }); await testEditor({ contentBefore: "
ab[]
", stepFunction: splitBlockA, contentAfter: "
ab

[]
", }); await testEditor({ contentBefore: "
ab[]cd
", stepFunction: splitBlockA, contentAfter: "
ab
[]cd
", }); }); test("should insert a paragraph break outside the starting edge of an anchor", async () => { await testEditor({ contentBefore: "

[]ab

", stepFunction: splitBlockA, contentAfterEdit: '


\ufeff\ufeff[]ab\ufeff\ufeff

', contentAfter: "


[]ab

", }); await testEditor({ contentBefore: "

ab[]cd

", stepFunction: splitBlockA, contentAfterEdit: '

ab

\ufeff\ufeff[]cd\ufeff\ufeff

', contentAfter: "

ab

[]cd

", }); }); test("should insert a paragraph break in the middle of an anchor", async () => { await testEditor({ contentBefore: "

a[]b

", stepFunction: splitBlockA, contentAfterEdit: '

\ufeff\ufeffa\ufeff\ufeff

\ufeff\ufeff[]b\ufeff\ufeff

', contentAfter: "

a

[]b

", }); }); test("should insert a paragraph break outside the ending edge of an anchor", async () => { await testEditor({ contentBefore: "

ab[]

", stepFunction: async (editor) => { splitBlock(editor); await press("enter"); editor.shared.selection.focusEditable(); await tick(); }, contentAfterEdit: `

\ufeff\ufeffab\ufeff\ufeff

[]

`, contentAfter: `

ab

[]

`, }); await testEditor({ contentBefore: "

ab[]cd

", stepFunction: splitBlockA, contentAfterEdit: "

\ufeff\ufeffab\ufeff\ufeff

[]cd

", contentAfter: "

ab

[]cd

", }); }); }); describe("With attributes", () => { test("should insert an empty paragraph before a paragraph with a span with a class", async () => { await testEditor({ contentBefore: '

ab

[]cd

', stepFunction: splitBlock, contentAfter: '

ab


[]cd

', }); }); test("should split a paragraph with a span with a bold in two", async () => { await testEditor({ contentBefore: '

ab[]cd

', stepFunction: splitBlock, contentAfter: '

ab

[]cd

', }); }); test("should split a paragraph at its end, with a paragraph after it, and both have the same class", async () => { await testEditor({ contentBefore: '

a[]


', stepFunction: splitBlock, contentAfter: '

a

[]


', }); }); }); describe("POC extra tests", () => { test("should insert a paragraph after an empty h1", async () => { await testEditor({ contentBefore: "

[]

", stepFunction: splitBlock, contentAfter: "


[]

", }); }); test("should insert a paragraph after an empty h1 with styles and a zero-width space", async () => { await testEditor({ contentBefore: '

[]\u200B

', stepFunction: splitBlock, contentAfter: "


[]

", }); }); test("should insert a new paragraph after an h1 with style", async () => { await testEditor({ contentBefore: `

ab[]

`, stepFunction: splitBlock, contentAfter: `

ab

[]

`, }); }); }); }); describe("Selection not collapsed", () => { test("should delete the first half of a paragraph, then split it", async () => { // Forward selection await testEditor({ contentBefore: "

[ab]cd

", stepFunction: splitBlock, contentAfter: "


[]cd

", }); // Backward selection await testEditor({ contentBefore: "

]ab[cd

", stepFunction: splitBlock, contentAfter: "


[]cd

", }); }); test("should delete part of a paragraph, then split it", async () => { // Forward selection await testEditor({ contentBefore: "

a[bc]d

", stepFunction: splitBlock, contentAfter: "

a

[]d

", }); // Backward selection await testEditor({ contentBefore: "

a]bc[d

", stepFunction: splitBlock, contentAfter: "

a

[]d

", }); }); test("should delete the last half of a paragraph, then split it", async () => { // Forward selection await testEditor({ contentBefore: "

ab[cd]

", stepFunction: splitBlock, contentAfter: "

ab

[]

", }); // Backward selection await testEditor({ contentBefore: "

ab]cd[

", stepFunction: splitBlock, contentAfter: "

ab

[]

", }); }); test("should delete all contents of a paragraph, then split it", async () => { // Forward selection await testEditor({ contentBefore: "

[abcd]

", stepFunction: splitBlock, contentAfter: "


[]

", }); // Backward selection await testEditor({ contentBefore: "

]abcd[

", stepFunction: splitBlock, contentAfter: "


[]

", }); }); test("should keep the selection at the start of the second text node after paragraph break", async () => { await testEditor({ contentBefore: "

ab
[c]de

", stepFunction: async (editor) => { await insertText(editor, "f"); }, contentAfter: "

ab
f[]de

", }); }); }); describe("Table", () => { test("should remove all contents of an anchor td and split paragraph on forward selection", async () => { // Forward selection await testEditor({ contentBefore: `

[abc

def

abcd

ab]




`, stepFunction: splitBlock, contentAfter: `


[]

abcd

ab




`, }); }); test("should remove all contents of an anchor td and split paragraph on backward selection", async () => { // Backward selection await testEditor({ contentBefore: `

]ab

abcd

abc

def[




`, stepFunction: splitBlock, contentAfter: `

ab

abcd


[]




`, }); }); test("remove selected text and insert paragraph tag within a table cell and enter key is pressed", async () => { await testEditor({ contentBefore: `

[Test

Test

Test]


`, stepFunction: splitBlock, contentAfter: `


[]


`, }); }); });