From a98c8e3bc8c4319c96da024b9356c140eff476c1 Mon Sep 17 00:00:00 2001 From: Philipp Fromme Date: Thu, 23 May 2019 22:37:06 +0200 Subject: [PATCH] chore(bpmn-snapping): seperate snapping into create/move and connect * move create/move snapping to BpmnCreateMoveSnapping * move connect snapping to BpmnConnectSnapping * refactor tests Related camunda/camunda-modeler#1290 --- lib/features/snapping/BpmnConnectSnapping.js | 98 ++ .../snapping/BpmnCreateMoveSnapping.js | 137 +++ lib/features/snapping/BpmnSnapping.js | 358 ------- lib/features/snapping/index.js | 13 +- .../snapping/BpmnConnectSnapping.bpmn | 54 + .../snapping/BpmnConnectSnappingSpec.js | 212 ++++ ...pmnCreateMoveSnapping.boundary-events.bpmn | 21 + .../BpmnCreateMoveSnapping.collaboration.bpmn | 31 + .../BpmnCreateMoveSnapping.process.bpmn | 13 + ...BpmnCreateMoveSnapping.sequence-flows.bpmn | 30 + .../snapping/BpmnCreateMoveSnappingSpec.js | 381 +++++++ .../snapping/BpmnSnapping.connect.bpmn | 55 - .../snapping/BpmnSnapping.general.bpmn | 38 - .../snapping/BpmnSnapping.labels.bpmn | 27 - .../snapping/BpmnSnapping.sequenceFlow.bpmn | 41 - .../features/snapping/BpmnSnappingSpec.js | 946 ------------------ 16 files changed, 987 insertions(+), 1468 deletions(-) create mode 100644 lib/features/snapping/BpmnConnectSnapping.js create mode 100644 lib/features/snapping/BpmnCreateMoveSnapping.js delete mode 100644 lib/features/snapping/BpmnSnapping.js create mode 100644 test/spec/features/snapping/BpmnConnectSnapping.bpmn create mode 100644 test/spec/features/snapping/BpmnConnectSnappingSpec.js create mode 100644 test/spec/features/snapping/BpmnCreateMoveSnapping.boundary-events.bpmn create mode 100644 test/spec/features/snapping/BpmnCreateMoveSnapping.collaboration.bpmn create mode 100644 test/spec/features/snapping/BpmnCreateMoveSnapping.process.bpmn create mode 100644 test/spec/features/snapping/BpmnCreateMoveSnapping.sequence-flows.bpmn create mode 100644 test/spec/features/snapping/BpmnCreateMoveSnappingSpec.js delete mode 100644 test/spec/features/snapping/BpmnSnapping.connect.bpmn delete mode 100644 test/spec/features/snapping/BpmnSnapping.general.bpmn delete mode 100644 test/spec/features/snapping/BpmnSnapping.labels.bpmn delete mode 100644 test/spec/features/snapping/BpmnSnapping.sequenceFlow.bpmn delete mode 100644 test/spec/features/snapping/BpmnSnappingSpec.js diff --git a/lib/features/snapping/BpmnConnectSnapping.js b/lib/features/snapping/BpmnConnectSnapping.js new file mode 100644 index 00000000..64717420 --- /dev/null +++ b/lib/features/snapping/BpmnConnectSnapping.js @@ -0,0 +1,98 @@ +import { + mid, + setSnapped +} from 'diagram-js/lib/features/snapping/SnapUtil'; + +import { isCmd } from 'diagram-js/lib/features/keyboard/KeyboardUtil'; + +import { is } from '../../util/ModelUtil'; + +import { some } from 'min-dash'; + +var HIGHER_PRIORITY = 1250; + + +/** + * Snap during connect. + * + * @param {EventBus} eventBus + * @param {Rules} rules + */ +export default function BpmnConnectSnapping(eventBus, rules) { + eventBus.on([ + 'connect.hover', + 'connect.move', + 'connect.end', + ], HIGHER_PRIORITY, function(event) { + var context = event.context, + source = context.source, + target = context.target; + + if (event.originalEvent && isCmd(event.originalEvent)) { + return; + } + + if (!context.initialSourcePosition) { + context.initialSourcePosition = context.sourcePosition; + } + + var connectionAttrs = rules.allowed('connection.create', { + source: source, + target: target + }); + + if (target && isAnyType(connectionAttrs, [ + 'bpmn:Association', + 'bpmn:DataInputAssociation', + 'bpmn:DataOutputAssociation', + 'bpmn:SequenceFlow' + ])) { + + // snap source + context.sourcePosition = mid(source); + + // snap target + snapToPosition(event, mid(target)); + } else if (isType(connectionAttrs, 'bpmn:MessageFlow')) { + + if (is(source, 'bpmn:Event')) { + + // snap source + context.sourcePosition = mid(source); + } + + if (is(target, 'bpmn:Event')) { + + // snap target + snapToPosition(event, mid(target)); + } + + } else { + + // un-snap source + context.sourcePosition = context.initialSourcePosition; + } + }); +} + +BpmnConnectSnapping.$inject = [ + 'eventBus', + 'rules' +]; + +// helpers ////////// + +function snapToPosition(event, position) { + setSnapped(event, 'x', position.x); + setSnapped(event, 'y', position.y); +} + +function isType(attrs, type) { + return attrs && attrs.type === type; +} + +function isAnyType(attrs, types) { + return some(types, function(type) { + return isType(attrs, type); + }); +} \ No newline at end of file diff --git a/lib/features/snapping/BpmnCreateMoveSnapping.js b/lib/features/snapping/BpmnCreateMoveSnapping.js new file mode 100644 index 00000000..56252d4f --- /dev/null +++ b/lib/features/snapping/BpmnCreateMoveSnapping.js @@ -0,0 +1,137 @@ +import inherits from 'inherits'; + +import CreateMoveSnapping from 'diagram-js/lib/features/snapping/CreateMoveSnapping'; + +import { + isSnapped, + setSnapped, +} from 'diagram-js/lib/features/snapping/SnapUtil'; + +import { is } from '../../util/ModelUtil'; + +import { asTRBL } from 'diagram-js/lib/layout/LayoutUtil'; + +import { getBoundaryAttachment } from './BpmnSnappingUtil'; + +var HIGH_PRIORITY = 1500; + + +/** + * Snap during create and move. + * + * @param {BpmnRules} bpmnRules + * @param {EventBus} eventBus + * @param {Injector} injector + */ +export default function BpmnCreateMoveSnapping(bpmnRules, eventBus, injector) { + injector.invoke(CreateMoveSnapping, this); + + // creating first participant + eventBus.on([ 'create.move', 'create.end' ], HIGH_PRIORITY, setSnappedIfConstrained); + + function canAttach(shape, target, position) { + return bpmnRules.canAttach([ shape ], target, null, position) === 'attach'; + } + + // snap boundary events + eventBus.on([ + 'create.move', + 'create.end', + 'shape.move.move', + 'shape.move.end' + ], HIGH_PRIORITY, function(event) { + var context = event.context, + target = context.target, + shape = context.shape; + + if (target && canAttach(shape, target, event) && !isSnapped(event)) { + snapBoundaryEvent(event, target); + } + }); +} + +inherits(BpmnCreateMoveSnapping, CreateMoveSnapping); + +BpmnCreateMoveSnapping.$inject = [ + 'bpmnRules', + 'eventBus', + 'injector' +]; + +BpmnCreateMoveSnapping.prototype.initSnap = function(event) { + var snapContext = CreateMoveSnapping.prototype.initSnap.call(this, event); + + var shape = event.shape; + + if (is(shape, 'bpmn:Participant')) { + + // snap to borders with higher priority + snapContext.setSnapLocations([ 'top-left', 'bottom-right', 'mid' ]); + } + + return snapContext; +}; + +BpmnCreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target) { + CreateMoveSnapping.prototype.addSnapTargetPoints.call(this, snapPoints, shape, target); + + // add sequence flow parents as snap targets + if (is(target, 'bpmn:SequenceFlow')) { + snapPoints = this.addSnapTargetPoints(snapPoints, shape, target.parent); + } + + return snapPoints; +}; + +BpmnCreateMoveSnapping.prototype.getSnapTargets = function(shape, target) { + return CreateMoveSnapping.prototype.getSnapTargets.call(this, shape, target) + .filter(function(snapTarget) { + + // do not snap to lanes + return !is(snapTarget, 'bpmn:Lane'); + }); +}; + +// helpers ////////// + +function snapBoundaryEvent(event, target) { + var targetTRBL = asTRBL(target); + + var direction = getBoundaryAttachment(event, target); + + if (/top/.test(direction)) { + setSnapped(event, 'y', targetTRBL.top); + } else + if (/bottom/.test(direction)) { + setSnapped(event, 'y', targetTRBL.bottom); + } + + if (/left/.test(direction)) { + setSnapped(event, 'x', targetTRBL.left); + } else + if (/right/.test(direction)) { + setSnapped(event, 'x', targetTRBL.right); + } +} + +function setSnappedIfConstrained(event) { + var context = event.context, + createConstraints = context.createConstraints; + + if (!createConstraints) { + return; + } + + var top = createConstraints.top, + right = createConstraints.right, + bottom = createConstraints.bottom, + left = createConstraints.left; + + if ((left && left >= event.x) || (right && right <= event.x)) { + setSnapped(event, 'x', event.x); + } + + if ((top && top >= event.y) || (bottom && bottom <= event.y)) { + setSnapped(event, 'y', event.y); + } +} \ No newline at end of file diff --git a/lib/features/snapping/BpmnSnapping.js b/lib/features/snapping/BpmnSnapping.js deleted file mode 100644 index 37ba1c73..00000000 --- a/lib/features/snapping/BpmnSnapping.js +++ /dev/null @@ -1,358 +0,0 @@ -import inherits from 'inherits'; - -import { - forEach, - isNumber -} from 'min-dash'; - -import Snapping from 'diagram-js/lib/features/snapping/Snapping'; - -import { is } from '../../util/ModelUtil'; - -import { isAny } from '../modeling/util/ModelingUtil'; - -import { - bottomRight, - isSnapped, - mid, - setSnapped, - topLeft -} from 'diagram-js/lib/features/snapping/SnapUtil'; - -import { asTRBL } from 'diagram-js/lib/layout/LayoutUtil'; - -import { getBoundaryAttachment } from './BpmnSnappingUtil'; - -import { getLanesRoot } from '../modeling/util/LaneUtil'; - -var round = Math.round; - -var HIGH_PRIORITY = 1500; - - -/** - * BPMN-specific snapping. - * - * @param {BpmnRules} bpmnRules - * @param {ElementRegistry} elementRegistry - * @param {EventBus} eventBus - * @param {Injector} injector - */ -export default function BpmnSnapping(bpmnRules, elementRegistry, eventBus, injector) { - injector.invoke(Snapping, this); - - function canAttach(shape, target, position) { - return bpmnRules.canAttach([ shape ], target, null, position) === 'attach'; - } - - function canConnect(source, target) { - return bpmnRules.canConnect(source, target); - } - - // creating first participant - eventBus.on([ 'create.move', 'create.end' ], HIGH_PRIORITY, setSnappedIfConstrained); - - /** - * Snap boundary events to elements border - */ - eventBus.on([ - 'create.move', - 'create.end', - 'shape.move.move', - 'shape.move.end' - ], HIGH_PRIORITY, function(event) { - - var context = event.context, - target = context.target, - shape = context.shape; - - if (target && !isSnapped(event) && canAttach(shape, target, event)) { - snapBoundaryEvent(event, shape, target); - } - }); - - /** - * Adjust parent for flowElements to the target participant - * when droping onto lanes. - */ - eventBus.on([ - 'shape.move.hover', - 'shape.move.move', - 'shape.move.end', - 'create.hover', - 'create.move', - 'create.end' - ], HIGH_PRIORITY, function(event) { - var context = event.context, - shape = context.shape, - hover = event.hover; - - if (is(hover, 'bpmn:Lane') && !isAny(shape, [ 'bpmn:Lane', 'bpmn:Participant' ])) { - event.hover = getLanesRoot(hover); - event.hoverGfx = elementRegistry.getGraphics(event.hover); - } - }); - - /** - * Snap sequence flows. - */ - eventBus.on([ - 'connect.move', - 'connect.hover', - 'connect.end' - ], HIGH_PRIORITY, function(event) { - var context = event.context, - source = context.source, - target = context.target; - - var connection = canConnect(source, target) || {}; - - if (!context.initialSourcePosition) { - context.initialSourcePosition = context.sourcePosition; - } - - if ( - target && ( - connection.type === 'bpmn:Association' || - connection.type === 'bpmn:DataOutputAssociation' || - connection.type === 'bpmn:DataInputAssociation' || - connection.type === 'bpmn:SequenceFlow' - ) - ) { - // snap source - context.sourcePosition = mid(source); - - // snap target - snapToPosition(event, mid(target)); - } else - - if (connection.type === 'bpmn:MessageFlow') { - - if (is(source, 'bpmn:Event')) { - // snap source - context.sourcePosition = mid(source); - } - - if (is(target, 'bpmn:Event')) { - // snap target - snapToPosition(event, mid(target)); - } - } - - else { - // otherwise reset source snap - context.sourcePosition = context.initialSourcePosition; - } - - }); -} - -inherits(BpmnSnapping, Snapping); - -BpmnSnapping.$inject = [ - 'bpmnRules', - 'elementRegistry', - 'eventBus', - 'injector' -]; - -BpmnSnapping.prototype.initSnap = function(event) { - var context = event.context, - shape = event.shape, - shapeMid, - shapeBounds, - shapeTopLeft, - shapeBottomRight, - snapContext; - - snapContext = Snapping.prototype.initSnap.call(this, event); - - if (is(shape, 'bpmn:Participant')) { - - // snap to borders with higher priority - snapContext.setSnapLocations([ 'top-left', 'bottom-right', 'mid' ]); - } - - - if (shape) { - - shapeMid = mid(shape, event); - - shapeBounds = { - width: shape.width, - height: shape.height, - x: isNaN(shape.x) ? round(shapeMid.x - shape.width / 2) : shape.x, - y: isNaN(shape.y) ? round(shapeMid.y - shape.height / 2) : shape.y - }; - - shapeTopLeft = topLeft(shapeBounds); - shapeBottomRight = bottomRight(shapeBounds); - - snapContext.setSnapOrigin('top-left', { - x: shapeTopLeft.x - event.x, - y: shapeTopLeft.y - event.y - }); - - snapContext.setSnapOrigin('bottom-right', { - x: shapeBottomRight.x - event.x, - y: shapeBottomRight.y - event.y - }); - - forEach(shape.outgoing, function(c) { - var docking = c.waypoints[0]; - - docking = docking.original || docking; - - snapContext.setSnapOrigin(c.id + '-docking', { - x: docking.x - event.x, - y: docking.y - event.y - }); - }); - - forEach(shape.incoming, function(c) { - var docking = c.waypoints[c.waypoints.length - 1]; - - docking = docking.original || docking; - - snapContext.setSnapOrigin(c.id + '-docking', { - x: docking.x - event.x, - y: docking.y - event.y - }); - }); - - } - - var source = context.source; - - if (source) { - snapContext.addDefaultSnap('mid', mid(source)); - } -}; - -BpmnSnapping.prototype.addTargetSnaps = function(snapPoints, shape, target) { - - // snap shape to itself - if (isNumber(shape.x) && isNumber(shape.y)) { - snapPoints.add('mid', mid(shape)); - } - - // use target parent as snap target - if (is(shape, 'bpmn:BoundaryEvent') && shape.type !== 'label') { - target = target.parent; - } - - // add sequence flow parents as snap targets - if (is(target, 'bpmn:SequenceFlow')) { - this.addTargetSnaps(snapPoints, shape, target.parent); - } - - var siblings = this.getSiblings(shape, target) || []; - - forEach(siblings, function(sibling) { - - // do not snap to lanes - if (is(sibling, 'bpmn:Lane')) { - return; - } - - if (sibling.waypoints) { - - forEach(sibling.waypoints.slice(1, -1), function(waypoint, i) { - var nextWaypoint = sibling.waypoints[i + 2], - previousWaypoint = sibling.waypoints[i]; - - if (!nextWaypoint || !previousWaypoint) { - throw new Error('waypoints must exist'); - } - - if (nextWaypoint.x === waypoint.x || - nextWaypoint.y === waypoint.y || - previousWaypoint.x === waypoint.x || - previousWaypoint.y === waypoint.y) { - snapPoints.add('mid', waypoint); - } - }); - - return; - } - - snapPoints.add('mid', mid(sibling)); - - if (is(sibling, 'bpmn:Participant')) { - snapPoints.add('top-left', topLeft(sibling)); - snapPoints.add('bottom-right', bottomRight(sibling)); - } - }); - - - forEach(shape.incoming, function(c) { - - if (siblings.indexOf(c.source) === -1) { - snapPoints.add('mid', mid(c.source)); - } - - var docking = c.waypoints[0]; - snapPoints.add(c.id + '-docking', docking.original || docking); - }); - - - forEach(shape.outgoing, function(c) { - - if (siblings.indexOf(c.target) === -1) { - snapPoints.add('mid', mid(c.target)); - } - - var docking = c.waypoints[c.waypoints.length - 1]; - snapPoints.add(c.id + '-docking', docking.original || docking); - }); -}; - -// helpers ////////// - -function snapBoundaryEvent(event, shape, target) { - var targetTRBL = asTRBL(target); - - var direction = getBoundaryAttachment(event, target); - - if (/top/.test(direction)) { - setSnapped(event, 'y', targetTRBL.top); - } else - if (/bottom/.test(direction)) { - setSnapped(event, 'y', targetTRBL.bottom); - } - - if (/left/.test(direction)) { - setSnapped(event, 'x', targetTRBL.left); - } else - if (/right/.test(direction)) { - setSnapped(event, 'x', targetTRBL.right); - } -} - - -function snapToPosition(event, position) { - setSnapped(event, 'x', position.x); - setSnapped(event, 'y', position.y); -} - -function setSnappedIfConstrained(event) { - var context = event.context, - createConstraints = context.createConstraints; - - if (!createConstraints) { - return; - } - - var top = createConstraints.top, - right = createConstraints.right, - bottom = createConstraints.bottom, - left = createConstraints.left; - - if ((left && left >= event.x) || (right && right <= event.x)) { - setSnapped(event, 'x', event.x); - } - - if ((top && top >= event.y) || (bottom && bottom <= event.y)) { - setSnapped(event, 'y', event.y); - } -} \ No newline at end of file diff --git a/lib/features/snapping/index.js b/lib/features/snapping/index.js index 6d729a55..88acf993 100644 --- a/lib/features/snapping/index.js +++ b/lib/features/snapping/index.js @@ -1,6 +1,13 @@ -import BpmnSnapping from './BpmnSnapping'; +import BpmnConnectSnapping from './BpmnConnectSnapping'; +import BpmnCreateMoveSnapping from './BpmnCreateMoveSnapping'; +import SnappingModule from 'diagram-js/lib/features/snapping'; export default { - __init__: [ 'snapping' ], - snapping: [ 'type', BpmnSnapping ] + __depends__: [ SnappingModule ], + __init__: [ + 'connectSnapping', + 'createMoveSnapping' + ], + connectSnapping: [ 'type', BpmnConnectSnapping ], + createMoveSnapping: [ 'type', BpmnCreateMoveSnapping ] }; \ No newline at end of file diff --git a/test/spec/features/snapping/BpmnConnectSnapping.bpmn b/test/spec/features/snapping/BpmnConnectSnapping.bpmn new file mode 100644 index 00000000..a48219e3 --- /dev/null +++ b/test/spec/features/snapping/BpmnConnectSnapping.bpmn @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/snapping/BpmnConnectSnappingSpec.js b/test/spec/features/snapping/BpmnConnectSnappingSpec.js new file mode 100644 index 00000000..e14c9e04 --- /dev/null +++ b/test/spec/features/snapping/BpmnConnectSnappingSpec.js @@ -0,0 +1,212 @@ +import { + bootstrapModeler, + inject +} from 'test/TestHelper'; + +import connectModule from 'diagram-js/lib/features/connect'; +import coreModule from 'lib/core'; +import globalConnectModule from 'diagram-js/lib/features/global-connect'; +import modelingModule from 'lib/features/modeling'; +import rulesModule from 'lib/features/rules'; +import snappingModule from 'lib/features/snapping'; + +import { createCanvasEvent as canvasEvent } from '../../../util/MockEvents'; + + +describe('features/snapping - BpmnConnectSnapping', function() { + + var testModules = [ + connectModule, + coreModule, + globalConnectModule, + modelingModule, + rulesModule, + snappingModule + ]; + + var diagramXML = require('./BpmnConnectSnapping.bpmn'); + + beforeEach(bootstrapModeler(diagramXML, { + modules: testModules + })); + + beforeEach(inject(function(dragging) { + dragging.setOptions({ manual: true }); + })); + + + describe('association', function() { + + describe('connect', function() { + + it('should snap target', inject(function(connect, dragging, elementRegistry) { + + // given + var task = elementRegistry.get('Task_1'), + dataObjectReference = elementRegistry.get('DataObjectReference_1'), + dataObjectReferenceGfx = elementRegistry.getGraphics(dataObjectReference); + + // when + connect.start(canvasEvent({ x: 300, y: 300 }), task); + + dragging.hover({ element: dataObjectReference, gfx: dataObjectReferenceGfx }); + + dragging.move(canvasEvent({ x: 410, y: 410 })); + + dragging.end(); + + // then + var waypoints = task.outgoing[0].waypoints; + + expect(waypoints[1].original).to.eql({ x: 400, y: 400 }); + })); + + }); + + }); + + + describe('sequence flow', function() { + + describe('connect', function() { + + it('should snap target', inject(function(connect, dragging, elementRegistry) { + + // given + var startEvent = elementRegistry.get('StartEvent_1'), + endEvent = elementRegistry.get('EndEvent_1'), + endEventGfx = elementRegistry.getGraphics(endEvent); + + // when + connect.start(canvasEvent({ x: 100, y: 100 }), startEvent); + + dragging.hover({ element: endEvent, gfx: endEventGfx }); + + dragging.move(canvasEvent({ x: 210, y: 210 })); + + dragging.end(); + + // then + var waypoints = startEvent.outgoing[0].waypoints; + + expect(waypoints[3].original).to.eql({ x: 200, y: 200 }); + })); + + }); + + + describe('global connect', function() { + + it('should snap target', inject( + function(connect, dragging, elementRegistry, eventBus) { + + // given + var startEvent = elementRegistry.get('StartEvent_1'), + endEvent = elementRegistry.get('EndEvent_1'), + endEventGfx = elementRegistry.getGraphics(endEvent); + + // when + connect.start(null, startEvent, { x: 110, y: 110 }); + + dragging.hover({ element: endEvent, gfx: endEventGfx }); + + dragging.move(canvasEvent({ x: 210, y: 210 })); + + dragging.end(); + + // then + var waypoints = startEvent.outgoing[0].waypoints; + + expect(waypoints[0].original).to.eql({ x: 100, y: 100 }); + expect(waypoints[3].original).to.eql({ x: 200, y: 200 }); + } + )); + + }); + + }); + + + describe('message flow', function() { + + describe('connect', function() { + + it('should snap target', inject(function(connect, dragging, elementRegistry) { + + // given + var task = elementRegistry.get('Task_1'), + intermediateCatchEvent = elementRegistry.get('IntermediateCatchEvent_1'), + intermediateCatchEventGfx = elementRegistry.getGraphics(intermediateCatchEvent); + + // when + connect.start(canvasEvent({ x: 300, y: 300 }), task); + + dragging.hover({ element: intermediateCatchEvent, gfx: intermediateCatchEventGfx }); + + dragging.move(canvasEvent({ x: 210, y: 610 })); + + dragging.end(); + + // then + var waypoints = task.outgoing[0].waypoints; + + expect(waypoints[3].original).to.eql({ x: 200, y: 600 }); + })); + + }); + + + describe('global connect', function() { + + it('should snap source', inject(function(connect, dragging, elementRegistry) { + + // given + var intermediateThrowEvent = elementRegistry.get('IntermediateThrowEvent_1'), + task = elementRegistry.get('Task_1'), + taskGfx = elementRegistry.getGraphics(task); + + // when + connect.start(null, intermediateThrowEvent, { x: 110, y: 610 }); + + dragging.hover({ element: task, gfx: taskGfx }); + + dragging.move(canvasEvent({ x: 310, y: 310 })); + + dragging.end(); + + // then + var waypoints = intermediateThrowEvent.outgoing[0].waypoints; + + expect(waypoints[0].original).to.eql({ x: 100, y: 600 }); + expect(waypoints[3].original).to.eql({ x: 310, y: 310 }); // NOT snapped + })); + + + it('should snap target', inject(function(connect, dragging, elementRegistry) { + + // given + var task = elementRegistry.get('Task_1'), + intermediateCatchEvent = elementRegistry.get('IntermediateCatchEvent_1'), + intermediateCatchEventGfx = elementRegistry.getGraphics(intermediateCatchEvent); + + // when + connect.start(null, task, { x: 310, y: 310 }); + + dragging.hover({ element: intermediateCatchEvent, gfx: intermediateCatchEventGfx }); + + dragging.move(canvasEvent({ x: 210, y: 610 })); + + dragging.end(); + + // then + var waypoints = task.outgoing[0].waypoints; + + expect(waypoints[0].original).to.eql({ x: 310, y: 310 }); // NOT snapped + expect(waypoints[3].original).to.eql({ x: 200, y: 600 }); + })); + + }); + + }); + +}); \ No newline at end of file diff --git a/test/spec/features/snapping/BpmnCreateMoveSnapping.boundary-events.bpmn b/test/spec/features/snapping/BpmnCreateMoveSnapping.boundary-events.bpmn new file mode 100644 index 00000000..5531f289 --- /dev/null +++ b/test/spec/features/snapping/BpmnCreateMoveSnapping.boundary-events.bpmn @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/snapping/BpmnCreateMoveSnapping.collaboration.bpmn b/test/spec/features/snapping/BpmnCreateMoveSnapping.collaboration.bpmn new file mode 100644 index 00000000..71caa3f2 --- /dev/null +++ b/test/spec/features/snapping/BpmnCreateMoveSnapping.collaboration.bpmn @@ -0,0 +1,31 @@ + + + + + + + + + Task_1 + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/snapping/BpmnCreateMoveSnapping.process.bpmn b/test/spec/features/snapping/BpmnCreateMoveSnapping.process.bpmn new file mode 100644 index 00000000..45dd78ba --- /dev/null +++ b/test/spec/features/snapping/BpmnCreateMoveSnapping.process.bpmn @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/test/spec/features/snapping/BpmnCreateMoveSnapping.sequence-flows.bpmn b/test/spec/features/snapping/BpmnCreateMoveSnapping.sequence-flows.bpmn new file mode 100644 index 00000000..2e1b468e --- /dev/null +++ b/test/spec/features/snapping/BpmnCreateMoveSnapping.sequence-flows.bpmn @@ -0,0 +1,30 @@ + + + + + SequenceFlow_1 + + + SequenceFlow_1 + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/snapping/BpmnCreateMoveSnappingSpec.js b/test/spec/features/snapping/BpmnCreateMoveSnappingSpec.js new file mode 100644 index 00000000..0d020554 --- /dev/null +++ b/test/spec/features/snapping/BpmnCreateMoveSnappingSpec.js @@ -0,0 +1,381 @@ +import { + bootstrapModeler, + inject +} from 'test/TestHelper'; + +import TestContainer from 'mocha-test-container-support'; + +import coreModule from 'lib/core'; +import createModule from 'diagram-js/lib/features/create'; +import modelingModule from 'lib/features/modeling'; +import moveModule from 'diagram-js/lib/features/move'; +import rulesModule from 'lib/features/rules'; +import snappingModule from 'lib/features/snapping'; + +import { + isSnapped, + mid +} from 'diagram-js/lib/features/snapping/SnapUtil'; + +import { createCanvasEvent as canvasEvent } from '../../../util/MockEvents'; + +import { queryAll as domQueryAll } from 'min-dom'; + +import { attr as svgAttr } from 'tiny-svg'; + + +describe('features/snapping - BpmnCreateMoveSnapping', function() { + + var testModules = [ + coreModule, + createModule, + modelingModule, + moveModule, + rulesModule, + snappingModule + ]; + + + describe('create participant', function() { + + describe('process', function() { + + var diagramXML = require('./BpmnCreateMoveSnapping.process.bpmn'); + + + it('should snap particpant if constrained', function(done) { + + bootstrapModeler(diagramXML, { + container: TestContainer.get(this), + modules: testModules + })(function() { + + // when + inject(function(canvas, create, dragging, elementFactory, eventBus) { + + // given + dragging.setOptions({ manual: true }); + + var participantShape = elementFactory.createParticipantShape(false), + rootElement = canvas.getRootElement(), + rootGfx = canvas.getGraphics(rootElement); + + create.start(canvasEvent({ x: 0, y: 0 }), participantShape); + + dragging.hover({ element: rootElement, gfx: rootGfx }); + + eventBus.once('create.move', function(event) { + + // then + // expect snapped to avoid snapping outside of constraints + expect(isSnapped(event)).to.be.true; + + done(); + }); + + // when + dragging.move(canvasEvent({ x: 1000, y: 1000 })); + })(); + + }); + + }); + + }); + + + describe('collaboration', function() { + + var diagramXML = require('./BpmnCreateMoveSnapping.collaboration.bpmn'); + + + it('should snap to participant border with higher priority', function(done) { + + var container = TestContainer.get(this); + + bootstrapModeler(diagramXML, { + container: container, + modules: testModules + })(function() { + + // when + inject(function(create, dragging, elementFactory, elementRegistry, eventBus) { + + // given + dragging.setOptions({ manual: true }); + + var participant = elementFactory.createParticipantShape(false), + collaboration = elementRegistry.get('Collaboration_1'), + collaborationGfx = elementRegistry.getGraphics(collaboration); + + create.start(canvasEvent({ x: 0, y: 0 }), participant); + + dragging.hover({ element: collaboration, gfx: collaborationGfx }); + + dragging.move(canvasEventTopLeft({ x: 0, y: 0 }, participant)); + + eventBus.once('create.move', function(event) { + + // then + // expect snap line at left border of participant + expect(svgAttr(domQueryAll('.djs-snap-line', container)[1], 'd')) + .to.equal('M 100,-100000 L 100, +100000'); + + done(); + }); + + // when + dragging.move(canvasEventTopLeft({ x: 95, y: 400 }, participant)); + })(); + + }); + + }); + + }); + + }); + + + describe('boundary events', function() { + + describe('creating boundary event', function() { + + var diagramXML = require('./BpmnCreateMoveSnapping.process.bpmn'); + + beforeEach(bootstrapModeler(diagramXML, { + modules: testModules + })); + + var task, taskGfx, intermediateThrowEvent; + + beforeEach(inject(function(create, dragging, elementRegistry, elementFactory) { + task = elementRegistry.get('Task_1'); + taskGfx = elementRegistry.getGraphics(task); + + intermediateThrowEvent = elementFactory.createShape({ + type: 'bpmn:IntermediateThrowEvent' + }); + + create.start(canvasEvent({ x: 0, y: 0 }), intermediateThrowEvent); + + dragging.hover({ element: task, gfx: taskGfx }); + + dragging.setOptions({ manual: false }); + })); + + + it('should snap to top', inject(function(dragging) { + + // when + dragging.move(canvasEvent({ x: 150, y: 95 })); + + dragging.end(); + + // then + var boundaryEvent = getBoundaryEvent(task); + + expect(mid(boundaryEvent)).to.eql({ + x: 150, + y: 100 // 95 snapped to 100 + }); + })); + + + it('should snap to right', inject(function(dragging) { + + // when + dragging.move(canvasEvent({ x: 195, y: 140 })); + + dragging.end(); + + // then + var boundaryEvent = getBoundaryEvent(task); + + expect(mid(boundaryEvent)).to.eql({ + x: 200, // 195 snapped to 200 + y: 140 + }); + })); + + + it('should snap to bottom', inject(function(dragging) { + + // when + dragging.move(canvasEvent({ x: 150, y: 175 })); + + dragging.end(); + + // then + var boundaryEvent = getBoundaryEvent(task); + + expect(mid(boundaryEvent)).to.eql({ + x: 150, + y: 180 // 175 snapped to 180 + }); + })); + + + it('should snap to left', inject(function(dragging) { + + // when + dragging.move(canvasEvent({ x: 95, y: 140 })); + + dragging.end(); + + // then + var boundaryEvent = getBoundaryEvent(task); + + expect(mid(boundaryEvent)).to.eql({ + x: 100, // 95 snapped to 100 + y: 140 + }); + })); + + }); + + + describe('snapping to boundary events', function() { + + var diagramXML = require('./BpmnCreateMoveSnapping.boundary-events.bpmn'); + + beforeEach(bootstrapModeler(diagramXML, { + modules: testModules + })); + + var task; + + beforeEach(inject(function(dragging, elementRegistry, move) { + task = elementRegistry.get('Task_1'); + + var process = elementRegistry.get('Process_1'), + processGfx = elementRegistry.getGraphics(process); + + dragging.setOptions({ manual: true }); + + move.start(canvasEventTopLeft({ x: 100, y: 400 }, task), task, true); + + dragging.hover({ element: process, gfx: processGfx }); + + dragging.move(canvasEventTopLeft({ x: 100, y: 400 }, task)); + })); + + + it('should snap to boundary events', inject(function(dragging) { + + // when + dragging.move(canvasEventTopLeft({ x: 245, y: 400 }, task)); + + dragging.end(); + + // then + expect(task).to.have.bounds({ + x: 250, // 245 snapped to 250 + y: 400, + width: 100, + height: 80 + }); + })); + + }); + + }); + + + describe('sequence flows', function() { + + var diagramXML = require('./BpmnCreateMoveSnapping.sequence-flows.bpmn'); + + beforeEach(bootstrapModeler(diagramXML, { + modules: testModules + })); + + var sequenceFlow, sequenceFlowGfx, task; + + beforeEach(inject(function(create, dragging, elementRegistry, elementFactory) { + sequenceFlow = elementRegistry.get('SequenceFlow_1'); + sequenceFlowGfx = elementRegistry.getGraphics(sequenceFlow); + + task = elementFactory.createShape({ + type: 'bpmn:Task' + }); + + create.start(canvasEvent({ x: 0, y: 0 }), task); + + dragging.hover({ element: sequenceFlow, gfx: sequenceFlowGfx }); + + dragging.setOptions({ manual: false }); + })); + + + it('should add snap targets of sequence flow parent', inject(function(dragging) { + + // when + dragging.move(canvasEventTopLeft({ x: 195, y: 60 }, task)); + + dragging.end(); + + // then + expect(task).to.have.bounds({ + x: 200, // 195 snapped to 200 + y: 60, + width: 100, + height: 80 + }); + })); + + }); + + + describe('lanes', function() { + + var diagramXML = require('./BpmnCreateMoveSnapping.collaboration.bpmn'); + + beforeEach(bootstrapModeler(diagramXML, { + modules: testModules + })); + + var task; + + beforeEach(inject(function(dragging, elementRegistry, move) { + task = elementRegistry.get('Task_1'); + + dragging.setOptions({ manual: false }); + + move.start(canvasEvent({ x: 200, y: 165 }), task); + })); + + + it('should should NOT snap to lanes', inject(function(dragging) { + + // when + // lane mid is { x: 415, y: 162.5 } + dragging.move(canvasEvent({ x: 410, y: 160 })); + + dragging.end(); + + // then + expect(task).to.have.bounds({ + x: 360, + y: 120, + width: 100, + height: 80 + }); + })); + + }); + +}); + +// helpers ////////// + +function canvasEventTopLeft(position, shape) { + return canvasEvent({ + x: position.x + shape.width / 2, + y: position.y + shape.height / 2 + }); +} + +function getBoundaryEvent(element) { + return element.attachers[0]; +} \ No newline at end of file diff --git a/test/spec/features/snapping/BpmnSnapping.connect.bpmn b/test/spec/features/snapping/BpmnSnapping.connect.bpmn deleted file mode 100644 index b01518ba..00000000 --- a/test/spec/features/snapping/BpmnSnapping.connect.bpmn +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/spec/features/snapping/BpmnSnapping.general.bpmn b/test/spec/features/snapping/BpmnSnapping.general.bpmn deleted file mode 100644 index 0f48812e..00000000 --- a/test/spec/features/snapping/BpmnSnapping.general.bpmn +++ /dev/null @@ -1,38 +0,0 @@ - - - - - SequenceFlow_1 - - - SequenceFlow_1 - SequenceFlow_2 - - - - SequenceFlow_2 - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/spec/features/snapping/BpmnSnapping.labels.bpmn b/test/spec/features/snapping/BpmnSnapping.labels.bpmn deleted file mode 100644 index b36c2bf5..00000000 --- a/test/spec/features/snapping/BpmnSnapping.labels.bpmn +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/spec/features/snapping/BpmnSnapping.sequenceFlow.bpmn b/test/spec/features/snapping/BpmnSnapping.sequenceFlow.bpmn deleted file mode 100644 index 9e81382b..00000000 --- a/test/spec/features/snapping/BpmnSnapping.sequenceFlow.bpmn +++ /dev/null @@ -1,41 +0,0 @@ - - - - - SequenceFlow_1 - - - SequenceFlow_1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/spec/features/snapping/BpmnSnappingSpec.js b/test/spec/features/snapping/BpmnSnappingSpec.js deleted file mode 100644 index 86bbd650..00000000 --- a/test/spec/features/snapping/BpmnSnappingSpec.js +++ /dev/null @@ -1,946 +0,0 @@ -import { - bootstrapModeler, - inject -} from 'test/TestHelper'; - -import TestContainer from 'mocha-test-container-support'; - -import { - createCanvasEvent as canvasEvent -} from '../../../util/MockEvents'; - -import coreModule from 'lib/core'; -import snappingModule from 'lib/features/snapping'; -import modelingModule from 'lib/features/modeling'; -import createModule from 'diagram-js/lib/features/create'; -import moveModule from 'diagram-js/lib/features/move'; -import rulesModule from 'lib/features/rules'; -import connectModule from 'diagram-js/lib/features/connect'; - -import { isSnapped } from 'diagram-js/lib/features/snapping/SnapUtil'; - - -describe('features/snapping - BpmnSnapping', function() { - - var testModules = [ - coreModule, - snappingModule, - modelingModule, - createModule, - rulesModule, - moveModule, - connectModule - ]; - - describe('on itself', function() { - - var diagramXML = require('./BpmnSnapping.general.bpmn'); - - beforeEach(bootstrapModeler(diagramXML, { - modules: testModules - })); - - var task; - - beforeEach(inject(function(dragging, elementRegistry) { - task = elementRegistry.get('Task_1'); - - dragging.setOptions({ manual: true }); - })); - - - it('should snap to itself', inject(function(canvas, dragging, move) { - - // given - var originalPosition = { - x: task.x, - y: task.y - }; - - var taskGfx = canvas.getGraphics(task); - - // when - move.start(canvasEvent(originalPosition), task); - - dragging.hover({ element: task, gfx: taskGfx }); - - dragging.move(canvasEvent({ - x: originalPosition.x + 5, - y: originalPosition.y + 5 - })); - - dragging.end(); - - // then - expect(task.x).to.eql(originalPosition.x); - expect(task.y).to.eql(originalPosition.y); - })); - - }); - - - describe('on Boundary Events', function() { - - var diagramXML = require('../../../fixtures/bpmn/collaboration/process.bpmn'); - - beforeEach(bootstrapModeler(diagramXML, { - modules: testModules - })); - - var task, intermediateThrowEvent; - - beforeEach(inject(function(elementFactory, elementRegistry, dragging) { - task = elementRegistry.get('Task_1'); - - intermediateThrowEvent = elementFactory.createShape({ - type: 'bpmn:IntermediateThrowEvent' - }); - - dragging.setOptions({ manual: true }); - })); - - afterEach(inject(function(dragging) { - dragging.setOptions({ manual: false }); - })); - - - it('should snap on create to the bottom', - inject(function(canvas, create, dragging, elementRegistry) { - - // given - var taskGfx = canvas.getGraphics(task); - - // when - create.start(canvasEvent({ x: 0, y: 0 }), intermediateThrowEvent); - - dragging.hover({ element: task, gfx: taskGfx }); - - dragging.move(canvasEvent({ x: 382, y: 170 })); - dragging.end(); - - var boundaryEvent = elementRegistry.get(task.attachers[0].id); - - // then - expect(boundaryEvent).to.have.bounds({ x: 364, y: 167, width: 36, height: 36 }); - }) - ); - - - it('should snap on create to the left', - inject(function(canvas, create, dragging, elementRegistry) { - - // given - var taskGfx = canvas.getGraphics(task); - - // when - create.start(canvasEvent({ x: 0, y: 0 }), intermediateThrowEvent); - - dragging.hover({ element: task, gfx: taskGfx }); - - dragging.move(canvasEvent({ x: 382, y: 115 })); - dragging.end(); - - var boundaryEvent = elementRegistry.get(task.attachers[0].id); - - // then - expect(boundaryEvent).to.have.bounds({ x: 364, y: 87, width: 36, height: 36 }); - }) - ); - - }); - - - describe('on Sequence Flows', function() { - - var diagramXML = require('./BpmnSnapping.sequenceFlow.bpmn'); - - beforeEach(bootstrapModeler(diagramXML, { - modules: testModules - })); - - var sequenceFlow, task; - - beforeEach(inject(function(elementFactory, elementRegistry, dragging) { - sequenceFlow = elementRegistry.get('SequenceFlow_1'); - - task = elementFactory.createShape({ - type: 'bpmn:Task' - }); - - dragging.setOptions({ manual: true }); - })); - - afterEach(inject(function(dragging) { - dragging.setOptions({ manual: false }); - })); - - - it('should snap vertically', - inject(function(canvas, create, dragging) { - - // given - var sequenceFlowGfx = canvas.getGraphics(sequenceFlow); - - // when - create.start(canvasEvent({ x: 0, y: 0 }), task); - - dragging.hover({ element: sequenceFlow, gfx: sequenceFlowGfx }); - - dragging.move(canvasEvent({ x: 300, y: 245 })); - dragging.end(); - - // then - expect(task.x).to.eql(254); - }) - ); - - - it('should snap horizontally', - inject(function(canvas, create, dragging) { - - // given - var sequenceFlowGfx = canvas.getGraphics(sequenceFlow); - - // when - create.start(canvasEvent({ x: 0, y: 0 }), task); - - dragging.hover({ element: sequenceFlow, gfx: sequenceFlowGfx }); - - dragging.move(canvasEvent({ x: 410, y: 320 })); - dragging.end(); - - // then - expect(task.y).to.eql(285); - }) - ); - - - it('should NOT snap shape to first waypoint', - inject(function(canvas, elementRegistry, move, dragging) { - - // given - var shape = elementRegistry.get('StartEvent_1'); - - var originalPosition = { - x: shape.x, - y: shape.y - }; - - var rootElement = canvas.getRootElement(); - - // when - move.start(canvasEvent(originalPosition), shape); - - dragging.hover({ - element: rootElement, - gfx: canvas.getGraphics(rootElement) - }); - - dragging.move(canvasEvent({ - x: originalPosition.x + 10, - y: originalPosition.y - })); - - dragging.move(canvasEvent({ - x: originalPosition.x + 20, - y: originalPosition.y - 5 - })); - - dragging.end(); - - // then - // no horizontal snap - expect(shape.x).to.eql(originalPosition.x + 20); - - // vertical snap though! - expect(shape.y).to.eql(originalPosition.y); - }) - ); - - - it('should NOT snap shape to last waypoint', - inject(function(canvas, elementRegistry, move, dragging) { - - // given - var shape = elementRegistry.get('Task_1'); - - var originalPosition = { - x: shape.x, - y: shape.y - }; - - var rootElement = canvas.getRootElement(); - - // when - move.start(canvasEvent(originalPosition), shape); - - dragging.hover({ - element: rootElement, - gfx: canvas.getGraphics(rootElement) - }); - - dragging.move(canvasEvent({ - x: originalPosition.x, - y: originalPosition.y - 10 - })); - - dragging.move(canvasEvent({ - x: originalPosition.x + 5, - y: originalPosition.y - 20 - })); - - dragging.end(); - - // then - // no vertical snap - expect(shape.y).to.eql(originalPosition.y - 20); - - // horizontal snap though! - expect(shape.x).to.eql(originalPosition.x); - }) - ); - }); - - - describe('on Participant create', function() { - - describe('in non-empty process', function() { - - var diagramXML = require('../../../fixtures/bpmn/collaboration/process.bpmn'); - - it('should set snapped if outside of constraints', function(done) { - - bootstrapModeler(diagramXML, { - container: TestContainer.get(this), - modules: testModules - })(function() { - - // when - inject(function(canvas, create, dragging, elementFactory, eventBus) { - - // given - dragging.setOptions({ manual: true }); - - var participantShape = elementFactory.createParticipantShape(false), - rootElement = canvas.getRootElement(), - rootGfx = canvas.getGraphics(rootElement); - - create.start(canvasEvent({ x: 50, y: 50 }), participantShape); - - dragging.hover({ element: rootElement, gfx: rootGfx }); - - eventBus.once('create.move', function(event) { - - // then - // expect snapped to avoid snapping outside of constraints - expect(isSnapped(event)).to.be.true; - - done(); - }); - - // when - dragging.move(canvasEvent({ x: 0, y: 0 })); - })(); - - }); - - }); - - }); - - - describe('in empty process', function() { - - var diagramXML = require('../../../fixtures/bpmn/collaboration/process-empty.bpmn'); - - beforeEach(bootstrapModeler(diagramXML, { - modules: testModules - })); - - - beforeEach(inject(function(dragging) { - dragging.setOptions({ manual: true }); - })); - - - it('should not snap', inject(function(canvas, create, dragging, elementFactory) { - - // given - var participantShape = elementFactory.createParticipantShape(false), - rootElement = canvas.getRootElement(), - rootGfx = canvas.getGraphics(rootElement); - - // when - create.start(canvasEvent({ x: 50, y: 50 }), participantShape); - - dragging.hover({ element: rootElement, gfx: rootGfx }); - - dragging.move(canvasEvent({ x: 400, y: 400 })); - dragging.end(canvasEvent({ x: 400, y: 400 })); - - // then - expect(participantShape).to.have.bounds({ - x: 100, y: 275, width: 600, height: 250 - }); - })); - - }); - - - describe('in collaboration', function() { - - var diagramXML = require('../../../fixtures/bpmn/collaboration/collaboration-participant.bpmn'); - - beforeEach(bootstrapModeler(diagramXML, { - modules: testModules - })); - - beforeEach(inject(function(dragging) { - dragging.setOptions({ manual: true }); - })); - - - it('should not snap to diagram contents', inject(function(canvas, create, dragging, elementFactory) { - - // given - var participantShape = elementFactory.createParticipantShape(false), - rootElement = canvas.getRootElement(), - rootGfx = canvas.getGraphics(rootElement); - - // when - create.start(canvasEvent({ x: 50, y: 50 }), participantShape); - - dragging.hover({ element: rootElement, gfx: rootGfx }); - - dragging.move(canvasEvent({ x: 400, y: 400 })); - dragging.end(canvasEvent({ x: 400, y: 400 })); - - // then - expect(participantShape).to.have.bounds({ - x: 100, y: 275, width: 600, height: 250 - }); - })); - - - it('should snap to participant border', inject(function(canvas, create, dragging, elementFactory) { - - // given - var participantShape = elementFactory.createParticipantShape(false), - rootElement = canvas.getRootElement(), - rootGfx = canvas.getGraphics(rootElement); - - // when - create.start(canvasEvent({ x: 50, y: 50 }), participantShape); - - dragging.hover({ element: rootElement, gfx: rootGfx }); - - dragging.move(canvasEvent({ x: 390, y: 400 })); - dragging.end(canvasEvent({ x: 390, y: 400 })); - - // then - expect(participantShape).to.have.bounds({ - x: 84, y: 275, width: 600, height: 250 - }); - })); - - }); - - }); - - - describe('labels', function() { - - var diagramXML = require('./BpmnSnapping.labels.bpmn'); - - beforeEach(bootstrapModeler(diagramXML, { modules: testModules })); - - - it('should snap to start events', inject(function(canvas, elementRegistry, move, dragging) { - - var label = elementRegistry.get('StartEvent_1_label'), - rootElement = canvas.getRootElement(); - - var originalPosition = { x: label.x, y: label.y }; - - move.start(canvasEvent({ x: label.x + 3, y: label.y + 3 }), label); - - dragging.hover({ - element: rootElement, - gfx: elementRegistry.getGraphics(rootElement) - }); - - dragging.move(canvasEvent({ x: label.x + 4, y: label.y + 40 })); - dragging.move(canvasEvent({ x: label.x + 4, y: label.y + 40 })); - - dragging.end(); - - expect(label.x).to.be.within(originalPosition.x, originalPosition.x + 1); - })); - - - it('should snap to boundary events', inject(function(canvas, elementRegistry, move, dragging) { - - var label = elementRegistry.get('BoundaryEvent_1_label'), - rootElement = canvas.getRootElement(); - - var originalPosition = { x: label.x, y: label.y }; - - move.start(canvasEvent({ x: label.x + 2, y: label.y + 2 }), label); - - dragging.hover({ - element: rootElement, - gfx: elementRegistry.getGraphics(rootElement) - }); - - dragging.move(canvasEvent({ x: label.x + 4, y: label.y + 40 })); - dragging.move(canvasEvent({ x: label.x + 4, y: label.y + 40 })); - - dragging.end(); - - expect(label.x).to.be.within(originalPosition.x, originalPosition.x + 1); - })); - - - it('should snap to siblings', inject(function(canvas, elementRegistry, move, dragging) { - - var label = elementRegistry.get('BoundaryEvent_1_label'), - task = elementRegistry.get('Task_1'), - rootElement = canvas.getRootElement(); - - move.start(canvasEvent({ x: label.x + 2, y: label.y + 2 }), label); - - dragging.hover({ - element: rootElement, - gfx: elementRegistry.getGraphics(rootElement) - }); - - dragging.move(canvasEvent({ x: label.x-23, y: label.y+40 })); - dragging.move(canvasEvent({ x: label.x-23, y: label.y+40 })); - - dragging.end(); - - var labelCenterX = label.x + Math.ceil(label.width / 2), - taskCenterX = task.x + Math.ceil(task.width / 2); - - expect(labelCenterX).to.equal(taskCenterX); - })); - - }); - - - describe('on connect', function() { - - var diagramXML = require('./BpmnSnapping.connect.bpmn'); - - beforeEach(bootstrapModeler(diagramXML, { modules: testModules })); - - - describe('sequence flow', function() { - - it('should snap on global connect', inject(function(connect, dragging, elementRegistry) { - - // given - var startEvent = elementRegistry.get('StartEvent_1'), - task = elementRegistry.get('Task_1'); - - var mid = { - x: startEvent.x + startEvent.width / 2, - y: startEvent.y + startEvent.height / 2 - }; - - // when - connect.start(canvasEvent({ x: mid.x + 10, y: mid.y + 10 }), startEvent); - - dragging.hover({ - element: task, - gfx: elementRegistry.getGraphics(task) - }); - - dragging.move(canvasEvent({ - x: task.x + task.width / 2, - y: task.y + task.height / 2 - })); - - dragging.end(); - - // then - var expected = [ - { - original: - { - x: startEvent.x + startEvent.width / 2, - y: startEvent.y + startEvent.height / 2 - }, - x: startEvent.x + startEvent.width, - y: startEvent.y + startEvent.height / 2 - }, - { - original: - { - x: task.x + task.width / 2, - y: task.y + task.height / 2 - }, - x: task.x, - y: task.y + task.height / 2 - } - ]; - - expect(startEvent.outgoing[0].waypoints).to.eql(expected); - - })); - - - it('should snap on connect', inject(function(connect, dragging, elementRegistry) { - - // given - var startEvent = elementRegistry.get('StartEvent_1'), - task = elementRegistry.get('Task_1'); - - var mid = { x: task.x + task.width / 2, y: task.y + task.height / 2 }; - - // when - connect.start(canvasEvent({ x: 0, y: 0 }), startEvent); - - dragging.hover({ - element: task, - gfx: elementRegistry.getGraphics(task) - }); - - dragging.move(canvasEvent({ x: mid.x + 10, y: mid.y + 10 })); - - dragging.end(); - - // then - var expected = [ - { - original: - { - x: startEvent.x + startEvent.width / 2, - y: startEvent.y + startEvent.height / 2 - }, - x: startEvent.x + startEvent.width, - y: startEvent.y + startEvent.height / 2 - }, - { - original: - { - x: task.x + task.width / 2, - y: task.y + task.height / 2 - }, - x: task.x, - y: task.y + task.height / 2 - } - ]; - - expect(startEvent.outgoing[0].waypoints).to.eql(expected); - - })); - - }); - - - it('should snap data output association', inject(function(connect, dragging, elementRegistry) { - - // given - var startEvent = elementRegistry.get('StartEvent_1'), - dataObjectReference = elementRegistry.get('DataObjectReference_1'); - - var mid = { x: dataObjectReference.x + dataObjectReference.width / 2, y: dataObjectReference.y + dataObjectReference.height / 2 }; - - // when - connect.start(canvasEvent({ x: 0, y: 0 }), startEvent); - - dragging.hover({ - element: dataObjectReference, - gfx: elementRegistry.getGraphics(dataObjectReference) - }); - - dragging.move(canvasEvent({ x: mid.x + 10, y: mid.y + 10 })); - - dragging.end(); - - // then - var expected = [ - { - original: - { - x: startEvent.x + startEvent.width / 2, - y: startEvent.y + startEvent.height / 2 - }, - x: startEvent.x + startEvent.width, - y: startEvent.y + startEvent.height / 2 - }, - { - original: - { - x: dataObjectReference.x + dataObjectReference.width / 2, - y: dataObjectReference.y + dataObjectReference.height / 2 - }, - x: dataObjectReference.x, - y: dataObjectReference.y + dataObjectReference.height / 2 - } - ]; - - expect(startEvent.outgoing[0].waypoints).to.eql(expected); - - })); - - - it('should snap data input association', inject(function(connect, dragging, elementRegistry) { - - // given - var dataStoreReference = elementRegistry.get('DataStoreReference_1'), - task = elementRegistry.get('Task_2'); - - var mid = { x: task.x + task.width / 2, y: task.y + task.height / 2 }; - - // when - connect.start(canvasEvent({ x: 0, y: 0 }), dataStoreReference); - - dragging.hover({ - element: task, - gfx: elementRegistry.getGraphics(task) - }); - - dragging.move(canvasEvent({ x: mid.x + 10, y: mid.y + 10 })); - - dragging.end(); - - // then - var expected = [ - { - original: - { - x: dataStoreReference.x + dataStoreReference.width / 2, - y: dataStoreReference.y + dataStoreReference.height / 2 - }, - x: dataStoreReference.x + dataStoreReference.width, - y: dataStoreReference.y + dataStoreReference.height / 2 - }, - { - original: - { - x: task.x + task.width / 2, - y: task.y + task.height / 2 - }, - x: task.x, - y: task.y + task.height / 2 - } - ]; - - expect(dataStoreReference.outgoing[0].waypoints).to.eql(expected); - - })); - - - describe('message flow', function() { - - it('should NOT snap Task -> Task on global connect', inject(function(connect, dragging, elementRegistry) { - - // given - var task1 = elementRegistry.get('Task_1'), - task2 = elementRegistry.get('Task_2'); - - var task1Mid = { x: task1.x + task1.width / 2, y: task1.y + task1.height / 2 }, - task2Mid = { x: task2.x + task2.width / 2, y: task2.y + task2.height / 2 }; - - // when - connect.start(null, task1, { x: 320, y: task1Mid.y + 20 }); - - dragging.hover({ - element: task2, - gfx: elementRegistry.getGraphics(task2) - }); - - dragging.move(canvasEvent({ - x: 320, - y: task2Mid.y - 20 - })); - - dragging.end(); - - // then - var expected = [ - { - original: - { - x: 320, - y: task1Mid.y + 20 - }, - x: 320, - y: task1.y + task1.height - }, - { - original: - { - x: 320, - y: task2Mid.y - 20 - }, - x: 320, - y: task2.y - } - ]; - - expect(task1.outgoing[0].waypoints).to.eql(expected); - - })); - - - it('should NOT snap Task -> Task on connect', inject(function(connect, dragging, elementRegistry) { - - // given - var task1 = elementRegistry.get('Task_1'), - task2 = elementRegistry.get('Task_2'); - - var task1Mid = { x: task1.x + task1.width / 2, y: task1.y + task1.height / 2 }, - task2Mid = { x: task2.x + task2.width / 2, y: task2.y + task2.height / 2 }; - - // when - connect.start(canvasEvent({ x: 0, y: 0 }), task1); - - dragging.hover({ - element: task2, - gfx: elementRegistry.getGraphics(task2) - }); - - dragging.move(canvasEvent({ - x: task2Mid.x + 20, - y: task2Mid.y - 20 - })); - - dragging.end(); - - // then - expect(task1.outgoing[0].waypoints.length).to.equal(4); - - expect(task1.outgoing[0].waypoints[0]).to.eql({ - original: - { - x: task1Mid.x, - y: task1Mid.y - }, - x: task1Mid.x, - y: task1.y + task1.height - }); - - expect(task1.outgoing[0].waypoints[3]).to.eql({ - original: - { - x: task2Mid.x + 20, - y: task2Mid.y - 20 - }, - x: task2Mid.x + 20, - y: task2.y - }); - - })); - - - it('should snap Task -> Event on connect', inject(function(connect, dragging, elementRegistry) { - - // given - var task = elementRegistry.get('Task_2'), - event = elementRegistry.get('StartEvent_1'); - - var taskMid = { x: task.x + task.width / 2, y: task.y + task.height / 2 }, - eventMid = { x: event.x + event.width / 2, y: event.y + event.height / 2 }; - - // when - connect.start(canvasEvent({ x: 0, y: 0 }), task); - - dragging.hover({ - element: event, - gfx: elementRegistry.getGraphics(event) - }); - - dragging.move(canvasEvent({ - x: eventMid.x + 10, - y: eventMid.y - 10 - })); - - dragging.end(); - - // then - var connection = task.outgoing[0]; - - expect(connection.waypoints.length).to.equal(4); - - expect(connection.waypoints[0]).to.eql({ - original: - { - x: taskMid.x, - y: taskMid.y - }, - x: taskMid.x, - y: task.y - }); - - expect(connection.waypoints[3]).to.eql({ - original: - { - x: eventMid.x, - y: eventMid.y - }, - x: eventMid.x, - y: event.y + event.height - }); - - })); - - - it('should snap IntermediateEvent -> Task on global connect', inject(function(connect, dragging, elementRegistry) { - - // given - var event = elementRegistry.get('IntermediateEvent'), - task = elementRegistry.get('Task_1'); - - var eventMid = { x: event.x + event.width / 2, y: event.y + event.height / 2 }, - taskMid = { x: task.x + task.width / 2, y: task.y + task.height / 2 }; - - // when - connect.start(null, event, { x: eventMid.x - 10, y: eventMid.y + 10 }); - - dragging.hover({ - element: task, - gfx: elementRegistry.getGraphics(task) - }); - - dragging.move(canvasEvent({ - x: taskMid.x + 10, - y: taskMid.y - 10 - })); - - dragging.end(); - - // then - var connection = event.outgoing[0]; - - expect(connection.waypoints.length).to.equal(4); - - expect(connection.waypoints[0]).to.eql({ - original: - { - x: eventMid.x, - y: eventMid.y - }, - x: eventMid.x, - y: event.y - }); - - expect(connection.waypoints[3]).to.eql({ - original: - { - x: taskMid.x + 10, - y: taskMid.y - 10 - }, - x: taskMid.x + 10, - y: task.y + task.height - }); - - })); - - }); - - }); - -}); \ No newline at end of file