diff --git a/lib/Viewer.js b/lib/Viewer.js index cf2497e1..25ab2f02 100644 --- a/lib/Viewer.js +++ b/lib/Viewer.js @@ -479,6 +479,11 @@ Viewer.prototype.getModules = function() { * @method Viewer#clear */ Viewer.prototype.clear = function() { + if (!this.getDefinitions()) { + + // no diagram to clear + return; + } // remove businessObject#di binding // diff --git a/lib/features/auto-place/AutoPlace.js b/lib/features/auto-place/AutoPlace.js index b887de76..9c7349e3 100644 --- a/lib/features/auto-place/AutoPlace.js +++ b/lib/features/auto-place/AutoPlace.js @@ -25,6 +25,11 @@ export default function AutoPlace(eventBus, modeling) { */ this.append = function(source, shape) { + emit('autoPlace.start', { + source: source, + shape: shape + }); + // allow others to provide the position var position = emit('autoPlace', { source: source, @@ -37,8 +42,8 @@ export default function AutoPlace(eventBus, modeling) { var newShape = modeling.appendShape(source, shape, position, source.parent); - // notify interested parties on new shape placed emit('autoPlace.end', { + source: source, shape: newShape }); diff --git a/lib/features/label-editing/LabelEditingProvider.js b/lib/features/label-editing/LabelEditingProvider.js index 6a7380b6..719d2b0f 100644 --- a/lib/features/label-editing/LabelEditingProvider.js +++ b/lib/features/label-editing/LabelEditingProvider.js @@ -44,10 +44,10 @@ export default function LabelEditingProvider( // complete on followup canvas operation eventBus.on([ - 'element.mousedown', - 'drag.init', + 'autoPlace.start', 'canvas.viewbox.changing', - 'autoPlace', + 'drag.init', + 'element.mousedown', 'popupMenu.open' ], function(event) { diff --git a/lib/features/snapping/BpmnConnectSnapping.js b/lib/features/snapping/BpmnConnectSnapping.js index 01b83b8d..40c45e98 100644 --- a/lib/features/snapping/BpmnConnectSnapping.js +++ b/lib/features/snapping/BpmnConnectSnapping.js @@ -74,11 +74,11 @@ export default function BpmnConnectSnapping(eventBus, rules) { // snap source context.sourcePosition = mid(source); - if (isAny(target, ['bpmn:Event', 'bpmn:Gateway'])) { + if (isAny(target, [ 'bpmn:Event', 'bpmn:Gateway' ])) { snapToPosition(event, mid(target)); } - if (is(target, 'bpmn:Task')) { + if (isAny(target, [ 'bpmn:Task', 'bpmn:SubProcess' ])) { snapToTaskMid(event, target); } diff --git a/test/spec/ViewerSpec.js b/test/spec/ViewerSpec.js index b1fdfc33..73f24332 100644 --- a/test/spec/ViewerSpec.js +++ b/test/spec/ViewerSpec.js @@ -1614,6 +1614,25 @@ describe('Viewer', function() { describe('#clear', function() { + it('should NOT clear if no diagram', function() { + + // given + var viewer = new Viewer({ container: container }); + + var eventBus = viewer.get('eventBus'); + + var spy = sinon.spy(); + + eventBus.on('diagram.clear', spy); + + // when + viewer.clear(); + + // then + expect(spy).not.to.have.been.called; + }); + + it('should not throw if diagram is already empty', function() { // given diff --git a/test/spec/features/auto-place/AutoPlaceSpec.js b/test/spec/features/auto-place/AutoPlaceSpec.js index 8931bdcd..9f94db43 100644 --- a/test/spec/features/auto-place/AutoPlaceSpec.js +++ b/test/spec/features/auto-place/AutoPlaceSpec.js @@ -1,13 +1,19 @@ +/* global sinon */ + import { bootstrapModeler, inject } from 'test/TestHelper'; import autoPlaceModule from 'lib/features/auto-place'; +import coreModule from 'lib/core'; +import labelEditingModule from 'lib/features/label-editing'; import modelingModule from 'lib/features/modeling'; import selectionModule from 'diagram-js/lib/features/selection'; -import labelEditingModule from 'lib/features/label-editing'; -import coreModule from 'lib/core'; + +import { getBusinessObject } from '../../../../lib/util/ModelUtil'; + +import { getMid } from 'diagram-js/lib/layout/LayoutUtil'; describe('features/auto-place', function() { @@ -110,31 +116,52 @@ describe('features/auto-place', function() { }); - describe('modeling flow', function() { + describe('integration', function() { var diagramXML = require('./AutoPlace.bpmn'); before(bootstrapModeler(diagramXML, { modules: [ - coreModule, - modelingModule, autoPlaceModule, - selectionModule, - labelEditingModule + coreModule, + labelEditingModule, + modelingModule, + selectionModule ] })); + it('should complete direct edit on autoPlace', inject( + function(autoPlace, directEditing, elementFactory, elementRegistry) { + + // given + var element = elementFactory.createShape({ type: 'bpmn:Task' }); + + var source = elementRegistry.get('TASK_2'); + + directEditing.activate(source); + + directEditing._textbox.content.textContent = 'foo'; + + // when + autoPlace.append(source, element); + + // then + expect(getBusinessObject(source).name).to.equal('foo'); + } + )); + + it('should select + direct edit on autoPlace', inject( function(autoPlace, elementRegistry, elementFactory, selection, directEditing) { // given - var el = elementFactory.createShape({ type: 'bpmn:Task' }); + var element = elementFactory.createShape({ type: 'bpmn:Task' }); var source = elementRegistry.get('TASK_2'); // when - var newShape = autoPlace.append(source, el); + var newShape = autoPlace.append(source, element); // then expect(selection.get()).to.eql([ newShape ]); @@ -230,6 +257,108 @@ describe('features/auto-place', function() { }); + + describe('eventbus integration', function() { + + var diagramXML = require('./AutoPlace.bpmn'); + + beforeEach(bootstrapModeler(diagramXML, { + modules: [ + autoPlaceModule, + coreModule, + labelEditingModule, + modelingModule, + selectionModule + ] + })); + + + it('', inject( + function(autoPlace, elementFactory, elementRegistry, eventBus) { + + // given + var element = elementFactory.createShape({ type: 'bpmn:Task' }); + + var source = elementRegistry.get('TASK_2'); + + var listener = sinon.spy(function(event) { + + // then + expect(event.shape).to.equal(element); + expect(event.source).to.equal(source); + }); + + eventBus.on('autoPlace.start', listener); + + // when + autoPlace.append(source, element); + + expect(listener).to.have.been.called; + } + )); + + + it('', inject( + function(autoPlace, elementFactory, elementRegistry, eventBus) { + + // given + var element = elementFactory.createShape({ type: 'bpmn:Task' }); + + var source = elementRegistry.get('TASK_2'); + + var listener = sinon.spy(function(event) { + + // then + expect(event.shape).to.equal(element); + expect(event.source).to.equal(source); + + return { + x: 0, + y: 0 + }; + }); + + eventBus.on('autoPlace', listener); + + // when + autoPlace.append(source, element); + + expect(listener).to.have.been.called; + + expect(getMid(element)).to.eql({ + x: 0, + y: 0 + }); + } + )); + + + it('', inject( + function(autoPlace, elementFactory, elementRegistry, eventBus) { + + // given + var element = elementFactory.createShape({ type: 'bpmn:Task' }); + + var source = elementRegistry.get('TASK_2'); + + var listener = sinon.spy(function(event) { + + // then + expect(event.shape).to.equal(element); + expect(event.source).to.equal(source); + }); + + eventBus.on('autoPlace.end', listener); + + // when + autoPlace.append(source, element); + + expect(listener).to.have.been.called; + } + )); + + }); + }); diff --git a/test/spec/features/snapping/BpmnConnectSnapping.bpmn b/test/spec/features/snapping/BpmnConnectSnapping.bpmn index ab926ec3..4dd8abfd 100644 --- a/test/spec/features/snapping/BpmnConnectSnapping.bpmn +++ b/test/spec/features/snapping/BpmnConnectSnapping.bpmn @@ -6,78 +6,86 @@ + + + + + + - - - - - - - - - - + + + + + + - + - + - + - + - + - + - + - + - + - + - - - - - + + - + - + - + - + + + + + + + + + + diff --git a/test/spec/features/snapping/BpmnConnectSnappingSpec.js b/test/spec/features/snapping/BpmnConnectSnappingSpec.js index 8aaa72b8..79261b52 100644 --- a/test/spec/features/snapping/BpmnConnectSnappingSpec.js +++ b/test/spec/features/snapping/BpmnConnectSnappingSpec.js @@ -34,242 +34,256 @@ describe('features/snapping - BpmnConnectSnapping', function() { dragging.setOptions({ manual: true }); })); + describe('sequence flow', function() { - describe('connect', function() { + describe('boundary event loop', function() { - describe('Boundary Event loop', function() { - - it('should snap to the left', - inject(function(connect, dragging, elementRegistry) { - - // given - var boundaryEvent = elementRegistry.get('BoundaryEvent'), - subProcess = elementRegistry.get('SubProcess'), - subProcessGfx = elementRegistry.getGraphics(subProcess); - - // when - connect.start(canvasEvent({ x: 600, y: 300 }), boundaryEvent); - - dragging.hover({ element: subProcess, gfx: subProcessGfx }); - - dragging.move(canvasEvent({ x: 582, y: 300 })); - - dragging.end(); - - // then - var waypoints = boundaryEvent.outgoing[0].waypoints; - - expect(waypoints[3].x).to.eql(560); - }) - ); - - - it('should snap to the right', - inject(function(connect, dragging, elementRegistry) { - - // given - var boundaryEvent = elementRegistry.get('BoundaryEvent'), - subProcess = elementRegistry.get('SubProcess'), - subProcessGfx = elementRegistry.getGraphics(subProcess); - - // when - connect.start(canvasEvent({ x: 600, y: 300 }), boundaryEvent); - - dragging.hover({ element: subProcess, gfx: subProcessGfx }); - - dragging.move(canvasEvent({ x: 618, y: 300 })); - - dragging.end(); - - // then - var waypoints = boundaryEvent.outgoing[0].waypoints; - - expect(waypoints[3].x).to.eql(640); - }) - ); - - - it('should snap above', - inject(function(connect, dragging, elementRegistry) { - - // given - var boundaryEvent = elementRegistry.get('BoundaryEventRight'), - subProcess = elementRegistry.get('SubProcess'), - subProcessGfx = elementRegistry.getGraphics(subProcess); - - // when - connect.start(canvasEvent({ x: 761, y: 218 }), boundaryEvent); - - dragging.hover({ element: subProcess, gfx: subProcessGfx }); - - dragging.move(canvasEvent({ x: 761, y: 200 })); - - dragging.end(); - - // then - var waypoints = boundaryEvent.outgoing[0].waypoints; - - expect(waypoints[3].y).to.eql(178); - }) - ); - - - it('should snap below', - inject(function(connect, dragging, elementRegistry) { - - // given - var boundaryEvent = elementRegistry.get('BoundaryEventRight'), - subProcess = elementRegistry.get('SubProcess'), - subProcessGfx = elementRegistry.getGraphics(subProcess); - - // when - connect.start(canvasEvent({ x: 761, y: 218 }), boundaryEvent); - - dragging.hover({ element: subProcess, gfx: subProcessGfx }); - - dragging.move(canvasEvent({ x: 761, y: 230 })); - - dragging.end(); - - // then - var waypoints = boundaryEvent.outgoing[0].waypoints; - - expect(waypoints[3].y).to.eql(258); - }) - ); - - }); - - - describe('Task target', function() { - - it('should snap to task mid', - inject(function(connect, dragging, elementRegistry) { - - // given - var startEvent = elementRegistry.get('StartEvent_2'), - task = elementRegistry.get('Task_3'), - taskGfx = elementRegistry.getGraphics(task); - - // when - connect.start(canvasEvent({ x: 130, y: 850 }), startEvent); - - dragging.hover({ element: task, gfx: taskGfx }); - - dragging.move(canvasEvent({ x: 171, y: 893 })); - - dragging.end(); - - // then - var waypoints = startEvent.outgoing[0].waypoints; - - expect(waypoints[waypoints.length-1].y).to.eql(895); - }) - ); - - - it('should snap to grid point', - inject(function(connect, dragging, elementRegistry) { - - // given - var startEvent = elementRegistry.get('StartEvent_1'), - task = elementRegistry.get('Task_1'), - taskGfx = elementRegistry.getGraphics(task); - - // when - connect.start(canvasEvent({ x: 210, y: 60 }), startEvent); - - dragging.hover({ element: task, gfx: taskGfx }); - - dragging.move(canvasEvent({ x: 300, y: 260 })); - - dragging.end(); - - // then - var waypoints = startEvent.outgoing[0].waypoints; - - expect(waypoints[3].y).to.eql(270); - }) - ); - }); - - - it('should snap event if close to target bounds', + it('should snap left', inject(function(connect, dragging, elementRegistry) { // given - var boundaryEvent = elementRegistry.get('BoundaryEvent'), + var boundaryEvent = elementRegistry.get('BoundaryEvent_Bottom'), subProcess = elementRegistry.get('SubProcess'), subProcessGfx = elementRegistry.getGraphics(subProcess); // when - connect.start(canvasEvent({ x: 600, y: 300 }), boundaryEvent); + connect.start(canvasEvent({ x: 590, y: 200 }), boundaryEvent); dragging.hover({ element: subProcess, gfx: subProcessGfx }); - dragging.move(canvasEvent({ x: 400, y: 305 })); + dragging.move(canvasEvent({ x: 400, y: 115 })); dragging.end(); // then var waypoints = boundaryEvent.outgoing[0].waypoints; - expect(waypoints[3].y).to.eql(280); + expect(waypoints).to.have.length(5); + + expect(waypoints[ 4 ].original).to.eql({ + x: 420, + y: 115 + }); }) ); - it('should snap gateway target mid', + it('should snap bottom', inject(function(connect, dragging, elementRegistry) { // given - var startEvent = elementRegistry.get('StartEvent_1'), - gateway = elementRegistry.get('Gateway_1'), - gatewayGfx = elementRegistry.getGraphics(gateway); + var boundaryEvent = elementRegistry.get('BoundaryEvent_Bottom'), + subProcess = elementRegistry.get('SubProcess'), + subProcessGfx = elementRegistry.getGraphics(subProcess); // when - connect.start(canvasEvent({ x: 210, y: 60 }), startEvent); + connect.start(canvasEvent({ x: 630, y: 200 }), boundaryEvent); - dragging.hover({ element: gateway, gfx: gatewayGfx }); + dragging.hover({ element: subProcess, gfx: subProcessGfx }); - dragging.move(canvasEvent({ x: 300, y: 80 })); + dragging.move(canvasEvent({ x: 580, y: 115 })); dragging.end(); // then - var waypoints = startEvent.outgoing[0].waypoints; + var waypoints = boundaryEvent.outgoing[0].waypoints; - expect(waypoints[1].y).to.eql(100); + expect(waypoints).to.have.length(4); + + expect(waypoints[ 3 ].original).to.eql({ + x: 550, + y: 115 + }); }) ); - it('should snap event target mid', + it('should snap right', inject(function(connect, dragging, elementRegistry) { // given - var startEvent = elementRegistry.get('StartEvent_1'), - endEvent = elementRegistry.get('EndEvent_1'), - endEventGfx = elementRegistry.getGraphics(endEvent); + var boundaryEvent = elementRegistry.get('BoundaryEvent_Right'), + subProcess = elementRegistry.get('SubProcess'), + subProcessGfx = elementRegistry.getGraphics(subProcess); // when - connect.start(canvasEvent({ x: 210, y: 60 }), startEvent); + connect.start(canvasEvent({ x: 760, y: 130 }), boundaryEvent); - dragging.hover({ element: endEvent, gfx: endEventGfx }); + dragging.hover({ element: subProcess, gfx: subProcessGfx }); - dragging.move(canvasEvent({ x: 310, y: 275 })); + dragging.move(canvasEvent({ x: 580, y: 115 })); dragging.end(); // then - var waypoints = startEvent.outgoing[0].waypoints; + var waypoints = boundaryEvent.outgoing[0].waypoints; - expect(waypoints[2].y).to.eql(200); + expect(waypoints).to.have.length(4); + + expect(waypoints[ 3 ].original).to.eql({ + x: 580, + y: 90 + }); + }) + ); + + + it('should snap bottom', + inject(function(connect, dragging, elementRegistry) { + + // given + var boundaryEvent = elementRegistry.get('BoundaryEvent_Right'), + subProcess = elementRegistry.get('SubProcess'), + subProcessGfx = elementRegistry.getGraphics(subProcess); + + // when + connect.start(canvasEvent({ x: 760, y: 130 }), boundaryEvent); + + dragging.hover({ element: subProcess, gfx: subProcessGfx }); + + dragging.move(canvasEvent({ x: 580, y: 200 })); + + dragging.end(); + + // then + var waypoints = boundaryEvent.outgoing[0].waypoints; + + expect(waypoints).to.have.length(5); + + expect(waypoints[ 4 ].original).to.eql({ + x: 580, + y: 180 + }); }) ); }); + + + describe('activity target', function() { + + it('should snap to task mid', + inject(function(connect, dragging, elementRegistry) { + + // given + var startEvent = elementRegistry.get('StartEvent_2'), + task = elementRegistry.get('Task_3'), + taskGfx = elementRegistry.getGraphics(task); + + // when + connect.start(canvasEvent({ x: 80, y: 845 }), startEvent); + + dragging.hover({ element: task, gfx: taskGfx }); + + dragging.move(canvasEvent({ x: 200, y: 850 })); + + dragging.end(); + + // then + var waypoints = startEvent.outgoing[0].waypoints; + + expect(waypoints).to.have.length(2); + + expect(waypoints[ 1 ].original).to.eql({ + x: 200, + y: 845 + }); + }) + ); + + + it('should snap to sub-process mid', + inject(function(connect, dragging, elementRegistry) { + + // given + var startEvent = elementRegistry.get('StartEvent_3'), + subProcess = elementRegistry.get('SubProcess_1'), + subProcessGfx = elementRegistry.getGraphics(subProcess); + + // when + connect.start(canvasEvent({ x: 80, y: 1025 }), startEvent); + + dragging.hover({ element: subProcess, gfx: subProcessGfx }); + + dragging.move(canvasEvent({ x: 325, y: 1030 })); + + dragging.end(); + + // then + var waypoints = startEvent.outgoing[0].waypoints; + + expect(waypoints).to.have.length(2); + + expect(waypoints[ 1 ].original).to.eql({ + x: 325, + y: 1025 + }); + }) + ); + + }); + + + it('should to snap gateway target mid', + inject(function(connect, dragging, elementRegistry) { + + // given + var startEvent = elementRegistry.get('StartEvent_1'), + gateway = elementRegistry.get('Gateway_1'), + gatewayGfx = elementRegistry.getGraphics(gateway); + + // when + connect.start(canvasEvent({ x: 80, y: 50 }), startEvent); + + dragging.hover({ element: gateway, gfx: gatewayGfx }); + + dragging.move(canvasEvent({ x: 255, y: 55 })); + + dragging.end(); + + // then + var waypoints = startEvent.outgoing[0].waypoints; + + expect(waypoints).to.have.length(2); + + expect(waypoints[ 1 ].original).to.eql({ + x: 250, + y: 50 + }); + }) + ); + + + it('should snap to event target mid', + 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: 80, y: 50 }), startEvent); + + dragging.hover({ element: endEvent, gfx: endEventGfx }); + + dragging.move(canvasEvent({ x: 85, y: 245 })); + + dragging.end(); + + // then + var waypoints = startEvent.outgoing[0].waypoints; + + expect(waypoints).to.have.length(2); + + expect(waypoints[ 1 ].original).to.eql({ + x: 80, + y: 240 + }); + }) + ); + }); @@ -285,18 +299,23 @@ describe('features/snapping - BpmnConnectSnapping', function() { intermediateCatchEventGfx = elementRegistry.getGraphics(intermediateCatchEvent); // when - connect.start(canvasEvent({ x: 300, y: 300 }), task); + connect.start(canvasEvent({ x: 250, y: 240 }), task); dragging.hover({ element: intermediateCatchEvent, gfx: intermediateCatchEventGfx }); - dragging.move(canvasEvent({ x: 210, y: 610 })); + dragging.move(canvasEvent({ x: 185, y: 555 })); dragging.end(); // then var waypoints = task.outgoing[0].waypoints; - expect(waypoints[3].original).to.eql({ x: 200, y: 600 }); + expect(waypoints).to.have.length(4); + + expect(waypoints[ 3 ].original).to.eql({ + x: 180, + y: 550 + }); })); }); @@ -312,19 +331,29 @@ describe('features/snapping - BpmnConnectSnapping', function() { taskGfx = elementRegistry.getGraphics(task); // when - connect.start(null, intermediateThrowEvent, { x: 110, y: 610 }); + connect.start(null, intermediateThrowEvent, { x: 75, y: 555 }); dragging.hover({ element: task, gfx: taskGfx }); - dragging.move(canvasEvent({ x: 310, y: 310 })); + dragging.move(canvasEvent({ x: 290, y: 240 })); 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 + expect(waypoints).to.have.length(4); + + expect(waypoints[ 0 ].original).to.eql({ + x: 70, + y: 550 + }); + + // NOT snapped + expect(waypoints[ 3 ].original).to.eql({ + x: 290, + y: 240 + }); })); @@ -336,19 +365,29 @@ describe('features/snapping - BpmnConnectSnapping', function() { intermediateCatchEventGfx = elementRegistry.getGraphics(intermediateCatchEvent); // when - connect.start(null, task, { x: 310, y: 310 }); + connect.start(null, task, { x: 255, y: 245 }); dragging.hover({ element: intermediateCatchEvent, gfx: intermediateCatchEventGfx }); - dragging.move(canvasEvent({ x: 210, y: 610 })); + dragging.move(canvasEvent({ x: 185, y: 555 })); 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 }); + expect(waypoints).to.have.length(4); + + // NOT snapped + expect(waypoints[ 0 ].original).to.eql({ + x: 255, + y: 245 + }); + + expect(waypoints[ 3 ].original).to.eql({ + x: 180, + y: 550 + }); })); });