[ADD] web_editor: add some tests for state preservation utils
This adds a few tests for `getState`, `restoreState` and `enforceWhitespace`, regarding the handling of space visibility. Each test handles equivalent cases for all three functions. X-original-commit: 2f28f42640a7322eec35a4d48a1ec7ce287b77b3 Part-of: odoo/odoo#138549
This commit is contained in:
@@ -2409,6 +2409,8 @@ const allRestoreStateRules = (function () {
|
||||
* @param {Object} prevStateData @see getState
|
||||
* @param {boolean} debug=false - if true, adds nicely formatted
|
||||
* console logs to help with debugging.
|
||||
* @returns {Object|undefined} the rule that was applied to restore the state,
|
||||
* if any, for testing purposes.
|
||||
*/
|
||||
export function restoreState(prevStateData, debug=false) {
|
||||
const { node, direction, cType: cType1, oldEditableHTML } = prevStateData;
|
||||
@@ -2449,6 +2451,7 @@ export function restoreState(prevStateData, debug=false) {
|
||||
const inverseDirection = direction === DIRECTIONS.LEFT ? DIRECTIONS.RIGHT : DIRECTIONS.LEFT;
|
||||
enforceWhitespace(el, offset, inverseDirection, rule);
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
/**
|
||||
* Enforces the whitespace and BR visibility in the given direction starting
|
||||
|
||||
@@ -35,6 +35,10 @@ import {
|
||||
DIRECTIONS,
|
||||
isBlock,
|
||||
isVisibleTextNode,
|
||||
CTYPES,
|
||||
getState,
|
||||
restoreState,
|
||||
enforceWhitespace,
|
||||
} from '../../src/utils/utils.js';
|
||||
import {
|
||||
BasicEditor,
|
||||
@@ -1692,4 +1696,276 @@ describe('Utils', () => {
|
||||
window.chai.expect(res).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Prepare / Save / Restore state utilities
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
describe('State preservation utilities', () => {
|
||||
describe('getState', () => {
|
||||
it('should recognize invisible space to the right', () => {
|
||||
// We'll be looking to the right while standing at `a[] `.
|
||||
const [p] = insertTestHtml('<p>a </p>');
|
||||
splitTextNode(p.firstChild, 1); // "a"" "
|
||||
window.chai.expect(p.childNodes.length).to.eql(2);
|
||||
const position = [p, 1]; // `<p>"a"[]" "</p>`
|
||||
window.chai.expect(getState(...position, DIRECTIONS.RIGHT)).to.eql({
|
||||
// We look to the right of "a" (`a[] `):
|
||||
node: p.firstChild, // "a"
|
||||
direction: DIRECTIONS.RIGHT,
|
||||
// The browser strips the space away so we ignore it and see
|
||||
// `</p>`: the closing tag from the inside.
|
||||
cType: CTYPES.BLOCK_INSIDE,
|
||||
});
|
||||
});
|
||||
it('should recognize invisible space to the right (among consecutive space within content)', () => {
|
||||
// We'll be looking to the right while standing at `a [] `. The
|
||||
// first space is visible, the rest isn't.
|
||||
const [p] = insertTestHtml('<p>a b</p>');
|
||||
splitTextNode(p.firstChild, 2); // "a "" b"
|
||||
window.chai.expect(p.childNodes.length).to.eql(2);
|
||||
const position = [p, 1]; // `<p>"a "[]" b"</p>`
|
||||
window.chai.expect(getState(...position, DIRECTIONS.RIGHT)).to.eql({
|
||||
// We look to the right of "a " (`a []`):
|
||||
node: p.firstChild, // "a "
|
||||
direction: DIRECTIONS.RIGHT,
|
||||
// The browser strips the space away so we ignore it and see
|
||||
// "b": visible content.
|
||||
cType: CTYPES.CONTENT,
|
||||
});
|
||||
});
|
||||
it('should recognize visible space to the left (followed by consecutive space within content)', () => {
|
||||
// We'll be looking to the left while standing at `[] b`. The
|
||||
// first space is visible, the rest isn't.
|
||||
const [p] = insertTestHtml('<p>a b</p>');
|
||||
splitTextNode(p.firstChild, 2); // "a "" b"
|
||||
window.chai.expect(p.childNodes.length).to.eql(2);
|
||||
const position = [p, 1]; // `<p>"a "[]" b"</p>`
|
||||
window.chai.expect(getState(...position, DIRECTIONS.LEFT)).to.eql({
|
||||
// We look to the left of " b" (`[] b`):
|
||||
node: p.lastChild, // "a"
|
||||
direction: DIRECTIONS.LEFT,
|
||||
// Left of " b" we see visible space that we should
|
||||
// preserve.
|
||||
cType: CTYPES.SPACE,
|
||||
});
|
||||
});
|
||||
it('should recognize invisible space to the left (nothing after)', () => {
|
||||
// We'll be looking to the left while standing at ` [] `.
|
||||
const [p] = insertTestHtml('<p> </p>');
|
||||
p.append(document.createTextNode('')); // " """
|
||||
window.chai.expect(getState(p, 1, DIRECTIONS.LEFT)).to.eql({
|
||||
// We look to the left of " " (` []`):
|
||||
node: p.lastChild, // ""
|
||||
direction: DIRECTIONS.LEFT,
|
||||
// The browser strips the space away so we ignore it and see
|
||||
// `<p>`: the opening tag from the inside.
|
||||
cType: CTYPES.BLOCK_INSIDE,
|
||||
});
|
||||
});
|
||||
it('should recognize invisible space to the left (more space after)', () => {
|
||||
// We'll be looking to the left while standing at ` [] `.
|
||||
const [p] = insertTestHtml('<p> </p>');
|
||||
splitTextNode(p.firstChild, 1); // " "" "
|
||||
window.chai.expect(getState(p, 1, DIRECTIONS.LEFT)).to.eql({
|
||||
// We look to the left of " " (` [] `):
|
||||
node: p.lastChild, // " ".
|
||||
direction: DIRECTIONS.LEFT,
|
||||
// The browser strips the space away so we ignore it and see
|
||||
// `<p>`: the opening tag from the inside.
|
||||
cType: CTYPES.BLOCK_INSIDE,
|
||||
});
|
||||
});
|
||||
it('should recognize invisible space to the left (br after)', () => {
|
||||
// We'll be looking to the left while standing at ` [] `.
|
||||
const [p] = insertTestHtml('<p> <br></p>');
|
||||
window.chai.expect(getState(p, 1, DIRECTIONS.LEFT)).to.eql({
|
||||
// We look to the left of the br element (` []<br>`):
|
||||
node: p.lastChild, // `<br>`.
|
||||
direction: DIRECTIONS.LEFT,
|
||||
// The browser strips the space away so we ignore it and see
|
||||
// `<p>`: the opening tag from the inside.
|
||||
cType: CTYPES.BLOCK_INSIDE,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('restoreState', () => {
|
||||
it('should restore invisible space to the left (looking right)', () => {
|
||||
// We'll be restoring the state of "a []" in `<p>a </p>`.
|
||||
const [p] = insertTestHtml('<p>a b</p>');
|
||||
splitTextNode(p.firstChild, 2); // "a ""b"
|
||||
const rule = restoreState({
|
||||
// We look to the right of "a " (`a []b`) to see if we need
|
||||
// to preserve the space at the end of "a ":
|
||||
node: p.firstChild, // "a "
|
||||
direction: DIRECTIONS.RIGHT,
|
||||
// The DOM used to be `<p>a </p>` so to the right of "a " we
|
||||
// used to see `</p>`: the closing tag from the inside.
|
||||
cType: CTYPES.BLOCK_INSIDE,
|
||||
});
|
||||
// Now looking to the right of "a " we see "b", which is content
|
||||
// and makes the formerly invisible space visible. We should get
|
||||
// back a rule that will enforce the invisibility of the space.
|
||||
window.chai.expect(rule.spaceVisibility).to.be.false;
|
||||
});
|
||||
it('should restore visible space to the left (looking right) (among consecutive space within content)', () => {
|
||||
// We'll be restoring the state of "a []" in `<p>a b</p>`.
|
||||
// The first space is visible, the rest isn't.
|
||||
const [p] = insertTestHtml('<p>a </p>');
|
||||
splitTextNode(p.firstChild, 2); // "a "" "
|
||||
const rule = restoreState({
|
||||
// We look to the right of "a " (`a []`) to see if we need
|
||||
// to preserve the space at the end of "a ":
|
||||
node: p.firstChild, // "a "
|
||||
direction: DIRECTIONS.RIGHT,
|
||||
// The DOM used to be `<p>a b</p>` so to the right of "a " we
|
||||
// used to see "b" which is visible content.
|
||||
cType: CTYPES.CONTENT,
|
||||
});
|
||||
// Now looking to the right of "a " we see `</p>`: the closing
|
||||
// tag, from the inside. This makes the formerly visible space
|
||||
// invisible. We should get back a rule that will enforce the
|
||||
// visibility of the space.
|
||||
window.chai.expect(rule.spaceVisibility).to.be.true;
|
||||
});
|
||||
it('should restore visible space to the right (looking left) (followed by consecutive space within content)', () => {
|
||||
// We'll be restoring the state of "[] b" in `<p>a b</p>`.
|
||||
// The first space is visible, the rest isn't.
|
||||
const [p] = insertTestHtml('<p>a </p>');
|
||||
splitTextNode(p.firstChild, 2); // "a "" "
|
||||
const rule = restoreState({
|
||||
// We look to the left of " " (`[] `) to see if we need
|
||||
// to preserve the space of " ":
|
||||
node: p.lastChild, // " "
|
||||
direction: DIRECTIONS.LEFT,
|
||||
// The DOM used to be `<p>a b</p>` so to the left of " b" we
|
||||
// used to see " " which is visible space.
|
||||
cType: CTYPES.SPACE,
|
||||
});
|
||||
// Now looking to the left of " " we see " " which is now
|
||||
// invisible. This means the space we're examining is also still
|
||||
// invisible. Since it should be invisible, we should get back a
|
||||
// rule that will enforce the invisibility of the space (but no
|
||||
// rule would work as well).
|
||||
window.chai.expect(rule.spaceVisibility).not.to.be.true;
|
||||
});
|
||||
it('should restore invisible space to the right (looking left) (nothing after)', () => {
|
||||
// We'll be restoring the state of " []" in `<p> </p>`.
|
||||
const [p] = insertTestHtml('<p>a </p>');
|
||||
splitTextNode(p.firstChild, 1); // "a"" "
|
||||
const rule = restoreState({
|
||||
// We look to the left of " " (`a[] `) to see if we need
|
||||
// to preserve the space of " ":
|
||||
node: p.lastChild, // " "
|
||||
direction: DIRECTIONS.LEFT,
|
||||
// The DOM used to be `<p> </p>` so to the left of " " we
|
||||
// used to see `<p>`: the opening tag from the inside.
|
||||
cType: CTYPES.BLOCK_INSIDE,
|
||||
});
|
||||
// Now looking to the left of " " we see "a", which is content
|
||||
// but since it's to the left of our space it has no incidence
|
||||
// on its visibility. Either way it should be invisible so we
|
||||
// should get back a rule that will enforce the invisibility of
|
||||
// the space (but no rule would work as well).
|
||||
window.chai.expect(rule.spaceVisibility).not.to.be.true;
|
||||
});
|
||||
it('should restore invisible space to the right (looking left) (more space after)', () => {
|
||||
// We'll be restoring the state of " [] " in `<p> </p>`.
|
||||
const [p] = insertTestHtml('<p>a </p>');
|
||||
splitTextNode(p.firstChild, 2); // "a "" "
|
||||
const rule = restoreState({
|
||||
// We look to the left of " " (`a [] `) to see if we need
|
||||
// to preserve the space of " ":
|
||||
node: p.lastChild, // " "
|
||||
direction: DIRECTIONS.LEFT,
|
||||
// The DOM used to be `<p> </p>` so to the left of " "
|
||||
// we used to see `<p>`: the opening tag from the inside.
|
||||
cType: CTYPES.BLOCK_INSIDE,
|
||||
});
|
||||
// Now looking to the left of " " we see "a", which is content
|
||||
// but since it's to the left of our space it has no incidence
|
||||
// on its visibility. Either way it should be invisible so we
|
||||
// should get back a rule that will enforce the invisibility of
|
||||
// the space (but no rule would work as well).
|
||||
window.chai.expect(rule.spaceVisibility).not.to.be.true;
|
||||
});
|
||||
it('should restore invisible space to the right (looking left) (br after)', () => {
|
||||
// We'll be restoring the state of " []<br>" in `<p> []<br></p>`.
|
||||
const [p] = insertTestHtml('<p>a <br></p>');
|
||||
const rule = restoreState({
|
||||
// We look to the left of `<br>` (`a []<br>`):
|
||||
node: p.lastChild, // `<br>`
|
||||
direction: DIRECTIONS.LEFT,
|
||||
// The DOM used to be `<p> <br></p>` so to the left of
|
||||
// `<br>` we used to see `<p>`: the opening tag from the
|
||||
// inside.
|
||||
cType: CTYPES.BLOCK_INSIDE,
|
||||
});
|
||||
// Now looking to the left of `<br>` we see "a", which is
|
||||
// content but since it's to the left of our space it has no
|
||||
// incidence on its visibility. Either way it should be
|
||||
// invisible so we should get back a rule that will enforce the
|
||||
// invisibility of the space (but no rule would work as well).
|
||||
window.chai.expect(rule.spaceVisibility).not.to.be.true;
|
||||
});
|
||||
});
|
||||
describe('enforceWhitespace', () => {
|
||||
it('should enforce invisible space to the left', () => {
|
||||
// We'll be making the space between "a" and "b" invisible.
|
||||
const [p] = insertTestHtml('<p>a b</p>');
|
||||
splitTextNode(p.firstChild, 2); // "a ""b"
|
||||
// We look to the left while standing at "a []":
|
||||
enforceWhitespace(p, 1, DIRECTIONS.LEFT, { spaceVisibility: false });
|
||||
window.chai.expect(p.innerHTML).to.eql('ab');
|
||||
});
|
||||
it('should restore visible space to the left (among consecutive space within content)', () => {
|
||||
// We'll be making the first space after "a" visible.
|
||||
const [p] = insertTestHtml('<p>a </p>');
|
||||
splitTextNode(p.firstChild, 2); // "a "" "
|
||||
// We look to the left while standing at "a []":
|
||||
enforceWhitespace(p, 1, DIRECTIONS.LEFT, { spaceVisibility: true });
|
||||
window.chai.expect(p.innerHTML).to.eql('a ');
|
||||
});
|
||||
it('should not enforce already invisible space to the right (followed by consecutive space within content)', () => {
|
||||
// We'll be keeping the last (invisible) space after "a" (we
|
||||
// could remove it but we don't need to - mostly we should not
|
||||
// make it visible).
|
||||
const [p] = insertTestHtml('<p>a </p>');
|
||||
splitTextNode(p.firstChild, 2); // "a "" "
|
||||
// We look to the left while standing at "a []":
|
||||
enforceWhitespace(p, 0, DIRECTIONS.RIGHT, { spaceVisibility: false });
|
||||
window.chai.expect(p.innerHTML).to.eql('a ');
|
||||
});
|
||||
it('should not enforce already invisible space to the right (nothing after)', () => {
|
||||
// We'll be keeping the invisible space after "a" (we could
|
||||
// remove it but we don't need to - mostly we should not make it
|
||||
// visible).
|
||||
const [p] = insertTestHtml('<p>a </p>');
|
||||
splitTextNode(p.firstChild, 1); // "a"" "
|
||||
// We look to the right while standing at "a[]":
|
||||
enforceWhitespace(p, 0, DIRECTIONS.RIGHT, { spaceVisibility: false });
|
||||
window.chai.expect(p.innerHTML).to.eql('a ');
|
||||
});
|
||||
it('should not enforce already invisible space to the left (more space after)', () => {
|
||||
// We'll be keeping the invisible space after "a" (we could
|
||||
// remove it but we don't need to - mostly we should not make it
|
||||
// visible).
|
||||
const [p] = insertTestHtml('<p>a </p>');
|
||||
splitTextNode(p.firstChild, 1); // "a"" "
|
||||
// We look to the right while standing at "a[]":
|
||||
enforceWhitespace(p, 0, DIRECTIONS.RIGHT, { spaceVisibility: false });
|
||||
window.chai.expect(p.innerHTML).to.eql('a ');
|
||||
});
|
||||
it('should not enforce already invisible space to the left (br after)', () => {
|
||||
// We'll be keeping the invisible space after "a" (we could
|
||||
// remove it but we don't need to - mostly we should not make it
|
||||
// visible).
|
||||
const [p] = insertTestHtml('<p>a <br></p>');
|
||||
splitTextNode(p.firstChild, 1); // "a"" "
|
||||
// We look to the right while standing at "a[]":
|
||||
enforceWhitespace(p, 0, DIRECTIONS.RIGHT, { spaceVisibility: false });
|
||||
window.chai.expect(p.innerHTML).to.eql('a <br>');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user