diff --git a/lib/features/modeling/BpmnFactory.js b/lib/features/modeling/BpmnFactory.js
index d74103bf..888b6941 100644
--- a/lib/features/modeling/BpmnFactory.js
+++ b/lib/features/modeling/BpmnFactory.js
@@ -16,6 +16,9 @@ BpmnFactory.prototype._needsId = function(element) {
return element.$instanceOf('bpmn:RootElement') ||
element.$instanceOf('bpmn:FlowElement') ||
element.$instanceOf('bpmn:Artifact') ||
+ element.$instanceOf('bpmn:Participant') ||
+ element.$instanceOf('bpmn:Process') ||
+ element.$instanceOf('bpmn:Collaboration') ||
element.$instanceOf('bpmndi:BPMNShape') ||
element.$instanceOf('bpmndi:BPMNEdge') ||
element.$instanceOf('bpmndi:BPMNDiagram') ||
@@ -81,5 +84,10 @@ BpmnFactory.prototype.createDiEdge = function(semantic, waypoints, attrs) {
}, attrs));
};
+BpmnFactory.prototype.createDiPlane = function(semantic) {
+ return this.create('bpmndi:BPMNPlane', {
+ bpmnElement: semantic
+ });
+};
module.exports = BpmnFactory;
diff --git a/lib/features/modeling/BpmnUpdater.js b/lib/features/modeling/BpmnUpdater.js
index b969d823..575dc65e 100644
--- a/lib/features/modeling/BpmnUpdater.js
+++ b/lib/features/modeling/BpmnUpdater.js
@@ -69,7 +69,6 @@ function BpmnUpdater(eventBus, bpmnFactory, connectionDocking) {
'connection.move',
'connection.delete' ], updateParent);
-
// update bounds
function updateBounds(e) {
self.updateBounds(e.context.shape);
@@ -186,6 +185,13 @@ BpmnUpdater.prototype.updateDiParent = function(di, parentDi) {
}
};
+function getDefinitions(element) {
+ while (element && !element.$instanceOf('bpmn:Definitions')) {
+ element = element.$parent;
+ }
+
+ return element;
+}
BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent) {
@@ -197,10 +203,6 @@ BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent)
if (businessObject.$instanceOf('bpmn:FlowElement')) {
- if (businessObject.$parent === newParent) {
- return;
- }
-
if (newParent && newParent.$instanceOf('bpmn:Participant')) {
newParent = newParent.processRef;
}
@@ -226,6 +228,30 @@ BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent)
containment = 'artifacts';
}
+ if (businessObject.$instanceOf('bpmn:Participant')) {
+ containment = 'participants';
+
+ // make sure the participants process is properly attached / detached
+ // from the XML document
+
+ var process = businessObject.processRef,
+ definitions;
+
+ if (process) {
+ definitions = getDefinitions(businessObject.$parent || newParent);
+
+ if (businessObject.$parent) {
+ Collections.remove(definitions.get('rootElements'), process);
+ process.$parent = null;
+ }
+
+ if (newParent) {
+ Collections.add(definitions.get('rootElements'), process);
+ process.$parent = definitions;
+ }
+ }
+ }
+
if (!containment) {
throw new Error('no parent for ', businessObject, newParent);
}
diff --git a/lib/features/modeling/ElementFactory.js b/lib/features/modeling/ElementFactory.js
index 0a7b643f..8387acc5 100644
--- a/lib/features/modeling/ElementFactory.js
+++ b/lib/features/modeling/ElementFactory.js
@@ -49,6 +49,11 @@ ElementFactory.prototype.create = function(elementType, attrs) {
}
if (!businessObject.di) {
+ if (elementType === 'root') {
+ businessObject.di = this._bpmnFactory.createDiPlane(businessObject, [], {
+ id: businessObject.id + '_di'
+ });
+ } else
if (elementType === 'connection') {
businessObject.di = this._bpmnFactory.createDiEdge(businessObject, [], {
id: businessObject.id + '_di'
@@ -111,5 +116,21 @@ ElementFactory.prototype._getDefaultSize = function(semantic) {
return { width: 36, height: 36 };
}
+ if (semantic.$instanceOf('bpmn:Participant')) {
+ return { width: 600, height: 300 };
+ }
+
return { width: 100, height: 80 };
};
+
+
+ElementFactory.prototype.createParticipantShape = function(collapsed) {
+
+ var participantShape = this.createShape({ type: 'bpmn:Participant' });
+
+ if (!collapsed) {
+ participantShape.businessObject.processRef = this._bpmnFactory.create('bpmn:Process');
+ }
+
+ return participantShape;
+};
\ No newline at end of file
diff --git a/lib/features/modeling/Modeling.js b/lib/features/modeling/Modeling.js
index 9a0d2d9e..b2e3583a 100644
--- a/lib/features/modeling/Modeling.js
+++ b/lib/features/modeling/Modeling.js
@@ -4,7 +4,8 @@ var inherits = require('inherits');
var BaseModeling = require('diagram-js/lib/features/modeling/Modeling');
-var UpdatePropertiesHandler = require('./cmd/UpdatePropertiesHandler');
+var UpdatePropertiesHandler = require('./cmd/UpdatePropertiesHandler'),
+ UpdateCanvasRootHandler = require('./cmd/UpdateCanvasRootHandler');
/**
@@ -29,6 +30,7 @@ Modeling.prototype.getHandlers = function() {
var handlers = BaseModeling.prototype.getHandlers.call(this);
handlers['element.updateProperties'] = UpdatePropertiesHandler;
+ handlers['canvas.updateRoot'] = UpdateCanvasRootHandler;
return handlers;
};
@@ -72,4 +74,43 @@ Modeling.prototype.updateProperties = function(element, properties) {
element: element,
properties: properties
});
+};
+
+
+/**
+ * Transform the current diagram into a collaboration.
+ *
+ * @return {djs.model.Root} the new root element
+ */
+Modeling.prototype.makeCollaboration = function() {
+
+ var collaborationElement = this._create('root', {
+ type: 'bpmn:Collaboration'
+ });
+
+ var context = {
+ newRoot: collaborationElement
+ };
+
+ this._commandStack.execute('canvas.updateRoot', context);
+
+ return collaborationElement;
+};
+
+/**
+ * Transform the current diagram into a process.
+ *
+ * @return {djs.model.Root} the new root element
+ */
+Modeling.prototype.makeProcess = function() {
+
+ var processElement = this._create('root', {
+ type: 'bpmn:Process'
+ });
+
+ var context = {
+ newRoot: processElement
+ };
+
+ this._commandStack.execute('canvas.updateRoot', context);
};
\ No newline at end of file
diff --git a/lib/features/modeling/behavior/CreateBehavior.js b/lib/features/modeling/behavior/CreateBehavior.js
new file mode 100644
index 00000000..bace4a8f
--- /dev/null
+++ b/lib/features/modeling/behavior/CreateBehavior.js
@@ -0,0 +1,104 @@
+'use strict';
+
+var inherits = require('inherits');
+
+var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor');
+
+var getBoundingBox = require('diagram-js/lib/util/Elements').getBBox,
+ assign = require('lodash/object/assign');
+
+/**
+ * BPMN specific create behavior
+ */
+function CreateBehavior(eventBus, modeling) {
+
+ CommandInterceptor.call(this, eventBus);
+
+
+ /**
+ * morph process into collaboration before adding
+ * participant onto collaboration
+ */
+
+ this.preExecute('shape.create', function(context) {
+
+ var parent = context.parent,
+ shape = context.shape,
+ position = context.position;
+
+ if (parent.businessObject.$instanceOf('bpmn:Process') &&
+ shape.businessObject.$instanceOf('bpmn:Participant')) {
+
+ // update shape size + position of new participant to
+ // encapsulate all existing children
+ if (parent.children.length) {
+ var bbox = getBoundingBox(parent.children);
+
+ assign(shape, {
+ width: bbox.width + 60,
+ height: bbox.height + 40
+ });
+
+ position = {
+ x: bbox.x - 40 + shape.width / 2,
+ y: bbox.y - 20 + shape.height / 2
+ };
+ }
+
+ // this is going to detach the process root
+ // and set the returned collaboration element
+ // as the new root element
+ var collaborationElement = modeling.makeCollaboration();
+
+ // monkey patch the create context
+ // so that the participant is being dropped
+ // onto the new collaboration root instead
+ context.position = position;
+ context.parent = collaborationElement;
+
+ context.processRoot = parent;
+ }
+ }, true);
+
+ this.execute('shape.create', function(context) {
+
+ var processRoot = context.processRoot,
+ shape = context.shape;
+
+ if (processRoot) {
+ context.oldProcessRef = shape.businessObject.processRef;
+
+ // assign the participant processRef
+ shape.businessObject.processRef = processRoot.businessObject;
+ }
+ }, 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.moveShapes(processChildren, { x: 0, y: 0 }, shape);
+ }
+ }, true);
+
+}
+
+CreateBehavior.$inject = [ 'eventBus', 'modeling' ];
+
+inherits(CreateBehavior, CommandInterceptor);
+
+module.exports = CreateBehavior;
\ No newline at end of file
diff --git a/lib/features/modeling/behavior/DropBehavior.js b/lib/features/modeling/behavior/DropBehavior.js
index e8c4dc67..469299e7 100644
--- a/lib/features/modeling/behavior/DropBehavior.js
+++ b/lib/features/modeling/behavior/DropBehavior.js
@@ -1,18 +1,20 @@
'use strict';
-var forEach = require('lodash/collection/forEach');
+var forEach = require('lodash/collection/forEach'),
+ inherits = require('inherits');
+var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor');
function DropBehavior(eventBus, modeling) {
- // sequence flow handling
+ CommandInterceptor.call(this, eventBus);
- eventBus.on([
- 'commandStack.shapes.move.postExecute'
- ], function(e) {
+ // remove sequence flows that should not be allowed
+ // after a move operation
- var context = e.context,
- closure = context.closure,
+ this.postExecute('shapes.move', function(context) {
+
+ var closure = context.closure,
allConnections = closure.allConnections;
forEach(allConnections, function(c) {
@@ -22,10 +24,11 @@ function DropBehavior(eventBus, modeling) {
modeling.removeConnection(c);
}
});
- });
-
+ }, true);
}
+inherits(DropBehavior, CommandInterceptor);
+
DropBehavior.$inject = [ 'eventBus', 'modeling' ];
module.exports = DropBehavior;
\ No newline at end of file
diff --git a/lib/features/modeling/behavior/RemoveBehavior.js b/lib/features/modeling/behavior/RemoveBehavior.js
new file mode 100644
index 00000000..3c07fe53
--- /dev/null
+++ b/lib/features/modeling/behavior/RemoveBehavior.js
@@ -0,0 +1,48 @@
+'use strict';
+
+var inherits = require('inherits');
+
+var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor');
+
+/**
+ * BPMN specific remove behavior
+ */
+function RemoveBehavior(eventBus, modeling) {
+
+ CommandInterceptor.call(this, eventBus);
+
+
+ /**
+ * morph collaboration diagram into process diagram
+ * after the last participant has been removed
+ */
+
+ this.preExecute('shape.delete', function(context) {
+
+ var shape = context.shape,
+ parent = shape.parent;
+
+ // activate the behavior if the shape to be removed
+ // is a participant
+ if (shape.businessObject.$instanceOf('bpmn:Participant')) {
+ context.collaborationRoot = parent;
+ }
+ }, true);
+
+ this.postExecute('shape.delete', function(context) {
+
+ var collaborationRoot = context.collaborationRoot;
+
+ if (collaborationRoot && collaborationRoot.businessObject.participants.length < 2) {
+ // replace empty collaboration with process diagram
+ modeling.makeProcess();
+ }
+ }, true);
+
+}
+
+RemoveBehavior.$inject = [ 'eventBus', 'modeling' ];
+
+inherits(RemoveBehavior, CommandInterceptor);
+
+module.exports = RemoveBehavior;
\ No newline at end of file
diff --git a/lib/features/modeling/behavior/index.js b/lib/features/modeling/behavior/index.js
index 68d9d2c2..3d4de43b 100644
--- a/lib/features/modeling/behavior/index.js
+++ b/lib/features/modeling/behavior/index.js
@@ -1,5 +1,7 @@
module.exports = {
- __init__: [ 'appendBehavior', 'dropBehavior' ],
+ __init__: [ 'appendBehavior', 'dropBehavior', 'createBehavior', 'removeBehavior' ],
appendBehavior: [ 'type', require('./AppendBehavior') ],
+ createBehavior: [ 'type', require('./CreateBehavior') ],
+ removeBehavior: [ 'type', require('./RemoveBehavior') ],
dropBehavior: [ 'type', require('./DropBehavior') ]
};
\ No newline at end of file
diff --git a/lib/features/modeling/cmd/UpdateCanvasRootHandler.js b/lib/features/modeling/cmd/UpdateCanvasRootHandler.js
new file mode 100644
index 00000000..0bcda9ff
--- /dev/null
+++ b/lib/features/modeling/cmd/UpdateCanvasRootHandler.js
@@ -0,0 +1,73 @@
+'use strict';
+
+var Collections = require('diagram-js/lib/util/Collections');
+
+
+function UpdateCanvasRootHandler(canvas, modeling) {
+ this._canvas = canvas;
+ this._modeling = modeling;
+}
+
+UpdateCanvasRootHandler.$inject = [ 'canvas', 'modeling' ];
+
+module.exports = UpdateCanvasRootHandler;
+
+
+UpdateCanvasRootHandler.prototype.execute = function(context) {
+
+ var canvas = this._canvas;
+
+ var newRoot = context.newRoot,
+ newRootBusinessObject = newRoot.businessObject,
+ oldRoot = canvas.getRootElement(),
+ oldRootBusinessObject = oldRoot.businessObject,
+ bpmnDefinitions = oldRootBusinessObject.$parent,
+ diPlane = oldRootBusinessObject.di;
+
+ // (1) replace process old <> new root
+ canvas.setRootElement(newRoot, true);
+
+ // (2) update root elements
+ Collections.add(bpmnDefinitions.rootElements, newRootBusinessObject);
+ newRootBusinessObject.$parent = bpmnDefinitions;
+
+ Collections.remove(bpmnDefinitions.rootElements, oldRootBusinessObject);
+ oldRootBusinessObject.$parent = null;
+
+ // (3) wire di
+ oldRootBusinessObject.di = null;
+
+ diPlane.bpmnElement = newRootBusinessObject;
+ newRootBusinessObject.di = diPlane;
+
+ context.oldRoot = oldRoot;
+};
+
+
+UpdateCanvasRootHandler.prototype.revert = function(context) {
+
+ var canvas = this._canvas;
+
+ var newRoot = context.newRoot,
+ newRootBusinessObject = newRoot.businessObject,
+ oldRoot = context.oldRoot,
+ oldRootBusinessObject = oldRoot.businessObject,
+ bpmnDefinitions = newRootBusinessObject.$parent,
+ diPlane = newRootBusinessObject.di;
+
+ // (1) replace process old <> new root
+ canvas.setRootElement(oldRoot, true);
+
+ // (2) update root elements
+ Collections.remove(bpmnDefinitions.rootElements, newRootBusinessObject);
+ newRootBusinessObject.$parent = null;
+
+ Collections.add(bpmnDefinitions.rootElements, oldRootBusinessObject);
+ oldRootBusinessObject.$parent = bpmnDefinitions;
+
+ // (3) wire di
+ newRootBusinessObject.di = null;
+
+ diPlane.bpmnElement = oldRootBusinessObject;
+ oldRootBusinessObject.di = diPlane;
+};
\ No newline at end of file
diff --git a/lib/features/modeling/rules/ModelingRules.js b/lib/features/modeling/rules/ModelingRules.js
index 41bf55a8..958b8d72 100644
--- a/lib/features/modeling/rules/ModelingRules.js
+++ b/lib/features/modeling/rules/ModelingRules.js
@@ -18,7 +18,6 @@ ModelingRules.$inject = [ 'eventBus' ];
module.exports = ModelingRules;
-
ModelingRules.prototype.init = function() {
function nonExistantOrLabel(element) {
@@ -173,15 +172,15 @@ ModelingRules.prototype.init = function() {
newBounds = context.newBounds,
bo = shape.businessObject;
- if (!bo.$instanceOf('bpmn:SubProcess') || !bo.di.isExpanded) {
- return false;
+ if (bo.$instanceOf('bpmn:SubProcess') && bo.di.isExpanded) {
+ return !newBounds || (newBounds.width >= 100 && newBounds.height >= 80);
}
- if (newBounds) {
- if (newBounds.width < 100 || newBounds.height < 80) {
- return false;
- }
+ if (bo.$instanceOf('bpmn:Participant')) {
+ return !newBounds || (newBounds.width >= 100 && newBounds.height >= 80);
}
+
+ return false;
});
/**
@@ -191,6 +190,14 @@ ModelingRules.prototype.init = function() {
*/
function canDrop(businessObject, targetBusinessObject, targetDi) {
+ // allow to create new participants on
+ // on existing collaboration and process diagrams
+ if (businessObject.$instanceOf('bpmn:Participant') &&
+ (targetBusinessObject.$instanceOf('bpmn:Process') ||
+ targetBusinessObject.$instanceOf('bpmn:Collaboration'))) {
+ return true;
+ }
+
if (businessObject.$instanceOf('bpmn:FlowElement') &&
targetBusinessObject.$instanceOf('bpmn:FlowElementsContainer')) {
@@ -202,15 +209,29 @@ ModelingRules.prototype.init = function() {
return true;
}
- if (businessObject.$instanceOf('bpmn:TextAnnotation') &&
- targetBusinessObject.$instanceOf('bpmn:FlowElementsContainer')) {
+ if (targetBusinessObject.$instanceOf('bpmn:Collaboration') &&
+ !businessObject.$instanceOf('bpmn:Participant') &&
+ !targetBusinessObject.participants.length) {
+ // TODO: transform collaboration into process
+ return false;
+ }
+
+ if (businessObject.$instanceOf('bpmn:FlowElement') &&
+ targetBusinessObject.$instanceOf('bpmn:Participant') &&
+ targetBusinessObject.processRef) {
+ return true;
+ }
+
+ if (businessObject.$instanceOf('bpmn:TextAnnotation') &&
+ (targetBusinessObject.$instanceOf('bpmn:FlowElementsContainer') ||
+ targetBusinessObject.$instanceOf('bpmn:Participant'))) {
return true;
}
if (businessObject.$instanceOf('bpmn:Association') &&
- targetBusinessObject.$instanceOf('bpmn:FlowElementsContainer')) {
-
+ (targetBusinessObject.$instanceOf('bpmn:FlowElementsContainer') ||
+ targetBusinessObject.$instanceOf('bpmn:Participant'))) {
return true;
}
diff --git a/lib/features/palette/PaletteProvider.js b/lib/features/palette/PaletteProvider.js
index 0625bb6f..95750fb0 100644
--- a/lib/features/palette/PaletteProvider.js
+++ b/lib/features/palette/PaletteProvider.js
@@ -20,6 +20,11 @@ PaletteProvider.$inject = [ 'palette', 'create', 'elementFactory' ];
PaletteProvider.prototype.getPaletteEntries = function(element) {
+ var actions = {},
+ create = this._create,
+ elementFactory = this._elementFactory;
+
+
function createAction(type, group, className, title, options) {
function createListener(event) {
@@ -43,9 +48,9 @@ PaletteProvider.prototype.getPaletteEntries = function(element) {
};
}
- var actions = {},
- create = this._create,
- elementFactory = this._elementFactory;
+ function createParticipant(event, collapsed) {
+ create.start(event, elementFactory.createParticipantShape(collapsed));
+ }
assign(actions, {
@@ -71,7 +76,16 @@ PaletteProvider.prototype.getPaletteEntries = function(element) {
'create.subprocess-expanded': createAction(
'bpmn:SubProcess', 'activity', 'icon-subprocess-expanded', 'Sub Process (expanded)',
{ isExpanded: true }
- )
+ ),
+ 'create.participant-expanded': {
+ group: 'collaboration',
+ className: 'icon-participant',
+ title: 'Create a participant',
+ action: {
+ dragstart: createParticipant,
+ click: createParticipant
+ }
+ }
});
return actions;
diff --git a/test/fixtures/bpmn/collaboration/collaboration-empty-participant.bpmn b/test/fixtures/bpmn/collaboration/collaboration-empty-participant.bpmn
new file mode 100644
index 00000000..f0fc4348
--- /dev/null
+++ b/test/fixtures/bpmn/collaboration/collaboration-empty-participant.bpmn
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/fixtures/bpmn/collaboration/collaboration-message-flows.bpmn b/test/fixtures/bpmn/collaboration/collaboration-message-flows.bpmn
new file mode 100644
index 00000000..ed430f52
--- /dev/null
+++ b/test/fixtures/bpmn/collaboration/collaboration-message-flows.bpmn
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+ SequenceFlow_1
+ SequenceFlow_2
+
+
+
+ SequenceFlow_1
+
+
+
+ SequenceFlow_2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/fixtures/bpmn/collaboration/collaboration-participant.bpmn b/test/fixtures/bpmn/collaboration/collaboration-participant.bpmn
new file mode 100644
index 00000000..2aebf233
--- /dev/null
+++ b/test/fixtures/bpmn/collaboration/collaboration-participant.bpmn
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+ SequenceFlow_1
+ SequenceFlow_2
+
+
+
+ SequenceFlow_1
+
+
+
+ SequenceFlow_2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/fixtures/bpmn/collaboration/process-empty.bpmn b/test/fixtures/bpmn/collaboration/process-empty.bpmn
new file mode 100644
index 00000000..b281085c
--- /dev/null
+++ b/test/fixtures/bpmn/collaboration/process-empty.bpmn
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/fixtures/bpmn/collaboration/process.bpmn b/test/fixtures/bpmn/collaboration/process.bpmn
new file mode 100644
index 00000000..1ef9119b
--- /dev/null
+++ b/test/fixtures/bpmn/collaboration/process.bpmn
@@ -0,0 +1,48 @@
+
+
+
+
+ SequenceFlow_1
+ SequenceFlow_2
+
+
+
+ SequenceFlow_1
+
+
+
+ SequenceFlow_2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/spec/features/modeling/CreateParticipantSpec.js b/test/spec/features/modeling/CreateParticipantSpec.js
new file mode 100644
index 00000000..f7695323
--- /dev/null
+++ b/test/spec/features/modeling/CreateParticipantSpec.js
@@ -0,0 +1,200 @@
+'use strict';
+
+var TestHelper = require('../../../TestHelper');
+
+/* global bootstrapModeler, inject */
+
+
+var modelingModule = require('../../../../lib/features/modeling'),
+ coreModule = require('../../../../lib/core');
+
+
+describe('features/modeling - create participant', function() {
+
+ var testModules = [ coreModule, modelingModule ];
+
+
+ describe('on process diagram', function() {
+
+ describe('should transform diagram into collaboration', function() {
+
+ var processDiagramXML = require('../../../fixtures/bpmn/collaboration/process-empty.bpmn');
+
+ beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules }));
+
+
+ it('execute', inject(function(modeling, elementFactory, canvas) {
+
+ // given
+ var processShape = canvas.getRootElement(),
+ process = processShape.businessObject,
+ participantShape = elementFactory.createParticipantShape(true),
+ participant = participantShape.businessObject,
+ diRoot = process.di.$parent;
+
+ // when
+ modeling.createShape(participantShape, { x: 350, y: 200 }, processShape);
+
+ // then
+ expect(participant.processRef).toBe(process);
+
+ var collaborationRoot = canvas.getRootElement(),
+ collaboration = collaborationRoot.businessObject,
+ collaborationDi = collaboration.di;
+
+ expect(collaboration.$instanceOf('bpmn:Collaboration')).toBe(true);
+
+ // participant / collaboration are wired
+ expect(participant.$parent).toBe(collaboration);
+ expect(collaboration.participants).toContain(participant);
+
+
+ // collaboration is added to root elements
+ expect(collaboration.$parent).toBe(process.$parent);
+
+ // di is wired
+ var participantDi = participant.di;
+
+ expect(participantDi.$parent).toBe(collaborationDi);
+ expect(collaborationDi.$parent).toBe(diRoot);
+ }));
+
+
+ it('undo', inject(function(modeling, elementFactory, canvas, commandStack) {
+
+ // 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;
+
+ modeling.createShape(participantShape, { x: 350, y: 200 }, processShape);
+
+ var collaborationRoot = canvas.getRootElement(),
+ collaboration = collaborationRoot.businessObject;
+
+ // when
+ commandStack.undo();
+
+
+ // then
+ expect(participant.processRef).toBe(oldParticipantProcessRef);
+
+ expect(participant.$parent).toBe(null);
+ expect(collaboration.participants).not.toContain(participant);
+
+ // collaboration is detached
+ expect(collaboration.$parent).toBe(null);
+
+ // di is wired
+ expect(processDi.$parent).toBe(diRoot);
+ }));
+
+ });
+
+
+ describe('should wrap existing elements', function() {
+
+ var processDiagramXML = require('../../../fixtures/bpmn/collaboration/process.bpmn');
+
+ beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules }));
+
+
+ it('execute', inject(function(modeling, elementFactory, canvas) {
+
+ // given
+ var processShape = canvas.getRootElement(),
+ process = processShape.businessObject,
+ participantShape = elementFactory.createParticipantShape(true),
+ participant = participantShape.businessObject;
+
+ // when
+ modeling.createShape(participantShape, { x: 350, y: 200 }, processShape);
+
+ // then
+ expect(participant.processRef).toBe(process);
+
+ var newRootShape = canvas.getRootElement(),
+ collaboration = newRootShape.businessObject;
+
+ expect(collaboration.$instanceOf('bpmn:Collaboration')).toBe(true);
+
+ expect(participant.$parent).toBe(collaboration);
+ expect(collaboration.participants).toContain(participant);
+ }));
+
+
+ it('undo', inject(function(modeling, elementFactory, canvas, commandStack) {
+
+ // given
+ var processShape = canvas.getRootElement(),
+ participantShape = elementFactory.createParticipantShape(true);
+
+ modeling.createShape(participantShape, { x: 350, y: 200 }, processShape);
+
+ // when
+ commandStack.undo();
+
+
+ // then
+ expect(participantShape.children.length).toBe(0);
+ expect(processShape.children.length).toBe(9);
+ }));
+
+ });
+
+ });
+
+
+ describe('should add to collaboration', function() {
+
+ var collaborationDiagramXML = require('../../../fixtures/bpmn/collaboration/collaboration-participant.bpmn');
+
+ beforeEach(bootstrapModeler(collaborationDiagramXML, { modules: testModules }));
+
+
+ it('execute', inject(function(modeling, elementFactory, canvas) {
+
+ // given
+ var collaborationRoot = canvas.getRootElement(),
+ collaboration = collaborationRoot.businessObject,
+ participantShape = elementFactory.createParticipantShape(true),
+ participant = participantShape.businessObject;
+
+ // when
+ modeling.createShape(participantShape, { x: 350, y: 500 }, collaborationRoot);
+
+ // then
+ expect(collaborationRoot.children).toContain(participantShape);
+
+ expect(participant.$parent).toBe(collaboration);
+ expect(collaboration.participants).toContain(participant);
+ }));
+
+
+ it('undo', inject(function(modeling, elementFactory, canvas, commandStack) {
+
+ // given
+ var collaborationRoot = canvas.getRootElement(),
+ collaboration = collaborationRoot.businessObject,
+ participantShape = elementFactory.createParticipantShape(true),
+ participant = participantShape.businessObject;
+
+ modeling.createShape(participantShape, { x: 350, y: 500 }, collaborationRoot);
+
+ // when
+ commandStack.undo();
+
+ // then
+ expect(collaborationRoot.children).not.toContain(participantShape);
+
+ expect(participant.$parent).toBeFalsy();
+ expect(collaboration.participants).not.toContain(participant);
+ }));
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/spec/features/modeling/DeleteParticipantSpec.js b/test/spec/features/modeling/DeleteParticipantSpec.js
new file mode 100644
index 00000000..b5533d4d
--- /dev/null
+++ b/test/spec/features/modeling/DeleteParticipantSpec.js
@@ -0,0 +1,97 @@
+'use strict';
+
+var TestHelper = require('../../../TestHelper');
+
+/* global bootstrapModeler, inject */
+
+
+var modelingModule = require('../../../../lib/features/modeling'),
+ coreModule = require('../../../../lib/core');
+
+
+describe('features/modeling - delete participant', function() {
+
+ var testModules = [ coreModule, modelingModule ];
+
+
+ describe('last remaining', function() {
+
+ describe('should transform diagram into process diagram', function() {
+
+ var processDiagramXML = require('../../../fixtures/bpmn/collaboration/collaboration-empty-participant.bpmn');
+
+ beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules }));
+
+
+ it('execute', inject(function(modeling, elementRegistry, canvas) {
+
+ // given
+ var participantShape = elementRegistry.get('_Participant_2'),
+ participant = participantShape.businessObject,
+ participantDi = participant.di,
+ process = participant.processRef,
+ collaborationElement = participantShape.parent,
+ collaboration = collaborationElement.businessObject,
+ diPlane = collaboration.di,
+ bpmnDefinitions = collaboration.$parent;
+
+ // when
+ modeling.removeShape(participantShape);
+
+ // then
+ expect(participant.$parent).toBeFalsy();
+
+ var newRootShape = canvas.getRootElement(),
+ newRootBusinessObject = newRootShape.businessObject;
+
+ expect(newRootBusinessObject.$instanceOf('bpmn:Process')).toBe(true);
+
+ // collaboration DI is unwired
+ expect(participantDi.$parent).toBeFalsy();
+ expect(collaboration.di).toBeFalsy();
+
+ expect(bpmnDefinitions.rootElements).not.toContain(process);
+ expect(bpmnDefinitions.rootElements).not.toContain(collaboration);
+
+ // process DI is wired
+ expect(diPlane.bpmnElement).toBe(newRootBusinessObject);
+ expect(newRootBusinessObject.di).toBe(diPlane);
+
+ expect(bpmnDefinitions.rootElements).toContain(newRootBusinessObject);
+ }));
+
+
+ it('undo', inject(function(modeling, elementRegistry, canvas, commandStack) {
+
+ // given
+ var participantShape = elementRegistry.get('_Participant_2'),
+ participant = participantShape.businessObject,
+ originalRootElement = participantShape.parent,
+ originalRootElementBo = originalRootElement.businessObject,
+ bpmnDefinitions = originalRootElementBo.$parent,
+ participantDi = participant.di,
+ diPlane = participantDi.$parent;
+
+ modeling.removeShape(participantShape);
+
+ // when
+ commandStack.undo();
+
+ // then
+ expect(participant.$parent).toBe(originalRootElementBo);
+ expect(originalRootElementBo.$parent).toBe(bpmnDefinitions);
+
+ expect(canvas.getRootElement()).toBe(originalRootElement);
+
+ // di is unwired
+ expect(participantDi.$parent).toBe(originalRootElementBo.di);
+
+ // new di is wired
+ expect(diPlane.bpmnElement).toBe(originalRootElementBo);
+ }));
+
+ });
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/spec/features/palette/PaletteProviderSpec.js b/test/spec/features/palette/PaletteProviderSpec.js
index 71590a76..d56865d4 100644
--- a/test/spec/features/palette/PaletteProviderSpec.js
+++ b/test/spec/features/palette/PaletteProviderSpec.js
@@ -32,7 +32,7 @@ describe('palette', function() {
var paletteElement = domQuery('.djs-palette', container);
// then
- expect(domQuery.all('.entry', paletteElement).length).toBe(7);
+ expect(domQuery.all('.entry', paletteElement).length).toBe(8);
done(err);
});