import { describe, expect, test } from "@odoo/hoot";
import { animationFrame, tick } from "@odoo/hoot-mock";
import { setupEditor, testEditor } from "../_helpers/editor";
import { unformat } from "../_helpers/format";
import { bold, resetSize, setColor } from "../_helpers/user_actions";
import { getContent, setSelection } from "../_helpers/selection";
import { manuallyDispatchProgrammaticEvent, queryAll } from "@odoo/hoot-dom";
import { nodeSize } from "@html_editor/utils/position";
describe("custom selection", () => {
test("should indicate selected cells with blue background", async () => {
const { el } = await setupEditor(
unformat(`
`)
);
expect(getContent(el)).toBe(
unformat(`
`)
);
const overlayColorTDs = queryAll("table td").map(
(td) => getComputedStyle(td)["box-shadow"]
);
// Unselected cells should have the default background color, without any overlay
expect(overlayColorTDs[0]).toBe("none");
// Selected cells should have a box-shadow color
expect(overlayColorTDs[1]).not.toBe("none");
expect(overlayColorTDs[2]).not.toBe("none");
});
});
describe("select a full table on cross over", () => {
describe("select", () => {
test("should select some characters and a table", async () => {
await testEditor({
contentBefore:
"a[bc
",
contentAfterEdit:
"a[bc
" +
'' +
'| a]b | ' +
'cd | ' +
'ef | ' +
"
",
});
});
test("should select a table and some characters", async () => {
await testEditor({
contentBefore:
"a]bc
",
contentAfterEdit:
'a]bc
',
});
});
test("should select some characters, a table and some more characters", async () => {
await testEditor({
contentBefore:
"a[bc
a]bc
",
contentAfterEdit:
'a[bc
a]bc
',
});
});
test("should select some characters, a table, some more characters and another table", async () => {
await testEditor({
contentBefore:
"a[bc
abc
",
contentAfterEdit:
'a[bc
' +
'abc
',
});
});
test("should select some characters, a table, some more characters, another table and some more characters", async () => {
await testEditor({
contentBefore:
"a[bc
abc
a]bc
",
contentAfterEdit:
'a[bc
' +
'abc
a]bc
',
});
});
});
describe("toggleFormat", () => {
test("should apply bold to some characters and a table", async () => {
await testEditor({
contentBefore:
"a[bc
" +
"| a]b | " +
"cd | " +
"ef | " +
"
",
stepFunction: bold,
contentAfterEdit:
"a[bc
" +
'' +
'| ab | ' +
'cd | ' +
'ef] | ' +
"
",
});
});
test("should apply bold to a table and some characters", async () => {
await testEditor({
contentBefore:
"" +
"| ab | " +
"cd | " +
"e[f | " +
"
a]bc
",
stepFunction: bold,
contentAfterEdit:
'' +
'| [ab | ' +
'cd | ' +
'ef | ' +
"
" +
"a]bc
",
});
});
test("should apply bold to some characters, a table and some more characters", async () => {
await testEditor({
contentBefore:
"a[bc
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"a]bc
",
stepFunction: bold,
contentAfterEdit:
"a[bc
" +
'' +
'| ab | ' +
'cd | ' +
'ef | ' +
"
" +
"a]bc
",
});
});
test("should apply bold to some characters, a table, some more characters and another table", async () => {
await testEditor({
contentBefore:
"a[bc
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"abc
" +
"" +
"| a]b | " +
"cd | " +
"ef | " +
"
",
stepFunction: bold,
contentAfterEdit:
"a[bc
" +
'' +
'| ab | ' +
'cd | ' +
'ef | ' +
"
" +
"abc
" +
'' +
'| ab | ' +
'cd | ' +
'ef] | ' +
"
",
});
});
test("should apply bold to some characters, a table, some more characters, another table and some more characters", async () => {
await testEditor({
contentBefore:
"a[bc
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"abc
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"a]bc
",
stepFunction: bold,
contentAfterEdit:
"a[bc
" +
'' +
'| ab | ' +
'cd | ' +
'ef | ' +
"
" +
"abc
" +
'' +
'| ab | ' +
'cd | ' +
'ef | ' +
"
" +
"a]bc
",
});
});
});
describe("color", () => {
test("should apply a color to some characters and a table", async () => {
await testEditor({
contentBefore: unformat(`
a[bc
`),
stepFunction: async editor => {
// Table selection happens on selectionchange
// event which is fired in the next tick.
await tick();
setColor("aquamarine", "color")(editor);
},
contentAfterEdit: unformat(`
a[bc
`),
});
});
test("should apply a color to a table and some characters", async () => {
await testEditor({
contentBefore:
"" +
"| ab | " +
"cd | " +
"e[f | " +
"
a]bc
",
stepFunction: setColor("aquamarine", "color"),
contentAfterEdit: unformat(`
a]bc
`),
});
});
test("should apply a color to some characters, a table and some more characters", async () => {
await testEditor({
contentBefore:
"a[bc
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"a]bc
",
stepFunction: setColor("aquamarine", "color"),
contentAfterEdit: unformat(`
a[bc
a]bc
`),
});
});
test("should apply a color to some characters, a table, some more characters and another table", async () => {
await testEditor({
contentBefore:
"a[bc
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"abc
" +
"" +
"| a]b | " +
"cd | " +
"ef | " +
"
",
stepFunction: async (editor) => {
// Table selection happens on selectionchange
// event which is fired in the next tick.
await tick();
setColor("aquamarine", "color")(editor);
},
contentAfterEdit: unformat(`
a[bc
abc
`),
});
});
test("should apply a color to some characters, a table, some more characters, another table and some more characters", async () => {
await testEditor({
contentBefore:
"a[bc
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"abc
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"a]bc
",
stepFunction: setColor("aquamarine", "color"),
contentAfterEdit: unformat(`
a[bc
abc
a]bc
`),
});
});
});
});
describe("single cell selection", () => {
test("should not select single cell via mouse movement if content is not fully selected", async () => {
const content = unformat(`
`);
const { el } = await setupEditor(content);
const firstTd = el.querySelector("td");
const firstP = firstTd.firstElementChild;
const textNode = firstP.firstChild;
// Get bounding rect of selection range at the end of text.
const range = document.createRange();
range.setStart(textNode, nodeSize(textNode));
range.setEnd(textNode, nodeSize(textNode));
const rangeRect = range.getBoundingClientRect();
// Simulate mousedown at the end of text.
await manuallyDispatchProgrammaticEvent(firstP, "mousedown", {
detail: 1,
clientX: rangeRect.right,
clientY: rangeRect.top,
});
// Put cursor at the end of text.
setSelection({
anchorNode: textNode,
anchorOffset: nodeSize(textNode),
});
await animationFrame();
// Simulate attempt to select single cell.
manuallyDispatchProgrammaticEvent(firstP, "mousemove", {
detail: 1,
clientX: rangeRect.right,
clientY: rangeRect.top,
});
manuallyDispatchProgrammaticEvent(firstP, "mousemove", {
detail: 1,
clientX: rangeRect.right + 15,
clientY: rangeRect.top,
});
manuallyDispatchProgrammaticEvent(firstP, "mouseup", {
detail: 1,
clientX: rangeRect.right + 15,
clientY: rangeRect.top,
});
await animationFrame();
expect(firstTd).not.toHaveClass("o_selected_td");
});
});
describe("select columns on cross over", () => {
describe("select", () => {
test("should select two columns", async () => {
await testEditor({
contentBefore:
"",
contentAfterEdit:
'' +
'| a[b | ' +
'c]d | ' +
"ef | " +
"
",
});
});
test("should select a whole row", async () => {
await testEditor({
contentBefore:
"",
contentAfterEdit:
'' +
'| a[b | ' +
'cd | ' +
'e]f | ' +
"
| ab | cd | ef |
",
});
});
test("should select a whole column", async () => {
await testEditor({
contentBefore:
"" +
"| a[b | cd | ef |
" +
"| ab | cd | ef |
" +
"| a]b | cd | ef |
" +
"
",
contentAfterEdit:
'' +
"" +
'| a[b | ' +
"cd | " +
"ef | " +
"
" +
"" +
'| ab | ' +
"cd | " +
"ef | " +
"
" +
"" +
'| a]b | ' +
"cd | " +
"ef | " +
"
" +
"
",
});
});
test("should select from (0,0) to (1,1) in a 3x3 table", async () => {
await testEditor({
contentBefore:
"" +
"| a[b | cd | ef |
" +
"| ab | c]d | ef |
" +
"| ab | cd | ef |
" +
"
",
contentAfterEdit:
'' +
"" +
'| a[b | ' +
'cd | ' +
"ef | " +
"
" +
"" +
'| ab | ' +
'c]d | ' +
"ef | " +
"
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"
",
});
});
test("should select a whole table", async () => {
await testEditor({
contentBefore:
"" +
"| a[b | cd | ef |
" +
"| ab | cd | ef |
" +
"| ab | cd | e]f |
" +
"
",
contentAfterEdit:
'' +
"" +
'| a[b | ' +
'cd | ' +
'ef | ' +
"
" +
"" +
'| ab | ' +
'cd | ' +
'ef | ' +
"
" +
"" +
'| ab | ' +
'cd | ' +
'e]f | ' +
"
" +
"
",
});
});
});
describe("toggleFormat", () => {
test("should apply bold to two columns", async () => {
await testEditor({
contentBefore:
"" +
"| a[b | " +
"c]d | " +
"ef | " +
"
",
stepFunction: bold,
contentAfterEdit:
'' +
'| [ab | ' +
'cd] | ' +
"ef | " +
"
",
});
});
test("should apply bold to a whole row", async () => {
await testEditor({
contentBefore:
"" +
"| a[b | " +
"cd | " +
"e]f | " +
"
| ab | cd | ef |
",
stepFunction: bold,
contentAfterEdit:
'' +
'| [ab | ' +
'cd | ' +
'ef] | ' +
"
| ab | cd | ef |
",
});
});
test("should apply bold to a whole column", async () => {
await testEditor({
contentBefore:
"" +
"" +
"| a[b | " +
"cd | " +
"ef | " +
"
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"" +
"| a]b | " +
"cd | " +
"ef | " +
"
" +
"
",
stepFunction: bold,
contentAfterEdit:
'' +
"" +
'| [ab | ' +
"cd | " +
"ef | " +
"
" +
"" +
'| ab | ' +
"cd | " +
"ef | " +
"
" +
"" +
'| ab] | ' +
"cd | " +
"ef | " +
"
" +
"
",
});
});
test("should apply bold from (0,0) to (1,1) in a 3x3 table", async () => {
await testEditor({
contentBefore:
"" +
"" +
"| a[b | " +
"cd | " +
"ef | " +
"
" +
"" +
"| ab | " +
"c]d | " +
"ef | " +
"
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"
",
stepFunction: bold,
contentAfterEdit:
'' +
"" +
'| [ab | ' +
'cd | ' +
"ef | " +
"
" +
"" +
'| ab | ' +
'cd] | ' +
"ef | " +
"
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"
",
});
});
test("should apply bold to a whole table", async () => {
await testEditor({
contentBefore:
"" +
"" +
"| a[b | " +
"cd | " +
"ef | " +
"
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"" +
"| ab | " +
"cd | " +
"e]f | " +
"
" +
"
",
stepFunction: bold,
contentAfterEdit:
'' +
"" +
'| [ab | ' +
'cd | ' +
'ef | ' +
"
" +
"" +
'| ab | ' +
'cd | ' +
'ef | ' +
"
" +
"" +
'| ab | ' +
'cd | ' +
'ef] | ' +
"
" +
"
",
});
});
});
describe("reset size", () => {
test("should remove any height or width of the table and bring it back to it original form", async () => {
await testEditor({
contentBefore: ``,
stepFunction: resetSize,
contentAfter: ``,
});
});
test("should remove any height or width of the table without loosing the style of the element inside it.", async () => {
await testEditor({
contentBefore: `
[]TESTTEXT |
|
|
|
TESTTEXT
|
|
codeTEST |
|
- text
- text
- text
|
`,
stepFunction: resetSize,
contentAfter: `
[]TESTTEXT |
|
|
|
TESTTEXT
|
|
codeTEST |
|
- text
- text
- text
|
`,
});
});
test("should remove any height or width of the table without removig the style of the table.", async () => {
await testEditor({
contentBefore: ``,
stepFunction: resetSize,
contentAfter: ``,
});
});
});
describe("color", () => {
test("should apply a color to two columns", async () => {
await testEditor({
contentBefore:
"" +
"| a[b | " +
"c]d | " +
"ef | " +
"
",
stepFunction: setColor("aquamarine", "color"),
contentAfterEdit: unformat(`
`),
});
});
test("should apply a color to a whole row", async () => {
await testEditor({
contentBefore:
"" +
"| a[b | " +
"cd | " +
"e]f | " +
"
| ab | cd | ef |
",
stepFunction: setColor("aquamarine", "color"),
contentAfterEdit: unformat(`
`),
});
});
test("should apply a color to a whole column", async () => {
await testEditor({
contentBefore:
"" +
"" +
"| a[b | " +
"cd | " +
"ef | " +
"
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"" +
"| a]b | " +
"cd | " +
"ef | " +
"
" +
"
",
stepFunction: setColor("aquamarine", "color"),
contentAfterEdit: unformat(`
|
a[b
|
cd |
ef |
|
ab
|
cd |
ef |
|
a]b
|
cd |
ef |
`),
});
});
test("should apply a color from (0,0) to (1,1) in a 3x3 table", async () => {
await testEditor({
contentBefore:
"" +
"" +
"| a[b | " +
"cd | " +
"ef | " +
"
" +
"" +
"| ab | " +
"c]d | " +
"ef | " +
"
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"
",
stepFunction: setColor("aquamarine", "color"),
contentAfterEdit: unformat(`
|
a[b
|
cd
|
ef |
|
ab
|
c]d
|
ef |
| ab |
cd |
ef |
`),
});
});
test("should apply a color to a whole table", async () => {
await testEditor({
contentBefore:
"" +
"" +
"| a[b | " +
"cd | " +
"ef | " +
"
" +
"" +
"| ab | " +
"cd | " +
"ef | " +
"
" +
"" +
"| ab | " +
"cd | " +
"e]f | " +
"
" +
"
",
stepFunction: setColor("aquamarine", "color"),
contentAfterEdit: unformat(`
|
a[b
|
cd
|
ef
|
|
ab
|
cd
|
ef
|
|
ab
|
cd
|
e]f
|
`),
});
});
});
});