import { before, describe, it } from '@ephox/bedrock-client'; import CaretPosition from 'tinymce/core/caret/CaretPosition'; import { CaretWalker } from 'tinymce/core/caret/CaretWalker'; import * as CaretAsserts from '../../module/test/CaretAsserts'; import * as ViewBlock from '../../module/test/ViewBlock'; describe('browser.tinymce.core.CaretWalkerTest', () => { const viewBlock = ViewBlock.bddSetup(); const getRoot = viewBlock.get; const setupHtml = viewBlock.update; const getChildNode = (childIndex: number) => getRoot().childNodes[childIndex]; const findElm = (selector: string) => { return getRoot().querySelector(selector); }; const findElmPos = (selector: string, offset: number) => { return CaretPosition(getRoot().querySelector(selector) as Node, offset); }; const findTextPos = (selector: string, offset: number) => { return CaretPosition(getRoot().querySelector(selector)?.firstChild as Text, offset); }; let logicalCaret: CaretWalker; before(() => logicalCaret = CaretWalker(getRoot())); it('inside empty root', () => { setupHtml(''); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 0)), null); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 0)), null); }); it('on null', () => { setupHtml(''); CaretAsserts.assertCaretPosition(logicalCaret.next(null), null); CaretAsserts.assertCaretPosition(logicalCaret.prev(null), null); }); it('within text node in root', () => { setupHtml('abc'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot().firstChild as Text, 0)), CaretPosition(getRoot().firstChild as Text, 1)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot().firstChild as Text, 1)), CaretPosition(getRoot().firstChild as Text, 2)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot().firstChild as Text, 2)), CaretPosition(getRoot().firstChild as Text, 3)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot().firstChild as Text, 3)), null); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot().firstChild as Text, 3)), CaretPosition(getRoot().firstChild as Text, 2)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot().firstChild as Text, 2)), CaretPosition(getRoot().firstChild as Text, 1)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot().firstChild as Text, 1)), CaretPosition(getRoot().firstChild as Text, 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot().firstChild as Text, 0)), null); }); it('within text node in element', () => { setupHtml('

abc

'); CaretAsserts.assertCaretPosition(logicalCaret.next(findTextPos('p', 0)), findTextPos('p', 1)); CaretAsserts.assertCaretPosition(logicalCaret.next(findTextPos('p', 1)), findTextPos('p', 2)); CaretAsserts.assertCaretPosition(logicalCaret.next(findTextPos('p', 2)), findTextPos('p', 3)); CaretAsserts.assertCaretPosition(logicalCaret.next(findTextPos('p', 3)), null); CaretAsserts.assertCaretPosition(logicalCaret.prev(findTextPos('p', 3)), findTextPos('p', 2)); CaretAsserts.assertCaretPosition(logicalCaret.prev(findTextPos('p', 2)), findTextPos('p', 1)); CaretAsserts.assertCaretPosition(logicalCaret.prev(findTextPos('p', 1)), findTextPos('p', 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(findTextPos('p', 0)), null); }); it('from index text node over comment', () => { setupHtml('abcdefgh'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 0)), CaretPosition(getRoot().firstChild as Text, 0)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 1)), CaretPosition(getRoot().lastChild as Text, 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 2)), CaretPosition(getRoot().firstChild as Text, 4)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 3)), CaretPosition(getRoot().lastChild as Text, 4)); }); it('from text to text across elements', () => { setupHtml('

abc

def

'); CaretAsserts.assertCaretPosition(logicalCaret.next(findTextPos('p:first-of-type', 3)), findTextPos('p:last-of-type', 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(findTextPos('p:last-of-type', 0)), findTextPos('p:first-of-type', 3)); }); it('from text to text across elements with siblings', () => { setupHtml('

abc

def

'); CaretAsserts.assertCaretPosition(logicalCaret.next(findTextPos('p:first-of-type', 3)), CaretPosition(findElm('p:last-of-type')?.lastChild as Text, 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(findElm('p:last-of-type')?.lastChild as Text, 0)), findTextPos('p:first-of-type', 3)); }); it('from input to text', () => { setupHtml('123456'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 2)), CaretPosition(getRoot().lastChild as Text, 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 1)), CaretPosition(getRoot().firstChild as Text, 3)); }); it('from input to input across elements', () => { setupHtml('

'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(findElm('p:first-of-type') as HTMLParagraphElement, 1)), CaretPosition(findElm('p:last-of-type') as HTMLParagraphElement, 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(findElm('p:last-of-type') as HTMLParagraphElement, 0)), CaretPosition(findElm('p:first-of-type') as HTMLParagraphElement, 1)); }); it('next br to br across elements', () => { setupHtml('



'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(findElm('p:first-of-type') as HTMLParagraphElement, 0)), CaretPosition(findElm('p:last-of-type') as HTMLParagraphElement, 0)); }); it('from text node to before cef span over br', () => { setupHtml('

a
X

'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(findElm('p') as HTMLParagraphElement, 1)), CaretPosition(findElm('p') as HTMLParagraphElement, 2)); }); it('prev br to br across elements', () => { setupHtml('



'); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(findElm('p:last-of-type') as HTMLParagraphElement, 0)), CaretPosition(findElm('p:first-of-type') as HTMLParagraphElement, 0)); }); it('from before/after br to text', () => { setupHtml('
123
456
789'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 0)), CaretPosition(getChildNode(1), 0)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 2)), CaretPosition(getChildNode(3), 0)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 4)), CaretPosition(getChildNode(5), 0)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 5)), CaretPosition(getRoot().lastChild as Text, 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 5)), CaretPosition(getRoot(), 4)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 4)), CaretPosition(getChildNode(3), 3)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 1)), CaretPosition(getRoot(), 0)); }); it('over br', () => { setupHtml('


'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 0)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 1)), CaretPosition(getRoot(), 2)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 2)), null); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 3)), null); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 3)), CaretPosition(getRoot(), 2)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 2)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 1)), CaretPosition(getRoot(), 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 0)), null); }); it('over input', () => { setupHtml(''); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 0)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 1)), CaretPosition(getRoot(), 2)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 2)), CaretPosition(getRoot(), 3)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 3)), null); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 3)), CaretPosition(getRoot(), 2)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 2)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 1)), CaretPosition(getRoot(), 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 0)), null); }); it('over img', () => { setupHtml(''); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 0)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 1)), CaretPosition(getRoot(), 2)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 2)), CaretPosition(getRoot(), 3)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 3)), null); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 3)), CaretPosition(getRoot(), 2)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 2)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 1)), CaretPosition(getRoot(), 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 0)), null); }); it('over script/style/textarea', () => { setupHtml('abcd'); CaretAsserts.assertCaretPosition( logicalCaret.next(CaretPosition(getRoot().firstChild as Text, 1)), CaretPosition(getRoot().childNodes[2], 0) ); CaretAsserts.assertCaretPosition( logicalCaret.next(CaretPosition(getRoot().childNodes[2], 1)), CaretPosition(getRoot().childNodes[4], 0) ); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 5)), CaretPosition(getRoot(), 6)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 6)), CaretPosition(getRoot(), 5)); CaretAsserts.assertCaretPosition( logicalCaret.prev(CaretPosition(getRoot().childNodes[4], 0)), CaretPosition(getRoot().childNodes[2], 1) ); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 2)), CaretPosition(getRoot().childNodes[0], 1)); }); it('around tables', () => { setupHtml('a
A
B
b'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot().firstChild as Text, 1)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 1)), findTextPos('td:first-of-type', 0)); CaretAsserts.assertCaretPosition(logicalCaret.next(findTextPos('td:first-of-type', 1)), CaretPosition(getRoot(), 2)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 2)), findTextPos('table:last-of-type td', 0)); CaretAsserts.assertCaretPosition(logicalCaret.next(findTextPos('table:last-of-type td', 1)), CaretPosition(getRoot(), 3)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 3)), CaretPosition(getRoot().lastChild as Text, 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot().lastChild as Text, 0)), CaretPosition(getRoot(), 3)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 3)), findTextPos('table:last-of-type td', 1)); CaretAsserts.assertCaretPosition(logicalCaret.prev(findTextPos('table:last-of-type td', 0)), CaretPosition(getRoot(), 2)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 2)), findTextPos('td:first-of-type', 1)); CaretAsserts.assertCaretPosition(logicalCaret.prev(findTextPos('td:first-of-type', 0)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 1)), CaretPosition(getRoot().firstChild as Text, 1)); }); it('over cE=false', () => { setupHtml('123a456'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot().firstChild as Text, 3)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 1)), CaretPosition(getRoot(), 2)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 2)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot().lastChild as Text, 0)), CaretPosition(getRoot(), 2)); }); /* it('from outside cE=false to nested cE=true', () => { setupHtml('abcbcdef'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot().firstChild, 3)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 1)), findTextPos('span span', 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot().lastChild, 0)), CaretPosition(getRoot(), 2)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 2)), findTextPos('span span', 1)); }); it('from outside cE=false to nested cE=true before/after cE=false', () => { setupHtml('abd'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot(), 1)), CaretPosition(findElm('span span'), 0)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(findElm('span span'), 1)), CaretPosition(getRoot(), 2)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot(), 2)), CaretPosition(findElm('span span'), 1)); }); */ it('from after to last element', () => { setupHtml(''); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition.after(getRoot())), CaretPosition(getRoot(), 1)); }); it('from after to last element with br', () => { setupHtml('
'); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition.after(getRoot())), CaretPosition(getRoot(), 1)); }); it('from inside cE=true in cE=false to after cE=false', () => { setupHtml( '

' + '' + '' + 'abc' + '' + 'def' + '' + '

' + '

abc

' ); CaretAsserts.assertCaretPosition(logicalCaret.next(findTextPos('span span', 3)), CaretPosition(findElm('p') as HTMLParagraphElement, 1)); }); it('around cE=false inside nested cE=true', () => { setupHtml( '' + '' + '1' + '2' + '3' + '' + '' ); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(findElm('span span') as HTMLSpanElement, 0)), CaretPosition(findElm('span span') as HTMLSpanElement, 1)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(findElm('span span') as HTMLSpanElement, 1)), CaretPosition(findElm('span span') as HTMLSpanElement, 2)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(findElm('span span') as HTMLSpanElement, 2)), CaretPosition(findElm('span span') as HTMLSpanElement, 3)); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(findElm('span span') as HTMLSpanElement, 3)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(findElm('span span') as HTMLSpanElement, 0)), CaretPosition(getRoot(), 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(findElm('span span') as HTMLSpanElement, 1)), CaretPosition(findElm('span span') as HTMLSpanElement, 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(findElm('span span') as HTMLSpanElement, 2)), CaretPosition(findElm('span span') as HTMLSpanElement, 1)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(findElm('span span') as HTMLSpanElement, 3)), CaretPosition(findElm('span span') as HTMLSpanElement, 2)); }); it('next from last node', () => { setupHtml( '

' + '' + '

' ); CaretAsserts.assertCaretPosition(logicalCaret.next(findElmPos('p:first-of-type', 1)), CaretPosition(getRoot(), 1)); CaretAsserts.assertCaretPosition(logicalCaret.next(findElmPos('p:last-of-type', 1)), null); }); it('left/right between cE=false inlines in different blocks', () => { setupHtml( '

' + 'abc' + '

' + '

' + 'def' + '

' ); CaretAsserts.assertCaretPosition(logicalCaret.next(findElmPos('p:first-of-type', 1)), findElmPos('p:last-of-type', 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(findElmPos('p:last-of-type', 0)), findElmPos('p:first-of-type', 1)); }); it('from before/after root', () => { setupHtml( '

a

' + '

b

' ); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition.before(getRoot())), findTextPos('p:first-of-type', 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition.after(getRoot())), findTextPos('p:last-of-type', 1)); }); it('never into caret containers', () => { setupHtml('abcdefghi'); CaretAsserts.assertCaretPosition(logicalCaret.next(CaretPosition(getRoot().firstChild as Text, 3)), CaretPosition(getRoot().lastChild as Text, 0)); CaretAsserts.assertCaretPosition(logicalCaret.prev(CaretPosition(getRoot().lastChild as Text, 0)), CaretPosition(getRoot().firstChild as Text, 3)); }); });