Odoo18-Base/addons/html_editor/static/tests/keyboard/arrow.test.js
2025-01-06 10:57:38 +07:00

699 lines
30 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, test } from "@odoo/hoot";
import { setupEditor, testEditor } from "../_helpers/editor";
import { tick } from "@odoo/hoot-mock";
import { press } from "@odoo/hoot-dom";
import { simulateArrowKeyPress } from "../_helpers/user_actions";
import { getContent, setSelection } from "../_helpers/selection";
import { unformat } from "../_helpers/format";
const keyPress = (keys) => async (editor) => {
await simulateArrowKeyPress(editor, keys);
// Allow onselectionchange handler to run.
await tick();
};
describe("Around ZWS", () => {
test("should move past a zws (collapsed - ArrowRight)", async () => {
await testEditor({
contentBefore: '<p>ab[]<span class="a">\u200B</span>cd</p>',
stepFunction: keyPress("ArrowRight"),
contentAfter: '<p>ab<span class="a">\u200B</span>c[]d</p>',
});
await testEditor({
contentBefore: '<p>ab<span class="a">[]\u200B</span>cd</p>',
stepFunction: keyPress("ArrowRight"),
contentAfter: '<p>ab<span class="a">\u200B</span>c[]d</p>',
});
});
test.tags("focus required");
test("should move past a zws (collapsed - ArrowLeft)", async () => {
await testEditor({
contentBefore: '<p>ab<span class="a">\u200B[]</span>cd</p>',
stepFunction: keyPress("ArrowLeft"),
contentAfter: '<p>a[]b<span class="a">\u200B</span>cd</p>',
});
await testEditor({
contentBefore: '<p>ab<span class="a">\u200B</span>[]cd</p>',
stepFunction: keyPress("ArrowLeft"),
contentAfter: '<p>a[]b<span class="a">\u200B</span>cd</p>',
});
await testEditor({
contentBefore:
'<p><span class="a">\u200B</span></p><p><span class="b">[]\u200B</span>ab</p>',
stepFunction: keyPress("ArrowLeft"),
contentAfter:
'<p><span class="a">\u200B[]</span></p><p><span class="b">\u200B</span>ab</p>',
});
await testEditor({
contentBefore:
'<p><span class="a">\u200B</span></p><p><span class="b">\u200B[]</span></p>',
stepFunction: keyPress("ArrowLeft"),
contentAfter:
'<p><span class="a">\u200B[]</span></p><p><span class="b">\u200B</span></p>',
});
});
test("should move past a zws (collapsed at the end of a block)", async () => {
await testEditor({
contentBefore: '<p>ab[]<span class="a">\u200B</span></p><p>cd</p>',
stepFunction: keyPress("ArrowRight"),
contentAfter: '<p>ab<span class="a">\u200B</span></p><p>[]cd</p>',
});
await testEditor({
contentBefore: '<p>ab<span class="a">[]\u200B</span></p><p>cd</p>',
stepFunction: keyPress("ArrowRight"),
contentAfter: '<p>ab<span class="a">\u200B</span></p><p>[]cd</p>',
});
await testEditor({
contentBefore:
'<p>ab<span class="a">\u200B[]</span></p><p><span class="b">\u200B</span></p>',
stepFunction: keyPress("ArrowRight"),
contentAfter:
'<p>ab<span class="a">\u200B</span></p><p><span class="b">[]\u200B</span></p>',
});
await testEditor({
contentBefore:
'<p>ab<span class="a">[]\u200B</span></p><p><span class="b">\u200B</span></p>',
stepFunction: keyPress("ArrowRight"),
contentAfter:
'<p>ab<span class="a">\u200B</span></p><p><span class="b">[]\u200B</span></p>',
});
});
test("should select a zws", async () => {
await testEditor({
contentBefore: '<p>[ab]<span class="a">\u200B</span>cd</p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter: '<p>[ab<span class="a">\u200B</span>c]d</p>',
});
await testEditor({
contentBefore: '<p>[ab<span class="a">]\u200B</span>cd</p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter: '<p>[ab<span class="a">\u200B</span>c]d</p>',
});
});
test("should select a zws (2)", async () => {
await testEditor({
contentBefore: '<p>a[b]<span class="a">\u200B</span>cd</p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter: '<p>a[b<span class="a">\u200B</span>c]d</p>',
});
await testEditor({
contentBefore: '<p>a[b<span class="a">]\u200B</span>cd</p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter: '<p>a[b<span class="a">\u200B</span>c]d</p>',
});
await testEditor({
contentBefore:
'<p>a[b]<span class="a">\u200B</span></p><p><span class="b">\u200B</span></p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter:
'<p>a[b<span class="a">\u200B</span></p><p>]<span class="b">\u200B</span></p>',
});
});
test("should select a zws (3)", async () => {
await testEditor({
contentBefore: '<p>ab[]<span class="a">\u200B</span>cd</p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter: '<p>ab<span class="a">[\u200B</span>c]d</p>',
});
await testEditor({
contentBefore: '<p>ab<span class="a">[]\u200B</span>cd</p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter: '<p>ab<span class="a">[\u200B</span>c]d</p>',
});
});
test("should select a zws backwards (ArrowLeft)", async () => {
await testEditor({
contentBefore: '<p>ab<span class="a">\u200B[]</span>cd</p>',
stepFunction: keyPress(["Shift", "ArrowLeft"]),
contentAfter: '<p>a]b<span class="a">\u200B[</span>cd</p>',
});
await testEditor({
contentBefore: '<p>ab<span class="a">\u200B</span>[]cd</p>',
stepFunction: keyPress(["Shift", "ArrowLeft"]),
contentAfter: '<p>a]b<span class="a">\u200B[</span>cd</p>',
});
});
test("should select a zws backwards (ArrowLeft - 2)", async () => {
await testEditor({
contentBefore: '<p>ab<span class="a">\u200B</span>]cd[</p>',
stepFunction: keyPress(["Shift", "ArrowLeft"]),
contentAfter: '<p>a]b<span class="a">\u200B</span>cd[</p>',
});
await testEditor({
contentBefore: '<p>ab<span class="a">\u200B]</span>cd[</p>',
stepFunction: keyPress(["Shift", "ArrowLeft"]),
contentAfter: '<p>a]b<span class="a">\u200B</span>cd[</p>',
});
});
test("should select a zws backwards (ArrowLeft - 3)", async () => {
await testEditor({
contentBefore: '<p>ab<span class="a">\u200B</span>]c[d</p>',
stepFunction: keyPress(["Shift", "ArrowLeft"]),
contentAfter: '<p>a]b<span class="a">\u200B</span>c[d</p>',
});
await testEditor({
contentBefore: '<p>ab<span class="a">\u200B]</span>c[d</p>',
stepFunction: keyPress(["Shift", "ArrowLeft"]),
contentAfter: '<p>a]b<span class="a">\u200B</span>c[d</p>',
});
});
test("should select a zws backwards (ArrowRight)", async () => {
await testEditor({
contentBefore: '<p>ab<span class="a">]\u200B[</span>cd</p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter: '<p>ab<span class="a">\u200B</span>[c]d</p>',
});
await testEditor({
contentBefore: '<p>ab<span class="a">]\u200B</span>[cd</p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter: '<p>ab<span class="a">\u200B</span>[c]d</p>',
});
await testEditor({
contentBefore: '<p>ab]<span class="a">\u200B</span>[cd</p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter: '<p>ab<span class="a">\u200B</span>[c]d</p>',
});
await testEditor({
contentBefore: '<p>ab]<span class="a">\u200B[</span>cd</p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter: '<p>ab<span class="a">\u200B</span>[c]d</p>',
});
});
test("should select a zws backwards (ArrowRight - 2)", async () => {
await testEditor({
contentBefore: '<p>ab<span class="a">]\u200B</span>c[d</p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter: '<p>ab<span class="a">\u200B</span>c[]d</p>',
});
await testEditor({
contentBefore: '<p>ab]<span class="a">\u200B</span>c[d</p>',
stepFunction: keyPress(["Shift", "ArrowRight"]),
contentAfter: '<p>ab<span class="a">\u200B</span>c[]d</p>',
});
});
test("should deselect a zws", async () => {
await testEditor({
contentBefore: '<p>ab<span class="a">[\u200B]</span>cd</p>',
stepFunction: keyPress(["Shift", "ArrowLeft"]),
contentAfter: '<p>a]b[<span class="a">\u200B</span>cd</p>', // Normalized by the browser
});
await testEditor({
contentBefore: '<p>ab<span class="a">[\u200B</span>]cd</p>',
stepFunction: keyPress(["Shift", "ArrowLeft"]),
contentAfter: '<p>a]b[<span class="a">\u200B</span>cd</p>', // Normalized by the browser
});
await testEditor({
contentBefore: '<p>ab[<span class="a">\u200B]</span>cd</p>',
stepFunction: keyPress(["Shift", "ArrowLeft"]),
contentAfter: '<p>a]b[<span class="a">\u200B</span>cd</p>', // Normalized by the browser
});
await testEditor({
contentBefore: '<p>ab[<span class="a">\u200B</span>]cd</p>',
stepFunction: keyPress(["Shift", "ArrowLeft"]),
contentAfter: '<p>a]b[<span class="a">\u200B</span>cd</p>', // Normalized by the browser
});
});
test("should deselect a zws (2)", async () => {
await testEditor({
contentBefore: '<p>a[b<span class="a">\u200B]</span>cd</p>',
stepFunction: keyPress(["Shift", "ArrowLeft"]),
contentAfter: '<p>a[]b<span class="a">\u200B</span>cd</p>',
});
await testEditor({
contentBefore: '<p>a[b<span class="a">\u200B</span>]cd</p>',
stepFunction: keyPress(["Shift", "ArrowLeft"]),
contentAfter: '<p>a[]b<span class="a">\u200B</span>cd</p>',
});
});
});
describe("Around links", () => {
test("should move into a link (ArrowRight)", async () => {
await testEditor({
contentBefore: '<p>ab[]<a href="#">cd</a>ef</p>',
contentBeforeEdit:
"<p>ab[]" +
"\ufeff" + // before zwnbsp
'<a href="#">' +
"\ufeff" + // start zwnbsp
"cd" + // content
"\ufeff" + // end zwnbsp
"</a>" +
"\ufeff" + // after zwnbsp
"ef</p>",
stepFunction: keyPress("ArrowRight"),
contentAfterEdit:
"<p>ab" +
"\ufeff" + // before zwnbsp
'<a href="#" class="o_link_in_selection">' +
"\ufeff" + // start zwnbsp
"[]cd" + // content
"\ufeff" + // end zwnbsp
"</a>" +
"\ufeff" + // after zwnbsp
"ef</p>",
contentAfter: '<p>ab<a href="#">[]cd</a>ef</p>',
});
});
test("should move into a link (ArrowLeft)", async () => {
await testEditor({
contentBefore: '<p>ab<a href="#">cd</a>[]ef</p>',
contentBeforeEdit:
"<p>ab" +
"\ufeff" + // before zwnbsp
'<a href="#">' +
"\ufeff" + // start zwnbsp
"cd" + // content
"\ufeff" + // end zwnbsp
"</a>" +
"\ufeff" + // after zwnbsp
"[]ef</p>",
stepFunction: keyPress("ArrowLeft"),
contentAfterEdit:
"<p>ab" +
"\ufeff" + // before zwnbsp
'<a href="#" class="o_link_in_selection">' +
"\ufeff" + // start zwnbsp
"cd[]" + // content
"\ufeff" + // end zwnbsp
"</a>" +
"\ufeff" + // after zwnbsp
"ef</p>",
contentAfter: '<p>ab<a href="#">cd[]</a>ef</p>',
});
});
test("should move out of a link (ArrowRight)", async () => {
await testEditor({
contentBefore: '<p>ab<a href="#">cd[]</a>ef</p>',
contentBeforeEdit:
"<p>ab" +
"\ufeff" + // before zwnbsp
'<a href="#" class="o_link_in_selection">' +
"\ufeff" + // start zwnbsp
"cd[]" + // content
"\ufeff" + // end zwnbsp
"</a>" +
"\ufeff" + // after zwnbsp
"ef</p>",
stepFunction: keyPress("ArrowRight"),
contentAfterEdit:
"<p>ab" +
"\ufeff" + // before zwnbsp
'<a href="#">' +
"\ufeff" + // start zwnbsp
"cd" + // content
"\ufeff" + // end zwnbsp
"</a>" +
"\ufeff" + // after zwnbsp
"[]ef</p>",
contentAfter: '<p>ab<a href="#">cd</a>[]ef</p>',
});
});
test("should move out of a link (ArrowLeft)", async () => {
await testEditor({
contentBefore: '<p>ab<a href="#">[]cd</a>ef</p>',
contentBeforeEdit:
"<p>ab" +
"\ufeff" + // before zwnbsp
'<a href="#" class="o_link_in_selection">' +
"\ufeff" + // start zwnbsp
"[]cd" + // content
"\ufeff" + // end zwnbsp
"</a>" +
"\ufeff" + // after zwnbsp
"ef</p>",
stepFunction: keyPress("ArrowLeft"),
contentAfterEdit:
"<p>ab[]" +
"\ufeff" + // before zwnbsp
'<a href="#">' +
"\ufeff" + // start zwnbsp
"cd" + // content
"\ufeff" + // end zwnbsp
"</a>" +
"\ufeff" + // after zwnbsp
"ef</p>",
contentAfter: '<p>ab[]<a href="#">cd</a>ef</p>',
});
});
});
describe("Around icons", () => {
test("should move past the icon (ArrowRight)", async () => {
await testEditor({
contentBefore: `<p>abc[]<span class="fa fa-music"></span>def</p>`,
contentBeforeEdit: `<p>abc[]<span class="fa fa-music" contenteditable="false">\u200b</span>def</p>`,
stepFunction: keyPress("ArrowRight"),
contentAfterEdit: `<p>abc<span class="fa fa-music" contenteditable="false">\u200b</span>[]def</p>`,
contentAfter: `<p>abc<span class="fa fa-music"></span>[]def</p>`,
});
});
test("should move past the icon (ArrowLeft)", async () => {
await testEditor({
contentBefore: `<p>abc<span class="fa fa-music"></span>[]def</p>`,
contentBeforeEdit: `<p>abc<span class="fa fa-music" contenteditable="false">\u200b</span>[]def</p>`,
stepFunction: keyPress("ArrowLeft"),
contentAfterEdit: `<p>abc[]<span class="fa fa-music" contenteditable="false">\u200b</span>def</p>`,
contentAfter: `<p>abc[]<span class="fa fa-music"></span>def</p>`,
});
});
});
describe("Selection correction when it lands at the editable root", () => {
test("should place cursor in the table below", async () => {
await testEditor({
contentBefore:
"<table><tbody><tr><td><p>a</p><p>b[]</p></td></tr></tbody></table>" +
"<table><tbody><tr><td><p>c</p><p>d</p></td></tr></tbody></table>",
stepFunction: keyPress("ArrowRight"),
contentAfter:
"<table><tbody><tr><td><p>a</p><p>b</p></td></tr></tbody></table>" +
"<table><tbody><tr><td><p>[]c</p><p>d</p></td></tr></tbody></table>",
});
});
test.tags("focus required");
test("should place cursor in the table above", async () => {
await testEditor({
contentBefore:
"<table><tbody><tr><td><p>a</p><p>b</p></td></tr></tbody></table>" +
"<table><tbody><tr><td><p>[]c</p><p>d</p></td></tr></tbody></table>",
stepFunction: keyPress("ArrowLeft"),
contentAfter:
"<table><tbody><tr><td><p>a</p><p>b[]</p></td></tr></tbody></table>" +
"<table><tbody><tr><td><p>c</p><p>d</p></td></tr></tbody></table>",
});
});
test("should place cursor in the paragraph below", async () => {
await testEditor({
contentBefore:
"<table><tbody><tr><td><p>a</p><p>b[]</p></td></tr></tbody></table>" +
"<p><br></p>",
stepFunction: keyPress("ArrowRight"),
contentAfter:
"<table><tbody><tr><td><p>a</p><p>b</p></td></tr></tbody></table>" +
"<p>[]<br></p>",
});
});
test("should place cursor in the paragraph above", async () => {
await testEditor({
contentBefore:
"<p><br></p>" +
"<table><tbody><tr><td><p>[]a</p><p>b</p></td></tr></tbody></table>",
stepFunction: keyPress("ArrowLeft"),
contentAfter:
"<p>[]<br></p>" +
"<table><tbody><tr><td><p>a</p><p>b</p></td></tr></tbody></table>",
});
});
test("should keep cursor at the same position (avoid reaching the editable root) (1)", async () => {
await testEditor({
contentBefore: "<table><tbody><tr><td><p>a</p><p>b[]</p></td></tr></tbody></table>",
stepFunction: keyPress("ArrowRight"),
contentAfter: "<table><tbody><tr><td><p>a</p><p>b[]</p></td></tr></tbody></table>",
});
});
test("should keep cursor at the same position (avoid reaching the editable root) (2)", async () => {
await testEditor({
contentBefore: "<table><tbody><tr><td><p>[]a</p><p>b</p></td></tr></tbody></table>",
stepFunction: keyPress("ArrowLeft"),
contentAfter: "<table><tbody><tr><td><p>[]a</p><p>b</p></td></tr></tbody></table>",
});
});
test("should place cursor after the second separator", async () => {
await testEditor({
contentBefore:
'<p>[]<br></p><hr contenteditable="false">' +
'<hr contenteditable="false"><p><br></p>',
stepFunction: keyPress("ArrowRight"),
contentAfter: "<p><br></p><hr>" + "<hr><p>[]<br></p>",
});
});
test.tags("focus required");
test("should place cursor before the first separator", async () => {
await testEditor({
contentBefore:
'<p><br></p><hr contenteditable="false">' +
'<hr contenteditable="false"><p>[]<br></p>',
stepFunction: keyPress("ArrowLeft"),
contentAfter: "<p>[]<br></p><hr>" + "<hr><p><br></p>",
});
});
});
describe.tags("focus required");
describe("Around invisible chars in RTL languages", () => {
describe("ZWS", () => {
const content = "<p>" + "الرجال" + '<span class="a">\u200B</span>' + "هؤلاء" + "</p>";
// Displayed as " هؤلاء<span class="a">\u200B</span>الرجال" in the editor:
// third + span + first
test("should move past the zws (ArrowLeft)", async () => {
const { editor, el } = await setupEditor(content, { config: { direction: "rtl" } });
const pFirstChild = el.firstChild.firstChild; // "الرجال"
const pThirdChild = el.firstChild.childNodes[2]; // "هؤلاء"
// Place cursor at the end of first child (next to the span)
// Displayed as هؤلاء<span class="a">\u200B</span>[]الرجال
setSelection({ anchorNode: pFirstChild, anchorOffset: pFirstChild.length });
await keyPress("ArrowLeft")(editor);
const selection = editor.document.getSelection();
expect(selection.anchorNode).toBe(pThirdChild);
expect(selection.anchorOffset).toBe(1);
// Displayed as ه[]ؤلاء<span class="a">\u200B</span>الرجال
expect(getContent(el)).toBe('<p>الرجال<span class="a">\u200B</span>ه[]ؤلاء</p>');
});
test("should move past the zws (ArrowRight)", async () => {
const { editor, el } = await setupEditor(content, { config: { direction: "rtl" } });
const pFirstChild = el.firstChild.firstChild; // "الرجال"
const pThirdChild = el.firstChild.childNodes[2]; // "هؤلاء"
// Place cursor at the beginning of third child (next to the span)
// Displayed as هؤلاء[]<span class="a">\u200B</span>الرجال
setSelection({ anchorNode: pThirdChild, anchorOffset: 0 });
await keyPress("ArrowRight")(editor);
const selection = editor.document.getSelection();
expect(selection.anchorNode).toBe(pFirstChild);
expect(selection.anchorOffset).toBe(pFirstChild.length - 1);
// Displayed as هؤلاء<span class="a">\u200B</span>الرجا[]ل
expect(getContent(el)).toBe('<p>الرجا[]ل<span class="a">\u200B</span>هؤلاء</p>');
});
});
describe("ZWNBSP", () => {
const content = "<p>" + "الرجال" + '<a href="#">اءيتجنب</a>' + "هؤلاء" + "</p>";
// Displayed as "هؤلاء<a href="#">اءيتجنب</a>الرجال" in the editor:
// third + link + first
test("should move into a link (ArrowLeft)", async () => {
const { editor, el } = await setupEditor(content, { config: { direction: "rtl" } });
const pFirstChild = el.firstChild.firstChild; // "الرجال"
// childNodes[1] and childNodes[3] are the ZWNBSP text nodes
const link = el.firstChild.childNodes[2];
// Place cursor at the end of first child (before the FEFF char)
// Displayed as هؤلاء\uFEFF<a href="#">\uFEFFاءيتجنب\uFEFF</a>\uFEFF[]الرجال
setSelection({ anchorNode: pFirstChild, anchorOffset: pFirstChild.length });
await keyPress("ArrowLeft")(editor);
const selection = editor.document.getSelection();
expect(selection.anchorNode).toBe(link.firstChild); // FEFF node
expect(selection.anchorOffset).toBe(1);
// Displayed as هؤلاء\uFEFF<a href="#">\uFEFFاءيتجنب[]\uFEFF</a>\uFEFFالرجال
expect(getContent(el)).toBe(
'<p>الرجال\uFEFF<a href="#" class="o_link_in_selection">\uFEFF[]اءيتجنب\uFEFF</a>\uFEFFهؤلاء</p>'
);
});
test("should move into a link (ArrowRight)", async () => {
const { editor, el } = await setupEditor(content, { config: { direction: "rtl" } });
// childNodes[1] and childNodes[3] are the ZWNBSP text nodes
const link = el.firstChild.childNodes[2];
const pFifthChild = el.firstChild.childNodes[4]; // "هؤلاء"
const link2ndChild = link.childNodes[1]; // اءيتجنب
// Place cursor at the beginning of fifth child (after the FEFF char)
// Displayed as هؤلاء[]\uFEFF<a href="#">\uFEFFاءيتجنب\uFEFF</a>\uFEFFالرجال
setSelection({ anchorNode: pFifthChild, anchorOffset: 0 });
await keyPress("ArrowRight")(editor);
const selection = editor.document.getSelection();
expect(selection.anchorNode).toBe(link2ndChild);
expect(selection.anchorOffset).toBe(link2ndChild.length);
// Displayed as هؤلاء\uFEFF<a href="#">\uFEFF[]اءيتجنب\uFEFF</a>\uFEFFالرجال
expect(getContent(el)).toBe(
'<p>الرجال\uFEFF<a href="#" class="o_link_in_selection">\uFEFFاءيتجنب[]\uFEFF</a>\uFEFFهؤلاء</p>'
);
});
test("should move out of a link (ArrowLeft)", async () => {
const { editor, el } = await setupEditor(content, { config: { direction: "rtl" } });
// childNodes[1] and childNodes[3] are the ZWNBSP text nodes
const link = el.firstChild.childNodes[2];
const link2ndChild = link.childNodes[1]; // text content inside link: اءيتجنب
// Place cursor at the end of link's content (before the FEFF char)
// Displayed as هؤلاء\uFEFF<a href="#">\uFEFF[]اءيتجنب\uFEFF</a>\uFEFFالرجال
setSelection({ anchorNode: link2ndChild, anchorOffset: link2ndChild.length });
await keyPress("ArrowLeft")(editor);
const selection = editor.document.getSelection();
expect(selection.anchorNode).toBe(el.firstChild.childNodes[3]); // FEFF node outside link
expect(selection.anchorOffset).toBe(1);
// Displayed as هؤلاء[]\uFEFF<a href="#">\uFEFFاءيتجنب\uFEFF</a>\uFEFFالرجال
expect(getContent(el)).toBe(
'<p>الرجال\uFEFF<a href="#">\uFEFFاءيتجنب\uFEFF</a>\uFEFF[]هؤلاء</p>'
);
});
test("should move out of a link (ArrowRight)", async () => {
const { editor, el } = await setupEditor(content, { config: { direction: "rtl" } });
// childNodes[1] and childNodes[3] are the ZWNBSP text nodes
const pFirstChild = el.firstChild.firstChild; // "الرجال"
const link = el.firstChild.childNodes[2];
const link2ndChild = link.childNodes[1]; // text content inside link: اءيتجنب
// Place cursor at the beginning of link's content (after the FEFF char)
// Displayed as هؤلاء\uFEFF<a href="#">\uFEFFاءيتجنب[]\uFEFF</a>\uFEFFالرجال
setSelection({ anchorNode: link2ndChild, anchorOffset: 0 });
await keyPress("ArrowRight")(editor);
const selection = editor.document.getSelection();
expect(selection.anchorNode).toBe(pFirstChild);
expect(selection.anchorOffset).toBe(pFirstChild.length);
// Displayed as هؤلاء\uFEFF<a href="#">\uFEFFاءيتجنب\uFEFF</a>\uFEFF[]الرجال
expect(getContent(el)).toBe(
'<p>الرجال[]\uFEFF<a href="#">\uFEFFاءيتجنب\uFEFF</a>\uFEFFهؤلاء</p>'
);
});
});
});
describe("Around contenteditable false elements containing contenteditable true elements", () => {
test("should select contenteditable false element (ArrowRight)", async () => {
await testEditor({
contentBefore: unformat(`
<p>abc</p>
<p>de[f]</p>
<div contenteditable="false">
<div contenteditable="true">
<p>ghi</p>
<p>jkl</p>
</div>
</div>
<p>mno</p>
`),
stepFunction: () => press(["shift", "arrowright"]),
contentAfterEdit: unformat(`
<p>abc</p>
<p>de[f</p>
<div contenteditable="false">
<div contenteditable="true">
<p>ghi</p>
<p>jkl</p>
</div>
</div>
<p>]mno</p>
`),
});
});
test("should select contenteditable false element (ArrowLeft)", async () => {
await testEditor({
contentBefore: unformat(`
<p>abc</p>
<p>def</p>
<div contenteditable="false">
<div contenteditable="true">
<p>ghi</p>
<p>jkl</p>
</div>
</div>
<p>]m[no</p>
`),
stepFunction: () => press(["shift", "arrowleft"]),
contentAfter: unformat(`
<p>abc</p>
<p>def]</p>
<div contenteditable="false">
<div contenteditable="true">
<p>ghi</p>
<p>jkl</p>
</div>
</div>
<p>m[no</p>
`),
});
});
test("should select contenteditable false element (ArrowUp)", async () => {
await testEditor({
contentBefore: unformat(`
<p>abc</p>
<p>def</p>
<div contenteditable="false">
<div contenteditable="true">
<p>ghi</p>
<p>jkl</p>
</div>
</div>
<p>]mno[</p>
`),
stepFunction: () => press(["shift", "arrowup"]),
contentAfter: unformat(`
<p>abc</p>
<p>def]</p>
<div contenteditable="false">
<div contenteditable="true">
<p>ghi</p>
<p>jkl</p>
</div>
</div>
<p>mno[</p>
`),
});
});
test("should select contenteditable false element (ArrowDown)", async () => {
await testEditor({
contentBefore: unformat(`
<p>abc</p>
<p>[def]</p>
<div contenteditable="false">
<div contenteditable="true">
<p>ghi</p>
<p>jkl</p>
</div>
</div>
<p>mno</p>
`),
stepFunction: () => press(["shift", "arrowdown"]),
contentAfter: unformat(`
<p>abc</p>
<p>[def</p>
<div contenteditable="false">
<div contenteditable="true">
<p>ghi</p>
<p>jkl</p>
</div>
</div>
<p>]mno</p>
`),
});
});
});