import { Assertions, GeneralSteps, Logger, Pipeline, Step } from '@ephox/agar'; import { Fun } from '@ephox/katamari'; import { Hierarchy, Element, SelectorFind, Selectors } from '@ephox/sugar'; import CaretPosition from 'tinymce/core/caret/CaretPosition'; import BoundaryLocation from 'tinymce/core/keyboard/BoundaryLocation'; import ViewBlock from '../../module/test/ViewBlock'; import Zwsp from 'tinymce/core/text/Zwsp'; import { UnitTest } from '@ephox/bedrock'; UnitTest.asynctest('browser.tinymce.core.keyboard.BoundaryLocationTest', function () { const success = arguments[arguments.length - 2]; const failure = arguments[arguments.length - 1]; const ZWSP = Zwsp.ZWSP; const viewBlock = ViewBlock(); const isInlineTarget = function (elm) { return Selectors.is(Element.fromDom(elm), 'a[href],code'); }; const createViewElement = function (html) { viewBlock.update(html); return Element.fromDom(viewBlock.get()); }; const createLocation = function (elm, elementPath, offset) { 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 = function (elm, elementPath, offset) { const container = Hierarchy.follow(elm, elementPath); return CaretPosition(container.getOrDie().dom(), offset); }; const locationName = function (location) { return location.fold( Fun.constant('before'), Fun.constant('start'), Fun.constant('end'), Fun.constant('after') ); }; const locationElement = function (location) { return Element.fromDom(location.fold( Fun.identity, Fun.identity, Fun.identity, Fun.identity )); }; const sTestValidLocation = function (html, elementPath, offset, expectedLocationName, expectedInline) { return Step.sync(function () { const elm = createViewElement(html); const location = createLocation(elm, elementPath, offset); Assertions.assertEq('Should be a valid location: ' + html, true, location.isSome()); Assertions.assertEq('Should be expected location', expectedLocationName, locationName(location.getOrDie())); Assertions.assertDomEq('Should be expected element', SelectorFind.descendant(elm, expectedInline).getOrDie(), locationElement(location.getOrDie())); }); }; const sTestInvalidLocation = function (html, elementPath, offset) { return Step.sync(function () { const elm = createViewElement(html); const location = createLocation(elm, elementPath, offset); Assertions.assertEq('Should not be a valid location: ' + html, true, location.isNone()); }); }; const sTestFindLocation = function (forward, html, elementPath, offset, expectedLocationName, expectedInline) { return Step.sync(function () { 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())); Assertions.assertEq('Should be a valid location: ' + html, true, location.isSome()); Assertions.assertEq('Should be expected location', expectedLocationName, locationName(location.getOrDie())); }); }; const sTestFindLocationInvalid = function (forward, html, elementPath, offset) { return Step.sync(function () { const elm = createViewElement(html); const position = createPosition(elm, elementPath, offset); const location = BoundaryLocation.findLocation(forward, isInlineTarget, elm.dom(), position); Assertions.assertEq('Should not be a valid location: ' + html, true, location.isNone()); }); }; const sTestPrevLocation = Fun.curry(sTestFindLocation, false); const sTestNextLocation = Fun.curry(sTestFindLocation, true); const sTestPrevLocationInvalid = Fun.curry(sTestFindLocationInvalid, false); const sTestNextLocationInvalid = Fun.curry(sTestFindLocationInvalid, true); const sTestValidLocations = Logger.t('sTestValidLocations', GeneralSteps.sequence([ Logger.t('anchor locations', GeneralSteps.sequence([ sTestValidLocation('

a

', [0], 0, 'before', 'a'), sTestValidLocation('

a

', [0, 0, 0], 0, 'start', 'a'), sTestValidLocation('

a

', [0, 0, 0], 1, 'end', 'a'), sTestValidLocation('

a

', [0], 1, 'after', 'a'), sTestValidLocation('

aa

', [0, 0], 1, 'before', 'a'), sTestValidLocation('

aa

', [0, 1], 0, 'after', 'a'), sTestValidLocation('

ab

', [0, 0, 0], 0, 'start', 'a'), sTestValidLocation('

ab

', [0, 0, 0], 2, 'end', 'a'), sTestValidLocation('

a

', [0], 1, 'before', 'a'), sTestValidLocation('

', [0, 0], 0, 'start', 'a'), sTestValidLocation('

', [0, 0], 1, 'end', 'a'), sTestValidLocation('

a

', [0], 1, 'after', 'a'), sTestValidLocation('

a

b

', [0], 1, 'after', 'a'), sTestValidLocation('

a

b

', [1], 0, 'before', 'p:nth-child(2) a') ])), Logger.t('code locations', GeneralSteps.sequence([ sTestValidLocation('

a

', [0], 0, 'before', 'code'), sTestValidLocation('

a

', [0, 0], 0, 'start', 'code'), sTestValidLocation('

a

', [0, 0], 1, 'end', 'code'), sTestValidLocation('

a

', [0], 1, 'after', 'code') ])), Logger.t('anchor + code locations', GeneralSteps.sequence([ sTestValidLocation('

a

', [0], 0, 'before', 'a'), sTestValidLocation('

a

', [0, 0, 0], 0, 'start', 'a'), sTestValidLocation('

a

', [0, 0, 0], 1, 'end', 'a'), sTestValidLocation('

a

', [0], 1, 'after', 'a') ])) ])); const sTestValidZwspLocations = Logger.t('sTestValidZwspLocations', GeneralSteps.sequence([ sTestValidLocation('

' + ZWSP + 'a

', [0, 0], 0, 'before', 'a'), sTestValidLocation('

' + ZWSP + 'a

', [0, 0, 0], 1, 'start', 'a'), sTestValidLocation('

a' + ZWSP + '

', [0, 0, 0], 1, 'end', 'a'), sTestValidLocation('

a' + ZWSP + '

', [0, 1], 1, 'after', 'a') ])); const sTestInvalidLocations = Logger.t('sTestInvalidLocations', GeneralSteps.sequence([ sTestInvalidLocation('

a

', [0, 0], 0), sTestInvalidLocation('

a

', [0], 0), sTestInvalidLocation('

a

', [0], 1), sTestInvalidLocation('

aab

', [0, 0], 0), sTestInvalidLocation('

aab

', [0, 2], 1), sTestInvalidLocation('

a

', [0], 0), sTestInvalidLocation('

a

', [0], 2), sTestInvalidLocation('

', [0, 0], 1), sTestInvalidLocation('

a

', [0, 0, 0], 0), sTestInvalidLocation('

\u05D4

', [0, 0, 0], 0), Logger.t('anchor + code locations', GeneralSteps.sequence([ sTestInvalidLocation('

abc

', [0, 0, 0], 1), sTestInvalidLocation('

abc

', [0, 0, 2], 0) ])), Logger.t('format caret parent', GeneralSteps.sequence([ sTestInvalidLocation('

a

', [0, 0, 0], 0), sTestInvalidLocation('

a

', [0, 0, 0, 0], 0) ])) ])); const sTestPrevLocations = Logger.t('sTestPrevLocations', GeneralSteps.sequence([ sTestPrevLocation('

ab

', [0, 1], 1, 'after', 'a'), sTestPrevLocation('

a

', [0], 1, 'end', 'a'), sTestPrevLocation('

a

', [0, 0, 0], 1, 'start', 'a'), sTestPrevLocation('

a

', [0, 0, 0], 0, 'before', 'a'), sTestPrevLocation('

', [0], 1, 'end', 'a'), sTestPrevLocation('

', [0, 0], 1, 'start', 'a'), sTestPrevLocation('

', [0, 0], 0, 'before', 'a') ])); const sTestPrevLocationsBetweenInlines = Logger.t('sTestPrevLocationsBetweenInlines', GeneralSteps.sequence([ sTestPrevLocation('

ab

', [0, 1, 0], 0, 'before', 'a:nth-child(2)') ])); const sTestPrevLocationsBetweenBlocks = Logger.t('sTestPrevLocationsBetweenBlocks', GeneralSteps.sequence([ sTestPrevLocation('

a

b

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

a

b

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

ab

c

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

a

c

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

a

bc

', [1, 0], 1), sTestPrevLocationInvalid('

ab

c

', [1], 0) ])); const sTestPrevZwspLocations = Logger.t('sTestPrevLocations', GeneralSteps.sequence([ sTestPrevLocation('

a' + ZWSP + 'b

', [0, 1], 2, 'after', 'a'), sTestPrevLocation('

a' + ZWSP + '

', [0, 1], 1, 'end', 'a'), sTestPrevLocation('

a' + ZWSP + '

', [0, 0, 0], 1, 'start', 'a'), sTestPrevLocation('

' + ZWSP + 'a

', [0, 0, 0], 1, 'before', 'a') ])); const sTestNextLocations = Logger.t('sTestNextLocations', GeneralSteps.sequence([ sTestNextLocation('

ab

', [0, 0], 0, 'before', 'a'), sTestNextLocation('

a

', [0], 0, 'start', 'a'), sTestNextLocation('

a

', [0, 0, 0], 0, 'end', 'a'), sTestNextLocation('

a

', [0, 0, 0], 1, 'after', 'a'), sTestNextLocation('

', [0], 0, 'start', 'a'), sTestNextLocation('

', [0, 0], 0, 'end', 'a'), sTestNextLocation('

', [0, 0], 1, 'after', 'a') ])); const sTestNextLocationsBetweenInlines = Logger.t('sTestNextLocationsBetweenInlines', GeneralSteps.sequence([ sTestNextLocation('

ab

', [0, 0, 0], 1, 'after', 'a:nth-child(1)') ])); const sTestNextLocationsBetweenBlocks = Logger.t('sTestNextLocationsBetweenBlocks', GeneralSteps.sequence([ sTestNextLocation('

a

b

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

a

b

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

ab

c

', [0, 1], 0), sTestNextLocationInvalid('

a

bc

', [0], 1) ])); const sTestNextZwspLocations = Logger.t('sTestNextZwspLocations', GeneralSteps.sequence([ sTestNextLocation('

a' + ZWSP + 'b

', [0, 0], 0, 'before', 'a'), sTestNextLocation('

' + ZWSP + 'a

', [0], 0, 'start', 'a'), sTestNextLocation('

' + ZWSP + 'a

', [0, 0, 0], 1, 'end', 'a'), sTestNextLocation('

a' + ZWSP + '

', [0, 0, 0], 1, 'after', 'a') ])); viewBlock.attach(); Pipeline.async({}, [ sTestValidLocations, sTestValidZwspLocations, sTestInvalidLocations, sTestPrevLocations, sTestPrevLocationsBetweenInlines, sTestPrevLocationsBetweenBlocks, sTestPrevZwspLocations, sTestNextLocations, sTestNextLocationsBetweenInlines, sTestNextLocationsBetweenBlocks, sTestNextZwspLocations ], function () { viewBlock.detach(); success(); }, failure); });