diff --git a/lib/features/modeling/behavior/DropOnFlowBehavior.js b/lib/features/modeling/behavior/DropOnFlowBehavior.js index 6ef66e7a..9e0b8980 100644 --- a/lib/features/modeling/behavior/DropOnFlowBehavior.js +++ b/lib/features/modeling/behavior/DropOnFlowBehavior.js @@ -2,7 +2,8 @@ import inherits from 'inherits'; import { assign, - find + find, + filter } from 'min-dash'; import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; @@ -23,7 +24,15 @@ export default function DropOnFlowBehavior(eventBus, bpmnRules, modeling) { function insertShape(shape, targetFlow, position) { var waypoints = targetFlow.waypoints, - waypointsBefore, waypointsAfter, dockingPoint, source, target, reconnected; + waypointsBefore, + waypointsAfter, + dockingPoint, + source, + target, + incomingConnection, + outgoingConnection, + oldOutgoing = shape.outgoing.slice(), + oldIncoming = shape.incoming.slice(); var intersection = getApproxIntersection(waypoints, position); @@ -56,18 +65,37 @@ export default function DropOnFlowBehavior(eventBus, bpmnRules, modeling) { // reconnect source -> inserted shape modeling.reconnectEnd(targetFlow, shape, waypointsBefore || position); - reconnected = true; + incomingConnection = targetFlow; } if (bpmnRules.canConnect(shape, target, targetFlow)) { - if (!reconnected) { + if (!incomingConnection) { // reconnect inserted shape -> end modeling.reconnectStart(targetFlow, shape, waypointsAfter || position); + + outgoingConnection = targetFlow; } else { - modeling.connect(shape, target, { type: targetFlow.type, waypoints: waypointsAfter }); + outgoingConnection = modeling.connect( + shape, target, { type: targetFlow.type, waypoints: waypointsAfter } + ); } } + + var duplicateConnections = [].concat( + + incomingConnection && filter(oldIncoming, function(connection) { + return connection.source === incomingConnection.source; + }) || [], + + outgoingConnection && filter(oldOutgoing, function(connection) { + return connection.source === outgoingConnection.source; + }) || [] + ); + + if (duplicateConnections.length) { + modeling.removeElements(duplicateConnections); + } } this.preExecute('elements.move', function(context) { diff --git a/test/spec/features/modeling/behavior/DropOnFlowBehavior.bpmn b/test/spec/features/modeling/behavior/DropOnFlowBehavior.bpmn index 59e6a830..67050aa7 100644 --- a/test/spec/features/modeling/behavior/DropOnFlowBehavior.bpmn +++ b/test/spec/features/modeling/behavior/DropOnFlowBehavior.bpmn @@ -3,9 +3,11 @@ SequenceFlow_1 + SequenceFlow_3 SequenceFlow_1 + SequenceFlow_4 SequenceFlow_2 @@ -17,6 +19,12 @@ + + SequenceFlow_3 + SequenceFlow_4 + + + @@ -27,10 +35,10 @@ - - - - + + + + @@ -53,19 +61,31 @@ - + - - - + + + - + + + + + + + + + + + + + diff --git a/test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js b/test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js index 4c467145..bb9652b0 100644 --- a/test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js +++ b/test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js @@ -69,11 +69,11 @@ describe('modeling/behavior - drop on connection', function() { // new outgoing connection expect(newShape.outgoing.length).to.equal(1); - expect(targetConnection).to.be.ok; + expect(targetConnection).to.exist; expect(targetConnection.type).to.equal('bpmn:SequenceFlow'); expect(startEvent.outgoing[0]).to.equal(newShape.incoming[0]); - expect(task.incoming[0]).to.equal(newShape.outgoing[0]); + expect(task.incoming[1]).to.equal(newShape.outgoing[0]); // split target at insertion point expect(sequenceFlow).to.have.waypoints(flatten([ @@ -259,11 +259,11 @@ describe('modeling/behavior - drop on connection', function() { // new outgoing connection expect(intermediateThrowEvent.outgoing.length).to.equal(1); - expect(targetConnection).to.be.ok; + expect(targetConnection).to.exist; expect(targetConnection.type).to.equal('bpmn:SequenceFlow'); expect(startEvent.outgoing[0]).to.equal(intermediateThrowEvent.incoming[0]); - expect(task.incoming[0]).to.equal(intermediateThrowEvent.outgoing[0]); + expect(task.incoming[1]).to.equal(intermediateThrowEvent.outgoing[0]); // split target at insertion point expect(sequenceFlow).to.have.waypoints(flatten([ @@ -319,11 +319,11 @@ describe('modeling/behavior - drop on connection', function() { // new outgoing connection expect(intermediateThrowEvent.outgoing.length).to.equal(1); - expect(targetConnection).to.be.ok; + expect(targetConnection).to.exist; expect(targetConnection.type).to.equal('bpmn:SequenceFlow'); expect(startEvent.outgoing[0]).to.equal(intermediateThrowEvent.incoming[0]); - expect(task.incoming[0]).to.equal(intermediateThrowEvent.outgoing[0]); + expect(task.incoming[1]).to.equal(intermediateThrowEvent.outgoing[0]); // split target at insertion point expect(sequenceFlow).to.have.waypoints(flatten([ @@ -561,6 +561,40 @@ describe('modeling/behavior - drop on connection', function() { } )); + + it('should remove redundant flows', inject( + function(elementRegistry, selection, move, dragging) { + + var existingIncoming = elementRegistry.get('SequenceFlow_3'), + existingOutgoing = elementRegistry.get('SequenceFlow_4'); + + // given + var element = elementRegistry.get('Task_4'); + + var targetFlow = elementRegistry.get('SequenceFlow_1'), + targetFlowGfx = elementRegistry.getGraphics(targetFlow); + + // when + selection.select(element); + + move.start(canvasEvent({ x: 0, y: 0 }), element); + + dragging.hover({ + element: targetFlow, + gfx: targetFlowGfx + }); + + dragging.move(canvasEvent({ x: -40, y: 179 })); + + dragging.end(); + + // then + // existing connections are removed, as they are duplicates + expect(element.incoming).not.to.contain(existingIncoming); + expect(element.outgoing).not.to.contain(existingOutgoing); + } + )); + }); });