import { Chain, Guard, NamedChain, Touch, UiFinder } from '@ephox/agar'; import { UnitTest } from '@ephox/bedrock-client'; import { Fun, Optional, Result } from '@ephox/katamari'; import { PlatformDetection } from '@ephox/sand'; import { Css, Scroll, SugarPosition } from '@ephox/sugar'; import * as Boxes from 'ephox/alloy/alien/Boxes'; import * as Behaviour from 'ephox/alloy/api/behaviour/Behaviour'; import { Dragging } from 'ephox/alloy/api/behaviour/Dragging'; import * as GuiFactory from 'ephox/alloy/api/component/GuiFactory'; import * as Memento from 'ephox/alloy/api/component/Memento'; import * as DragCoord from 'ephox/alloy/api/data/DragCoord'; import { Container } from 'ephox/alloy/api/ui/Container'; import * as GuiSetup from 'ephox/alloy/test/GuiSetup'; UnitTest.asynctest('TouchDraggingTest', (success, failure) => { // Override isTouch() to make Alloy listen to touch events const origPlatform = PlatformDetection.detect(); PlatformDetection.override({ deviceType: { ...origPlatform.deviceType, isTouch: Fun.always } }); const subject = Memento.record( Container.sketch({ dom: { styles: { 'box-sizing': 'border-box', 'width': '100px', 'height': '100px', 'border': '1px solid green' } }, containerBehaviours: Behaviour.derive([ Dragging.config({ mode: 'touch', blockerClass: 'test-blocker', snaps: { getSnapPoints: () => [ Dragging.snap({ sensor: DragCoord.fixed(300, 10), range: SugarPosition(1000, 30), output: DragCoord.fixed(Optional.none(), Optional.some(10)) }) ], leftAttr: 'data-snap-left', topAttr: 'data-snap-top' }, getBounds: () => { const scroll = Scroll.get(); return Boxes.bounds(scroll.left, scroll.top, 500, 500); } }) ]) }) ); GuiSetup.setup((_store, _doc, _body) => GuiFactory.build( Container.sketch({ dom: { tag: 'div', styles: { 'margin-bottom': '2000px' } }, components: [ subject.asSpec() ] }) ), (_doc, _body, gui, component, _store) => { const cSubject = Chain.mapper(() => subject.get(component).element); const cEnsurePositionChanged = Chain.control( Chain.binder((all: any) => all.box_position1.left !== all.box_position2.left && all.box_position2.left !== all.box_position3.left ? Result.value({}) : Result.error('Positions did not change.\nSugarPosition data: ' + JSON.stringify({ 1: all.box_position1, 2: all.box_position2, 3: all.box_position3 }, null, 2))), Guard.addLogging('Ensuring that the position information read from the different stages was different') ); const cEnsureBound = Chain.control( Chain.binder((all: any) => { const boundLeft = all.box_position4.left !== all.box_position5.left && all.box_position5.left === all.box_position6_bound.left && all.box_position5.left === '0px' && all.box_position6_bound.top === '100px'; const boundRight = all.box_position6_bound.left !== all.box_position7.left && all.box_position7.left === all.box_position8_bound.left && all.box_position7.left === '400px' && all.box_position8_bound.top === '100px'; return boundLeft && boundRight ? Result.value({}) : Result.error('Dragging should have been restricted to the bounds.\nSugarPosition data: ' + JSON.stringify({ 1: all.box_position4, 2: all.box_position5, 3: all.box_position6_bound, 4: all.box_position7, 5: all.box_position8_bound }, null, 2)); }), Guard.addLogging('Checking bounding behaviour at left and right of screen') ); const cEnsureScrollBound = Chain.control( Chain.binder((all: any) => { const boundBottom = all.box_scrolled_position9.top === all.box_scrolled_position10_bound.top && all.box_scrolled_position9.top === '400px' && all.box_scrolled_position10_bound.left === '50px'; return boundBottom ? Result.value({}) : Result.error('Dragging should have been restricted to the bounds.\nSugarPosition data: ' + JSON.stringify({ 1: all.box_scrolled_position9, 2: all.box_scrolled_position10_bound }, null, 2)); }), Guard.addLogging('Checking bounding behaviour at bottom of screen') ); const cEnsurePinned = Chain.control( Chain.binder((all: any) => { const pinned = all.box_position11.top !== all.box_position12_pinned.top && all.box_position12_pinned.top === all.box_position13_pinned.top && all.box_position12_pinned.top === '10px'; return pinned ? Result.value({ }) : Result.error( 'Box should only have been pinned at 2 and 3 at top: 10px. Positions: ' + JSON.stringify({ 1: all.box_position11, 2: all.box_position12_pinned, 3: all.box_position13_pinned }, null, 2) ); }), Guard.addLogging('Checking pinning behaviour to top of screen') ); const cRecordPosition = Chain.fromChains([ Chain.control( Chain.binder((box) => Css.getRaw(box, 'left').bind((left) => Css.getRaw(box, 'top').map((top) => Result.value({ left, top }))).getOrThunk(() => Result.error('No left,top information yet'))), Guard.tryUntil('Waiting for position data to record') ) ]); const cScrollTo = (x: number, y: number): Chain => Chain.op(() => { Scroll.to(x, y); }); const cReset = Chain.fromChains([ NamedChain.direct('blocker', Touch.cTouchEnd, '_'), NamedChain.direct('container', Chain.control( UiFinder.cFindIn('.test-blocker'), Guard.tryUntilNot('There should no longer be a blocker') ), 'blocker'), // When testing bounds/pinning, we need every browser to behave identically, so we reset positions // so we know what we are dealing with NamedChain.direct('box', Chain.op((elem) => { Css.setAll(elem, { left: '50px', top: '100px' }); }), '_'), NamedChain.direct('box', Touch.cTouchStart, '_'), NamedChain.direct('container', UiFinder.cFindIn('.test-blocker'), 'blocker') ]); return [ Chain.asStep({}, [ NamedChain.asChain([ NamedChain.write('box', cSubject), NamedChain.direct('box', Touch.cTouchStart, '_'), NamedChain.writeValue('container', gui.element), NamedChain.direct('container', UiFinder.cFindIn('.test-blocker'), 'blocker'), NamedChain.direct('blocker', Touch.cTouchMoveTo(100, 200), '_'), NamedChain.direct('blocker', Touch.cTouchMoveTo(120, 200), '_'), NamedChain.direct('box', cRecordPosition, 'box_position1'), NamedChain.direct('blocker', Touch.cTouchMoveTo(140, 200), '_'), NamedChain.direct('box', cRecordPosition, 'box_position2'), NamedChain.direct('blocker', Touch.cTouchMoveTo(160, 200), '_'), NamedChain.direct('box', cRecordPosition, 'box_position3'), NamedChain.write('_', cEnsurePositionChanged), cReset, // Touch move on component (box) NamedChain.direct('box', Touch.cTouchMoveTo(100, 200), '_'), NamedChain.direct('box', Touch.cTouchMoveTo(120, 200), '_'), NamedChain.direct('box', cRecordPosition, 'box_position1'), NamedChain.direct('box', Touch.cTouchMoveTo(140, 200), '_'), NamedChain.direct('box', cRecordPosition, 'box_position2'), NamedChain.direct('box', Touch.cTouchMoveTo(160, 200), '_'), NamedChain.direct('box', cRecordPosition, 'box_position3'), NamedChain.write('_', cEnsurePositionChanged), cReset, // Test bounds NamedChain.direct('blocker', Touch.cTouchMoveTo(100, 200), '_'), NamedChain.direct('blocker', Touch.cTouchMoveTo(50, 200), '_'), NamedChain.direct('box', cRecordPosition, 'box_position4'), NamedChain.direct('blocker', Touch.cTouchMoveTo(0, 200), '_'), NamedChain.direct('box', cRecordPosition, 'box_position5'), NamedChain.direct('blocker', Touch.cTouchMoveTo(-50, 200), '_'), NamedChain.direct('box', cRecordPosition, 'box_position6_bound'), NamedChain.direct('blocker', Touch.cTouchMoveTo(400, 200), '_'), NamedChain.direct('box', cRecordPosition, 'box_position7'), NamedChain.direct('blocker', Touch.cTouchMoveTo(500, 200), '_'), NamedChain.direct('box', cRecordPosition, 'box_position8_bound'), NamedChain.write('_', cEnsureBound), // Test bounds when scrolled cScrollTo(0, 1000), cReset, NamedChain.direct('blocker', Touch.cTouchMoveTo(100, 1100), '_'), NamedChain.direct('blocker', Touch.cTouchMoveTo(100, 1100), '_'), NamedChain.direct('blocker', Touch.cTouchMoveTo(100, 1400), '_'), NamedChain.direct('box', cRecordPosition, 'box_scrolled_position9'), NamedChain.direct('blocker', Touch.cTouchMoveTo(100, 1500), '_'), NamedChain.direct('box', cRecordPosition, 'box_scrolled_position10_bound'), NamedChain.write('_', cEnsureScrollBound), cScrollTo(0, 0), cReset, // Test pinning. NamedChain.direct('blocker', Touch.cTouchMoveTo(50, 100), '_'), NamedChain.direct('blocker', Touch.cTouchMoveTo(50, 100), '_'), NamedChain.direct('blocker', Touch.cTouchMoveTo(50, 60), '_'), NamedChain.direct('box', cRecordPosition, 'box_position11'), NamedChain.direct('blocker', Touch.cTouchMoveTo(50, 30), '_'), NamedChain.direct('box', cRecordPosition, 'box_position12_pinned'), NamedChain.direct('blocker', Touch.cTouchMoveTo(160, 20), '_'), NamedChain.direct('box', cRecordPosition, 'box_position13_pinned'), NamedChain.write('_', cEnsurePinned), Chain.wait(10), NamedChain.bundle((output) => Result.value(output)) ]) ]) ]; }, () => { PlatformDetection.override(origPlatform); success(); }, (err, logs) => { PlatformDetection.override(origPlatform); failure(err, logs); }); });