From ca12ac91a435a06877490a98665f3b42271dba5f Mon Sep 17 00:00:00 2001 From: Philipp Fromme Date: Mon, 3 Jun 2019 10:46:52 +0200 Subject: [PATCH] chore(bpmn-snapping): move participant fitting to participant behavior Related to #1290 --- .../behavior/CreateParticipantBehavior.js | 122 ++++- lib/features/snapping/BpmnSnapping.js | 173 ++------ lib/util/LabelUtil.js | 2 +- .../behavior/CreateParticipantBehaviorSpec.js | 420 +++++++++++++----- .../features/snapping/BpmnSnappingSpec.js | 103 ++--- 5 files changed, 470 insertions(+), 350 deletions(-) diff --git a/lib/features/modeling/behavior/CreateParticipantBehavior.js b/lib/features/modeling/behavior/CreateParticipantBehavior.js index 652392d2..ed383e7f 100644 --- a/lib/features/modeling/behavior/CreateParticipantBehavior.js +++ b/lib/features/modeling/behavior/CreateParticipantBehavior.js @@ -4,23 +4,81 @@ import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; import { is } from '../../../util/ModelUtil'; +import { isLabel } from '../../../util/LabelUtil'; + +import { getBBox } from 'diagram-js/lib/util/Elements'; + +import { assign } from 'min-dash'; + +var HORIZONTAL_PARTICIPANT_PADDING = 20, + VERTICAL_PARTICIPANT_PADDING = 20, + PARTICIPANT_BORDER_WIDTH = 30; + +var HIGH_PRIORITY = 2000; + /** - * BPMN specific create participant behavior + * BPMN-specific behavior for creating participants. */ -export default function CreateParticipantBehavior( - eventBus, modeling, elementFactory, - bpmnFactory, canvas) { - +export default function CreateParticipantBehavior(canvas, eventBus, modeling) { CommandInterceptor.call(this, eventBus); - /** - * morph process into collaboration before adding - * participant onto collaboration - */ + // fit participant + eventBus.on([ + 'create.start', + 'shape.move.start' + ], HIGH_PRIORITY, function(event) { + var context = event.context, + shape = context.shape, + rootElement = canvas.getRootElement(); + if (!is(shape, 'bpmn:Participant') || + !is(rootElement, 'bpmn:Process') || + !rootElement.children.length) { + return; + } + + // ignore connections, groups and labels + var children = rootElement.children.filter(function(element) { + return !is(element, 'bpmn:Group') && + !isLabel(element) && + !isConnection(element); + }); + + var childrenBBox = getBBox(children); + + var participantBounds = getParticipantBounds(shape, childrenBBox); + + // assign width and height + assign(shape, participantBounds); + + // assign create constraints + context.createConstraints = getParticipantCreateConstraints(shape, childrenBBox); + }); + + // force hovering process when creating first participant + eventBus.on('create.start', HIGH_PRIORITY, function(event) { + var context = event.context, + shape = context.shape, + rootElement = canvas.getRootElement(), + rootElementGfx = canvas.getGraphics(rootElement); + + function ensureHoveringProcess(event) { + event.element = rootElement; + event.gfx = rootElementGfx; + } + + if (is(shape, 'bpmn:Participant') && is(rootElement, 'bpmn:Process')) { + eventBus.on('element.hover', HIGH_PRIORITY, ensureHoveringProcess); + + eventBus.once('create.cleanup', function() { + eventBus.off('element.hover', ensureHoveringProcess); + }); + } + }); + + // turn process into collaboration before adding participant this.preExecute('shape.create', function(context) { - var parent = context.parent, shape = context.shape, position = context.position; @@ -48,9 +106,7 @@ export default function CreateParticipantBehavior( } }, true); - this.execute('shape.create', function(context) { - var processRoot = context.processRoot, shape = context.shape; @@ -62,39 +118,63 @@ export default function CreateParticipantBehavior( } }, true); - this.revert('shape.create', function(context) { var processRoot = context.processRoot, shape = context.shape; if (processRoot) { + // assign the participant processRef shape.businessObject.processRef = context.oldProcessRef; } }, true); - this.postExecute('shape.create', function(context) { - var processRoot = context.processRoot, shape = context.shape; if (processRoot) { + // process root is already detached at this point var processChildren = processRoot.children.slice(); + modeling.moveElements(processChildren, { x: 0, y: 0 }, shape); } }, true); - } CreateParticipantBehavior.$inject = [ + 'canvas', 'eventBus', - 'modeling', - 'elementFactory', - 'bpmnFactory', - 'canvas' + 'modeling' ]; -inherits(CreateParticipantBehavior, CommandInterceptor); \ No newline at end of file +inherits(CreateParticipantBehavior, CommandInterceptor); + +// helpers ////////// + +function getParticipantBounds(shape, childrenBBox) { + childrenBBox = { + width: childrenBBox.width + HORIZONTAL_PARTICIPANT_PADDING * 2 + PARTICIPANT_BORDER_WIDTH, + height: childrenBBox.height + VERTICAL_PARTICIPANT_PADDING * 2 + }; + + return { + width: Math.max(shape.width, childrenBBox.width), + height: Math.max(shape.height, childrenBBox.height) + }; +} + +function getParticipantCreateConstraints(shape, childrenBBox) { + return { + bottom: childrenBBox.y + shape.height / 2 - VERTICAL_PARTICIPANT_PADDING, + left: childrenBBox.x + childrenBBox.width - shape.width / 2 + HORIZONTAL_PARTICIPANT_PADDING, + top: childrenBBox.y + childrenBBox.height - shape.height / 2 + VERTICAL_PARTICIPANT_PADDING, + right: childrenBBox.x + shape.width / 2 - HORIZONTAL_PARTICIPANT_PADDING - PARTICIPANT_BORDER_WIDTH + }; +} + +function isConnection(element) { + return !!element.waypoints; +} \ No newline at end of file diff --git a/lib/features/snapping/BpmnSnapping.js b/lib/features/snapping/BpmnSnapping.js index 81228378..b484342a 100644 --- a/lib/features/snapping/BpmnSnapping.js +++ b/lib/features/snapping/BpmnSnapping.js @@ -5,41 +5,30 @@ import { isNumber } from 'min-dash'; -import { - getBBox as getBoundingBox -} from 'diagram-js/lib/util/Elements'; +import Snapping from 'diagram-js/lib/features/snapping/Snapping'; import { is } from '../../util/ModelUtil'; import { isAny } from '../modeling/util/ModelingUtil'; -import { - isExpanded -} from '../../util/DiUtil'; - -import Snapping from 'diagram-js/lib/features/snapping/Snapping'; +import { isExpanded } from '../../util/DiUtil'; import { - mid, - topLeft, bottomRight, isSnapped, - setSnapped + mid, + setSnapped, + topLeft } from 'diagram-js/lib/features/snapping/SnapUtil'; -import { - asTRBL -} from 'diagram-js/lib/layout/LayoutUtil'; - +import { asTRBL } from 'diagram-js/lib/layout/LayoutUtil'; import { getBoundaryAttachment, getParticipantSizeConstraints } from './BpmnSnappingUtil'; -import { - getLanesRoot -} from '../modeling/util/LaneUtil'; +import { getLanesRoot } from '../modeling/util/LaneUtil'; var round = Math.round; @@ -47,59 +36,14 @@ var HIGH_PRIORITY = 1500; /** - * BPMN specific snapping functionality - * - * * snap on process elements if a pool is created inside a - * process diagram + * BPMN-specific snapping. * * @param {EventBus} eventBus * @param {Canvas} canvas */ -export default function BpmnSnapping(eventBus, canvas, bpmnRules, elementRegistry) { - - // instantiate super +export default function BpmnSnapping(bpmnRules, canvas, elementRegistry, eventBus) { Snapping.call(this, eventBus, canvas); - - /** - * Drop participant on process <> process elements snapping - */ - eventBus.on('create.start', function(event) { - - var context = event.context, - shape = context.shape, - rootElement = canvas.getRootElement(); - - // snap participant around existing elements (if any) - if (is(shape, 'bpmn:Participant') && is(rootElement, 'bpmn:Process')) { - initParticipantSnapping(context, shape, rootElement.children); - } - }); - - eventBus.on([ 'create.move', 'create.end' ], HIGH_PRIORITY, function(event) { - - var context = event.context, - shape = context.shape, - participantSnapBox = context.participantSnapBox; - - if (!isSnapped(event) && participantSnapBox) { - snapParticipant(participantSnapBox, shape, event); - } - }); - - eventBus.on('shape.move.start', function(event) { - - var context = event.context, - shape = context.shape, - rootElement = canvas.getRootElement(); - - // snap participant around existing elements (if any) - if (is(shape, 'bpmn:Participant') && is(rootElement, 'bpmn:Process')) { - initParticipantSnapping(context, shape, rootElement.children); - } - }); - - function canAttach(shape, target, position) { return bpmnRules.canAttach([ shape ], target, null, position) === 'attach'; } @@ -108,6 +52,9 @@ export default function BpmnSnapping(eventBus, canvas, bpmnRules, elementRegistr return bpmnRules.canConnect(source, target); } + // creating first participant + eventBus.on([ 'create.move', 'create.end' ], HIGH_PRIORITY, setSnappedIfConstrained); + /** * Snap boundary events to elements border */ @@ -233,15 +180,13 @@ export default function BpmnSnapping(eventBus, canvas, bpmnRules, elementRegistr inherits(BpmnSnapping, Snapping); BpmnSnapping.$inject = [ - 'eventBus', - 'canvas', 'bpmnRules', - 'elementRegistry' + 'canvas', + 'elementRegistry', + 'eventBus' ]; - BpmnSnapping.prototype.initSnap = function(event) { - var context = event.context, shape = event.shape, shapeMid, @@ -250,11 +195,11 @@ BpmnSnapping.prototype.initSnap = function(event) { shapeBottomRight, snapContext; - snapContext = Snapping.prototype.initSnap.call(this, event); if (is(shape, 'bpmn:Participant')) { - // assign higher priority for outer snaps on participants + + // snap to borders with higher priority snapContext.setSnapLocations([ 'top-left', 'bottom-right', 'mid' ]); } @@ -393,67 +338,7 @@ BpmnSnapping.prototype.addTargetSnaps = function(snapPoints, shape, target) { }); }; - -// participant snapping ////////////////////// - -function initParticipantSnapping(context, shape, elements) { - - if (!elements.length) { - return; - } - - var snapBox = getBoundingBox(elements.filter(function(e) { - return !e.labelTarget && !e.waypoints && !is(e, 'bpmn:Group'); - })); - - snapBox.x -= 50; - snapBox.y -= 20; - snapBox.width += 70; - snapBox.height += 40; - - // adjust shape height to include bounding box - shape.width = Math.max(shape.width, snapBox.width); - shape.height = Math.max(shape.height, snapBox.height); - - context.participantSnapBox = snapBox; -} - -function snapParticipant(snapBox, shape, event, offset) { - offset = offset || 0; - - var shapeHalfWidth = shape.width / 2 - offset, - shapeHalfHeight = shape.height / 2; - - var currentTopLeft = { - x: event.x - shapeHalfWidth - offset, - y: event.y - shapeHalfHeight - }; - - var currentBottomRight = { - x: event.x + shapeHalfWidth + offset, - y: event.y + shapeHalfHeight - }; - - var snapTopLeft = snapBox, - snapBottomRight = bottomRight(snapBox); - - if (currentTopLeft.x >= snapTopLeft.x) { - setSnapped(event, 'x', snapTopLeft.x + offset + shapeHalfWidth); - } else - if (currentBottomRight.x <= snapBottomRight.x) { - setSnapped(event, 'x', snapBottomRight.x - offset - shapeHalfWidth); - } - - if (currentTopLeft.y >= snapTopLeft.y) { - setSnapped(event, 'y', snapTopLeft.y + shapeHalfHeight); - } else - if (currentBottomRight.y <= snapBottomRight.y) { - setSnapped(event, 'y', snapBottomRight.y - shapeHalfHeight); - } -} - - -// boundary event snapping ////////////////////// +// helpers ////////// function snapBoundaryEvent(event, shape, target) { var targetTRBL = asTRBL(target); @@ -479,4 +364,26 @@ function snapBoundaryEvent(event, shape, target) { 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/util/LabelUtil.js b/lib/util/LabelUtil.js index 737c310a..5c6513bf 100644 --- a/lib/util/LabelUtil.js +++ b/lib/util/LabelUtil.js @@ -153,5 +153,5 @@ export function getExternalLabelBounds(semantic, element) { } export function isLabel(element) { - return element && element.labelTarget; + return element && !!element.labelTarget; } diff --git a/test/spec/features/modeling/behavior/CreateParticipantBehaviorSpec.js b/test/spec/features/modeling/behavior/CreateParticipantBehaviorSpec.js index 38cb935d..d76a09a6 100644 --- a/test/spec/features/modeling/behavior/CreateParticipantBehaviorSpec.js +++ b/test/spec/features/modeling/behavior/CreateParticipantBehaviorSpec.js @@ -3,220 +3,394 @@ import { inject } from 'test/TestHelper'; -import modelingModule from 'lib/features/modeling'; import coreModule from 'lib/core'; +import createModule from 'diagram-js/lib/features/create'; +import modelingModule from 'lib/features/modeling'; + +import { + getBBox +} from 'diagram-js/lib/util/Elements'; + +import { asTRBL } from 'diagram-js/lib/layout/LayoutUtil'; + +import { + createCanvasEvent as canvasEvent +} from '../../../../util/MockEvents'; describe('features/modeling - create participant', function() { - var testModules = [ coreModule, modelingModule ]; + var testModules = [ + coreModule, + createModule, + modelingModule + ]; + describe('process', function() { - describe('on process diagram', function() { - - describe('should transform diagram into collaboration', function() { + describe('should turn diagram into collaboration', function() { var processDiagramXML = require('../../../../fixtures/bpmn/collaboration/process-empty.bpmn'); beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules })); + var collaboration, + collaborationBo, + collaborationDi, + diRoot, + participant, + participantBo, + participantDi, + process, + processBo, + processDi; - it('execute', inject(function(modeling, elementFactory, canvas) { + beforeEach(inject(function(canvas, elementFactory, modeling) { // given - var processShape = canvas.getRootElement(), - process = processShape.businessObject, - participantShape = elementFactory.createParticipantShape(true), - participant = participantShape.businessObject, - diRoot = process.di.$parent; + process = canvas.getRootElement(); + processBo = process.businessObject; + processDi = processBo.di; + + diRoot = processBo.di.$parent; + + participant = elementFactory.createParticipantShape(); + participantBo = participant.businessObject; + participantDi = participantBo.di; // when - modeling.createShape(participantShape, { x: 350, y: 200 }, processShape); + modeling.createShape(participant, { x: 350, y: 200 }, process); - // then - expect(participant.processRef).to.eql(process); - - var collaborationRoot = canvas.getRootElement(), - collaboration = collaborationRoot.businessObject, - collaborationDi = collaboration.di; - - expect(collaboration.$instanceOf('bpmn:Collaboration')).to.be.true; - - // participant / collaboration are wired - expect(participant.$parent).to.eql(collaboration); - expect(collaboration.participants).to.include(participant); - - - // collaboration is added to root elements - expect(collaboration.$parent).to.eql(process.$parent); - - // di is wired - var participantDi = participant.di; - - expect(participantDi.$parent).to.eql(collaborationDi); - expect(collaborationDi.$parent).to.eql(diRoot); + collaboration = canvas.getRootElement(); + collaborationBo = collaboration.businessObject; + collaborationDi = collaborationBo.di; })); - it('undo', inject(function(modeling, elementFactory, canvas, commandStack) { + it('execute', function() { - // given - var processShape = canvas.getRootElement(), - process = processShape.businessObject, - processDi = process.di, - participantShape = elementFactory.createParticipantShape(true), - participant = participantShape.businessObject, - oldParticipantProcessRef = participant.processRef, - diRoot = process.di.$parent; + // then + expect(participantBo.$parent).to.equal(collaborationBo); + expect(participantBo.processRef).to.equal(processBo); - modeling.createShape(participantShape, { x: 350, y: 200 }, processShape); + expect(collaborationBo.$instanceOf('bpmn:Collaboration')).to.be.true; + expect(collaborationBo.$parent).to.equal(processBo.$parent); + expect(collaborationBo.participants).to.include(participantBo); - var collaborationRoot = canvas.getRootElement(), - collaboration = collaborationRoot.businessObject; + expect(participantDi.$parent).to.equal(collaborationDi); + expect(collaborationDi.$parent).to.equal(diRoot); + }); + + + it('undo', inject(function(commandStack) { // when commandStack.undo(); - // then - expect(participant.processRef).to.eql(oldParticipantProcessRef); + expect(participantBo.$parent).not.to.exist; + expect(participantBo.processRef).not.to.equal(processBo); - expect(participant.$parent).to.be.null; - expect(collaboration.participants).not.to.include(participant); + expect(collaborationBo.$parent).not.to.exist; + expect(collaborationBo.participants).not.to.include(participantBo); - // collaboration is detached - expect(collaboration.$parent).to.be.null; - - // di is wired - expect(processDi.$parent).to.eql(diRoot); + expect(processDi.$parent).to.equal(diRoot); })); }); - describe('should wrap existing elements', function() { + describe('should move existing elements', function() { var processDiagramXML = require('../../../../fixtures/bpmn/collaboration/process.bpmn'); beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules })); + var collaboration, + collaborationBo, + collaborationDi, + participant, + process, + processBo, + processDi; - it('execute', inject(function(modeling, elementFactory, canvas) { + beforeEach(inject(function(canvas, elementFactory, modeling) { // given - var processShape = canvas.getRootElement(), - process = processShape.businessObject, - participantShape = elementFactory.createParticipantShape(true), - participant = participantShape.businessObject; + process = canvas.getRootElement(); + processBo = process.businessObject; + processDi = processBo.di; + + participant = elementFactory.createParticipantShape(); // when - modeling.createShape(participantShape, { x: 350, y: 200 }, processShape); + modeling.createShape(participant, { x: 350, y: 200 }, process); - // then - expect(participant.processRef).to.eql(process); - - var newRootShape = canvas.getRootElement(), - collaboration = newRootShape.businessObject; - - expect(collaboration.$instanceOf('bpmn:Collaboration')).to.be.true; - - expect(participant.$parent).to.eql(collaboration); - expect(collaboration.participants).to.include(participant); + collaboration = canvas.getRootElement(); + collaborationBo = collaboration.businessObject; + collaborationDi = collaborationBo.di; })); - it('undo', inject(function(modeling, elementFactory, elementRegistry, canvas, commandStack) { + it('execute', function() { - // given - var processShape = canvas.getRootElement(), - participantShape = elementFactory.createParticipantShape(true); + // then + expect(collaboration.children).to.have.length(4); - modeling.createShape(participantShape, { x: 350, y: 200 }, processShape); + collaboration.children.forEach(function(child) { + var childBo = child.businessObject, + childDi = childBo.di; + + expect(childDi.$parent).to.eql(collaborationDi); + expect(collaborationDi.planeElement).to.include(childDi); + }); + + expect(participant.children).to.have.length(5); + + participant.children.forEach(function(child) { + var childBo = child.businessObject, + childDi = childBo.di; + + expect(childDi.$parent).to.eql(collaborationDi); + expect(collaborationDi.planeElement).to.include(childDi); + }); + }); + + + it('undo', inject(function(commandStack) { // when commandStack.undo(); - var startEventElement = elementRegistry.get('StartEvent_1'), - startEventDi = startEventElement.businessObject.di, - rootElement = canvas.getRootElement(), - rootShapeDi = rootElement.businessObject.di; + expect(process.children).to.have.length(8); - // then - expect(participantShape.children.length).to.equal(0); - expect(processShape.children.length).to.equal(8); + process.children.forEach(function(child) { + var childBo = child.businessObject, + childDi = childBo.di; - // children di is wired - expect(startEventDi.$parent).to.eql(rootShapeDi); - expect(rootShapeDi.planeElement).to.include(startEventDi); + expect(childDi.$parent).to.eql(processDi); + expect(processDi.planeElement).to.include(childDi); + }); + + expect(participant.children.length).to.equal(0); })); - it('should detach DI on update canvas root', inject( - function(canvas, elementFactory, commandStack, modeling, elementRegistry) { + it('should detach DI when turning process into collaboration', inject(function(modeling) { - // when - modeling.makeCollaboration(); + // when + modeling.makeCollaboration(); - var startEventElement = elementRegistry.get('StartEvent_1'), - startEventDi = startEventElement.businessObject.di, - rootElement = canvas.getRootElement(), - rootShapeDi = rootElement.businessObject.di; + // then + process.children.forEach(function(child) { + var childBo = child.businessObject, + childDi = childBo.di; - // then - expect(startEventDi.$parent).not.to.be.ok; - expect(rootShapeDi.planeElement).not.to.include(startEventDi); + expect(childDi.$parent).not.to.exist; + expect(processDi.planeElement).not.to.include(childDi); + }); + })); + + }); + + + describe('hovering process when creating first participant', function() { + + var processDiagramXML = require('../../../../fixtures/bpmn/collaboration/process.bpmn'); + + beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules })); + + var participant, + process, + processGfx, + subProcess, + subProcessGfx; + + beforeEach(inject(function(canvas, elementFactory, elementRegistry) { + + // given + process = canvas.getRootElement(); + processGfx = canvas.getGraphics(process); + + participant = elementFactory.createParticipantShape(); + + subProcess = elementRegistry.get('SubProcess_1'); + subProcessGfx = canvas.getGraphics(subProcess); + })); + + + it('should ensure hovering process', inject(function(create, dragging, eventBus) { + + // given + create.start(canvasEvent({ x: 100, y: 100 }), participant); + + var event = eventBus.createEvent({ + element: subProcess, + gfx: subProcessGfx + }); + + // when + eventBus.fire('element.hover', event); + + // then + expect(event.element).to.equal(process); + expect(event.gfx).to.equal(processGfx); + })); + + + it('should clean up', inject(function(create, dragging, eventBus) { + + // given + create.start(canvasEvent({ x: 100, y: 100 }), participant); + + // when + dragging.end(); + + // then + var event = eventBus.createEvent({ + element: subProcess, + gfx: subProcessGfx + }); + + eventBus.fire('element.hover', event); + + expect(event.element).to.equal(subProcess); + expect(event.gfx).to.equal(subProcessGfx); + })); + + }); + + + describe('fitting participant (default size)', function() { + + var processDiagramXML = require('../../../../fixtures/bpmn/collaboration/process.bpmn'); + + beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules })); + + var participant, + participantBo, + process, + processGfx; + + beforeEach(inject(function(canvas, create, dragging, elementFactory) { + + // given + process = canvas.getRootElement(); + processGfx = canvas.getGraphics(process); + + participant = elementFactory.createParticipantShape(); + participantBo = participant.businessObject; + + create.start(canvasEvent({ x: 100, y: 100 }), participant); + + dragging.hover({ element: process, gfx: processGfx }); + })); + + + it('should fit participant', inject(function(elementFactory) { + + // then + var defaultSize = elementFactory._getDefaultSize(participantBo); + + expect(participant.width).to.equal(defaultSize.width); + expect(participant.height).to.equal(defaultSize.height); + })); + + + describe('create constraints', function() { + + function expectBoundsWithin(inner, outer, padding) { + expect(inner.top >= outer.top + padding.top).to.be.true; + expect(inner.right <= outer.right - padding.right).to.be.true; + expect(inner.bottom <= outer.bottom - padding.bottom).to.be.true; + expect(inner.left >= outer.left + padding.left).to.be.true; } - )); + + var padding = { + top: 20, + right: 20, + bottom: 20, + left: 50 + }; + + + [ + { x: 0, y: 0 }, + { x: 1000, y: 0 }, + { x: 0, y: 1000 }, + { x: 1000, y: 1000 } + ].forEach(function(position) { + + it('should constrain ' + JSON.stringify(position), inject(function(dragging) { + + // when + dragging.move(canvasEvent(position)); + + dragging.end(); + + // then + expectBoundsWithin( + asTRBL(getBBox(participant.children)), + asTRBL(getBBox(participant)), + padding + ); + })); + + }); + + }); }); }); - describe('should add to collaboration', function() { + describe('collaboration', function() { - var collaborationDiagramXML = require('../../../../fixtures/bpmn/collaboration/collaboration-participant.bpmn'); + var collaborationDiagramXML = + require('../../../../fixtures/bpmn/collaboration/collaboration-participant.bpmn'); beforeEach(bootstrapModeler(collaborationDiagramXML, { modules: testModules })); + var collaborationBo, + participant, + participantBo, + rootElement; - it('execute', inject(function(modeling, elementFactory, canvas) { + beforeEach(inject(function(canvas, elementFactory, modeling) { // given - var collaborationRoot = canvas.getRootElement(), - collaboration = collaborationRoot.businessObject, - participantShape = elementFactory.createParticipantShape(true), - participant = participantShape.businessObject; + rootElement = canvas.getRootElement(); + collaborationBo = rootElement.businessObject; + + participant = elementFactory.createParticipantShape(); + participantBo = participant.businessObject; // when - modeling.createShape(participantShape, { x: 350, y: 500 }, collaborationRoot); - - // then - expect(collaborationRoot.children).to.include(participantShape); - - expect(participant.$parent).to.eql(collaboration); - expect(collaboration.participants).to.include(participant); + modeling.createShape(participant, { x: 350, y: 500 }, rootElement); })); - it('undo', inject(function(modeling, elementFactory, canvas, commandStack) { + it('execute', function() { - // given - var collaborationRoot = canvas.getRootElement(), - collaboration = collaborationRoot.businessObject, - participantShape = elementFactory.createParticipantShape(true), - participant = participantShape.businessObject; + // then + expect(rootElement.children).to.include(participant); - modeling.createShape(participantShape, { x: 350, y: 500 }, collaborationRoot); + expect(participantBo.$parent).to.equal(collaborationBo); + expect(collaborationBo.participants).to.include(participantBo); + }); + + + it('undo', inject(function(commandStack) { // when commandStack.undo(); // then - expect(collaborationRoot.children).not.to.include(participantShape); + expect(rootElement.children).not.to.include(participant); - expect(participant.$parent).not.to.be.ok; - expect(collaboration.participants).not.to.include(participant); + expect(participantBo.$parent).not.to.exist; + expect(collaborationBo.participants).not.to.include(participantBo); })); }); diff --git a/test/spec/features/snapping/BpmnSnappingSpec.js b/test/spec/features/snapping/BpmnSnappingSpec.js index 528e9724..ebd6297a 100644 --- a/test/spec/features/snapping/BpmnSnappingSpec.js +++ b/test/spec/features/snapping/BpmnSnappingSpec.js @@ -3,6 +3,8 @@ import { inject } from 'test/TestHelper'; +import TestContainer from 'mocha-test-container-support'; + import { createCanvasEvent as canvasEvent } from '../../../util/MockEvents'; @@ -16,6 +18,8 @@ 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() { @@ -29,7 +33,7 @@ describe('features/snapping - BpmnSnapping', function() { connectModule ]; - describe('general', function() { + describe('on itself', function() { var diagramXML = require('./BpmnSnapping.general.bpmn'); @@ -75,6 +79,7 @@ describe('features/snapping - BpmnSnapping', function() { }); + describe('on Boundary Events', function() { var diagramXML = require('../../../fixtures/bpmn/collaboration/process.bpmn'); @@ -304,89 +309,43 @@ describe('features/snapping - BpmnSnapping', function() { var diagramXML = require('../../../fixtures/bpmn/collaboration/process.bpmn'); - beforeEach(bootstrapModeler(diagramXML, { - modules: testModules - })); + it('should set snapped if outside of constraints', function(done) { - beforeEach(inject(function(dragging) { - dragging.setOptions({ manual: true }); - })); - - - it('should snap to process children bounds / top left', - inject(function(canvas, create, dragging, elementFactory) { - - // given - var participantShape = elementFactory.createParticipantShape(false), - rootElement = canvas.getRootElement(), - rootGfx = canvas.getGraphics(rootElement); + bootstrapModeler(diagramXML, { + container: TestContainer.get(this), + modules: testModules + })(function() { // when - create.start(canvasEvent({ x: 50, y: 50 }), participantShape); + inject(function(canvas, create, dragging, elementFactory, eventBus) { - dragging.hover({ element: rootElement, gfx: rootGfx }); + // given + dragging.setOptions({ manual: true }); - dragging.move(canvasEvent({ x: 65, y: 65 })); - dragging.end(canvasEvent({ x: 65, y: 65 })); + var participantShape = elementFactory.createParticipantShape(false), + rootElement = canvas.getRootElement(), + rootGfx = canvas.getGraphics(rootElement); - // then - expect(participantShape).to.have.bounds({ - width: 600, height: 250, x: 18, y: -8 - }); - }) - ); + create.start(canvasEvent({ x: 50, y: 50 }), participantShape); + dragging.hover({ element: rootElement, gfx: rootGfx }); - it('should not snap to group bounds', - inject(function(canvas, create, dragging, elementFactory, elementRegistry) { + eventBus.once('create.move', function(event) { - // given - var participantShape = elementFactory.createParticipantShape(false), - rootElement = canvas.getRootElement(), - rootGfx = canvas.getGraphics(rootElement), - groupElement = elementRegistry.get('Group_1'); + // then + // expect snapped to avoid snapping outside of constraints + expect(isSnapped(event)).to.be.true; - // when - create.start(canvasEvent({ x: 50, y: 50 }), participantShape); + done(); + }); - dragging.hover({ element: rootElement, gfx: rootGfx }); + // when + dragging.move(canvasEvent({ x: 0, y: 0 })); + })(); - dragging.move(canvasEvent({ x: 400, y: 400 })); - dragging.end(canvasEvent({ x: 400, y: 400 })); + }); - // then - var totalWidth = groupElement.x + groupElement.width + 70, - totalHeight = groupElement.y + groupElement.height + 40; - - expect(participantShape).not.to.have.bounds({ - width: totalWidth, height: totalHeight, x: 100, y: 52 - }); - }) - ); - - - it('should snap to process children bounds / bottom right', - 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({ - width: 600, height: 250, x: 100, y: 52 - }); - }) - ); + }); }); @@ -1188,4 +1147,4 @@ describe('features/snapping - BpmnSnapping', function() { }); -}); +}); \ No newline at end of file