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('
', [0], 0, 'before', 'a'), sTestValidLocation('', [0, 0, 0], 0, 'start', 'a'), sTestValidLocation('', [0, 0, 0], 1, 'end', 'a'), sTestValidLocation('', [0], 1, 'after', 'a'), sTestValidLocation('aa
', [0, 0], 1, 'before', 'a'), sTestValidLocation('aa
', [0, 1], 0, 'after', 'a'), sTestValidLocation('', [0, 0, 0], 0, 'start', 'a'), sTestValidLocation('', [0, 0, 0], 2, 'end', 'a'), sTestValidLocation('', [0], 1, 'before', 'a'), sTestValidLocation('', [0, 0], 0, 'start', 'a'), sTestValidLocation('', [0, 0], 1, 'end', 'a'), sTestValidLocation('', [0], 1, 'after', 'a'), sTestValidLocation('', [0], 1, 'after', 'a'), sTestValidLocation('', [1], 0, 'before', 'p:nth-child(2) a') ])), Logger.t('code locations', GeneralSteps.sequence([ sTestValidLocation('a
a
a
a
' + ZWSP + 'a
', [0, 0], 0, 'before', 'a'), sTestValidLocation('', [0, 0, 0], 1, 'start', 'a'), sTestValidLocation('', [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('', [0], 0), sTestInvalidLocation('', [0], 2), sTestInvalidLocation('', [0, 0], 1), sTestInvalidLocation('', [0, 0, 0], 0), sTestInvalidLocation('', [0, 0, 0], 0), Logger.t('anchor + code locations', GeneralSteps.sequence([ sTestInvalidLocation('', [0, 0, 0], 1), sTestInvalidLocation('', [0, 0, 2], 0) ])), Logger.t('format caret parent', GeneralSteps.sequence([ sTestInvalidLocation('a
', [0, 0, 0], 0), sTestInvalidLocation('a
ab
', [0, 1], 1, 'after', 'a'), sTestPrevLocation('', [0], 1, 'end', 'a'), sTestPrevLocation('', [0, 0, 0], 1, 'start', 'a'), sTestPrevLocation('', [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('', [0, 1, 0], 0, 'before', 'a:nth-child(2)') ])); const sTestPrevLocationsBetweenBlocks = Logger.t('sTestPrevLocationsBetweenBlocks', GeneralSteps.sequence([ sTestPrevLocation('', [1], 0, 'end', 'p:nth-child(1) a'), sTestPrevLocation('', [1, 0, 0], 0, 'before', 'p:nth-child(2) a'), sTestPrevLocation('ab
', [1, 0, 0], 0, 'before', 'p:nth-child(2) a'), sTestPrevLocation('', [1], 0, 'after', 'p:nth-child(1) a'), sTestPrevLocationInvalid('bc
', [1, 0], 1), sTestPrevLocationInvalid('ab
', [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('', [0, 0, 0], 1, 'start', 'a'), sTestPrevLocation('', [0, 0, 0], 1, 'before', 'a') ])); const sTestNextLocations = Logger.t('sTestNextLocations', GeneralSteps.sequence([ sTestNextLocation('ab
', [0, 0], 0, 'before', 'a'), sTestNextLocation('', [0], 0, 'start', 'a'), sTestNextLocation('', [0, 0, 0], 0, 'end', 'a'), sTestNextLocation('', [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('', [0, 0, 0], 1, 'after', 'a:nth-child(1)') ])); const sTestNextLocationsBetweenBlocks = Logger.t('sTestNextLocationsBetweenBlocks', GeneralSteps.sequence([ sTestNextLocation('', [0], 1, 'start', 'p:nth-child(2) a'), sTestNextLocation('', [0, 0, 0], 1, 'after', 'p:nth-child(1) a'), sTestNextLocationInvalid('ab
', [0, 1], 0), sTestNextLocationInvalid('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('', [0, 0, 0], 1, 'end', 'a'), sTestNextLocation('', [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); });