import { Assertions } from '@ephox/agar'; import { context, describe, it } from '@ephox/bedrock-client'; import { Fun } from '@ephox/katamari'; import { Hierarchy, SelectorFind, Selectors, SugarElement } from '@ephox/sugar'; import { assert } from 'chai'; import CaretPosition from 'tinymce/core/caret/CaretPosition'; import * as BoundaryLocation from 'tinymce/core/keyboard/BoundaryLocation'; import { ZWSP } from 'tinymce/core/text/Zwsp'; import * as ViewBlock from '../../module/test/ViewBlock'; describe('browser.tinymce.core.keyboard.BoundaryLocationTest', () => { const viewBlock = ViewBlock.bddSetup(); const isInlineTarget = (elm: Node) => { return Selectors.is(SugarElement.fromDom(elm), 'a[href],code'); }; const createViewElement = (html: string) => { viewBlock.update(html); return SugarElement.fromDom(viewBlock.get()); }; const createLocation = (elm: SugarElement, elementPath: number[], offset: number) => { const container = Hierarchy.follow(elm, elementPath); const pos = CaretPosition(container.getOrDie().dom, offset); const location = BoundaryLocation.readLocation(isInlineTarget, elm.dom, pos); return location; }; const createPosition = (elm: SugarElement, elementPath: number[], offset: number) => { const container = Hierarchy.follow(elm, elementPath); return CaretPosition(container.getOrDie().dom, offset); }; const locationName = (location: BoundaryLocation.LocationAdt) => { return location.fold( Fun.constant('before'), Fun.constant('start'), Fun.constant('end'), Fun.constant('after') ); }; const locationElement = (location: BoundaryLocation.LocationAdt) => { return SugarElement.fromDom(location.fold( Fun.identity, Fun.identity, Fun.identity, Fun.identity )); }; const testValidLocation = (html: string, elementPath: number[], offset: number, expectedLocationName: string, expectedInline: string) => { const elm = createViewElement(html); const location = createLocation(elm, elementPath, offset); assert.isTrue(location.isSome(), 'Should be a valid location: ' + html); assert.equal(locationName(location.getOrDie()), expectedLocationName, 'Should be expected location'); Assertions.assertDomEq('Should be expected element', SelectorFind.descendant(elm, expectedInline).getOrDie(), locationElement(location.getOrDie())); }; const testInvalidLocation = (html: string, elementPath: number[], offset: number) => { const elm = createViewElement(html); const location = createLocation(elm, elementPath, offset); assert.isTrue(location.isNone(), 'Should not be a valid location: ' + html); }; const testFindLocation = (forward: boolean, html: string, elementPath: number[], offset: number, expectedLocationName: string, expectedInline: string) => { const elm = createViewElement(html); const position = createPosition(elm, elementPath, offset); const location = BoundaryLocation.findLocation(forward, isInlineTarget, elm.dom, position); Assertions.assertDomEq('Should be expected element', SelectorFind.descendant(elm, expectedInline).getOrDie(), locationElement(location.getOrDie())); assert.isTrue(location.isSome(), 'Should be a valid location: ' + html); assert.equal(locationName(location.getOrDie()), expectedLocationName, 'Should be expected location'); }; const testFindLocationInvalid = (forward: boolean, html: string, elementPath: number[], offset: number) => { const elm = createViewElement(html); const position = createPosition(elm, elementPath, offset); const location = BoundaryLocation.findLocation(forward, isInlineTarget, elm.dom, position); assert.isTrue(location.isNone(), 'Should not be a valid location: ' + html); }; const testPrevLocation = Fun.curry(testFindLocation, false); const testNextLocation = Fun.curry(testFindLocation, true); const testPrevLocationInvalid = Fun.curry(testFindLocationInvalid, false); const testNextLocationInvalid = Fun.curry(testFindLocationInvalid, true); context('Valid locations', () => { it('anchor locations', () => { testValidLocation('

a

', [ 0 ], 0, 'before', 'a'); testValidLocation('

a

', [ 0, 0, 0 ], 0, 'start', 'a'); testValidLocation('

a

', [ 0, 0, 0 ], 1, 'end', 'a'); testValidLocation('

a

', [ 0 ], 1, 'after', 'a'); testValidLocation('

aa

', [ 0, 0 ], 1, 'before', 'a'); testValidLocation('

aa

', [ 0, 1 ], 0, 'after', 'a'); testValidLocation('

ab

', [ 0, 0, 0 ], 0, 'start', 'a'); testValidLocation('

ab

', [ 0, 0, 0 ], 2, 'end', 'a'); testValidLocation('

a

', [ 0 ], 1, 'before', 'a'); testValidLocation('

', [ 0, 0 ], 0, 'start', 'a'); testValidLocation('

', [ 0, 0 ], 1, 'end', 'a'); testValidLocation('

a

', [ 0 ], 1, 'after', 'a'); testValidLocation('

a

b

', [ 0 ], 1, 'after', 'a'); testValidLocation('

a

b

', [ 1 ], 0, 'before', 'p:nth-child(2) a'); }); it('code locations', () => { testValidLocation('

a

', [ 0 ], 0, 'before', 'code'); testValidLocation('

a

', [ 0, 0 ], 0, 'start', 'code'); testValidLocation('

a

', [ 0, 0 ], 1, 'end', 'code'); testValidLocation('

a

', [ 0 ], 1, 'after', 'code'); }); it('anchor + code locations', () => { testValidLocation('

a

', [ 0 ], 0, 'before', 'a'); testValidLocation('

a

', [ 0, 0, 0 ], 0, 'start', 'a'); testValidLocation('

a

', [ 0, 0, 0 ], 1, 'end', 'a'); testValidLocation('

a

', [ 0 ], 1, 'after', 'a'); }); }); it('Valid zwsp locations', () => { testValidLocation('

' + ZWSP + 'a

', [ 0, 0 ], 0, 'before', 'a'); testValidLocation('

' + ZWSP + 'a

', [ 0, 0, 0 ], 1, 'start', 'a'); testValidLocation('

a' + ZWSP + '

', [ 0, 0, 0 ], 1, 'end', 'a'); testValidLocation('

a' + ZWSP + '

', [ 0, 1 ], 1, 'after', 'a'); }); context('Invalid locations', () => { it('paragraph locations', () => { testInvalidLocation('

a

', [ 0, 0 ], 0); testInvalidLocation('

a

', [ 0 ], 0); testInvalidLocation('

a

', [ 0 ], 1); }); it('anchor locations', () => { testInvalidLocation('

aab

', [ 0, 0 ], 0); testInvalidLocation('

aab

', [ 0, 2 ], 1); testInvalidLocation('

a

', [ 0 ], 0); testInvalidLocation('

a

', [ 0 ], 2); testInvalidLocation('

', [ 0, 0 ], 1); testInvalidLocation('

a

', [ 0, 0, 0 ], 0); testInvalidLocation('

\u05D4

', [ 0, 0, 0 ], 0); }); it('anchor + code locations', () => { testInvalidLocation('

abc

', [ 0, 0, 0 ], 1); testInvalidLocation('

abc

', [ 0, 0, 2 ], 0); }); it('format caret parent', () => { testInvalidLocation('

a

', [ 0, 0, 0 ], 0); testInvalidLocation('

a

', [ 0, 0, 0, 0 ], 0); }); }); it('Previous locations', () => { testPrevLocation('

ab

', [ 0, 1 ], 1, 'after', 'a'); testPrevLocation('

a

', [ 0 ], 1, 'end', 'a'); testPrevLocation('

a

', [ 0, 0, 0 ], 1, 'start', 'a'); testPrevLocation('

a

', [ 0, 0, 0 ], 0, 'before', 'a'); testPrevLocation('

', [ 0 ], 1, 'end', 'a'); testPrevLocation('

', [ 0, 0 ], 1, 'start', 'a'); testPrevLocation('

', [ 0, 0 ], 0, 'before', 'a'); }); it('Previous locations between inlines', () => { testPrevLocation('

ab

', [ 0, 1, 0 ], 0, 'before', 'a:nth-child(2)'); }); it('Previous locations between blocks', () => { testPrevLocation('

a

b

', [ 1 ], 0, 'end', 'p:nth-child(1) a'); testPrevLocation('

a

b

', [ 1, 0, 0 ], 0, 'before', 'p:nth-child(2) a'); testPrevLocation('

ab

c

', [ 1, 0, 0 ], 0, 'before', 'p:nth-child(2) a'); testPrevLocation('

a

c

', [ 1 ], 0, 'after', 'p:nth-child(1) a'); testPrevLocationInvalid('

a

bc

', [ 1, 0 ], 1); testPrevLocationInvalid('

ab

c

', [ 1 ], 0); }); it('Previous zwsp locations', () => { testPrevLocation('

a' + ZWSP + 'b

', [ 0, 1 ], 2, 'after', 'a'); testPrevLocation('

a' + ZWSP + '

', [ 0, 1 ], 1, 'end', 'a'); testPrevLocation('

a' + ZWSP + '

', [ 0, 0, 0 ], 1, 'start', 'a'); testPrevLocation('

' + ZWSP + 'a

', [ 0, 0, 0 ], 1, 'before', 'a'); }); it('Next locations', () => { testNextLocation('

ab

', [ 0, 0 ], 0, 'before', 'a'); testNextLocation('

a

', [ 0 ], 0, 'start', 'a'); testNextLocation('

a

', [ 0, 0, 0 ], 0, 'end', 'a'); testNextLocation('

a

', [ 0, 0, 0 ], 1, 'after', 'a'); testNextLocation('

', [ 0 ], 0, 'start', 'a'); testNextLocation('

', [ 0, 0 ], 0, 'end', 'a'); testNextLocation('

', [ 0, 0 ], 1, 'after', 'a'); }); it('Next locations between inlines', () => { testNextLocation('

ab

', [ 0, 0, 0 ], 1, 'after', 'a:nth-child(1)'); }); it('Next locations between blocks', () => { testNextLocation('

a

b

', [ 0 ], 1, 'start', 'p:nth-child(2) a'); testNextLocation('

a

b

', [ 0, 0, 0 ], 1, 'after', 'p:nth-child(1) a'); testNextLocationInvalid('

ab

c

', [ 0, 1 ], 0); testNextLocationInvalid('

a

bc

', [ 0 ], 1); }); it('Next zwsp locations', () => { testNextLocation('

a' + ZWSP + 'b

', [ 0, 0 ], 0, 'before', 'a'); testNextLocation('

' + ZWSP + 'a

', [ 0 ], 0, 'start', 'a'); testNextLocation('

' + ZWSP + 'a

', [ 0, 0, 0 ], 1, 'end', 'a'); testNextLocation('

a' + ZWSP + '

', [ 0, 0, 0 ], 1, 'after', 'a'); }); });