From 08c2ebc78f0731023d3d95b91bb498b26c0fe478 Mon Sep 17 00:00:00 2001 From: pedesen Date: Tue, 6 Oct 2015 12:33:21 +0200 Subject: [PATCH] feat(modeling): add data objects Closes #344 --- .../context-pad/ContextPadProvider.js | 14 + lib/features/modeling/BpmnFactory.js | 1 + lib/features/modeling/BpmnUpdater.js | 76 ++++- lib/features/modeling/ElementFactory.js | 4 + lib/features/modeling/Modeling.js | 15 +- .../behavior/CreateDataObjectBehavior.js | 37 +++ lib/features/modeling/behavior/index.js | 2 + lib/features/palette/PaletteProvider.js | 3 + lib/features/rules/BpmnRules.js | 34 +- ...bjectBehavior.create-data-association.bpmn | 21 ++ ...aObjectBehavior.data-object-reference.bpmn | 13 + ...bjectBehavior.remove-data-association.bpmn | 41 +++ .../behavior/CreateDataObjectBehaviorSpec.js | 300 ++++++++++++++++++ .../ReconnectConnection.data-association.bpmn | 41 +++ .../behavior/ReconnectConnectionSpec.js | 120 +++++++ .../features/palette/PaletteProviderSpec.js | 2 +- .../features/rules/BpmnRules.process.bpmn | 8 + test/spec/features/rules/BpmnRulesSpec.js | 203 +++++++++--- test/spec/features/rules/Helper.js | 6 +- 19 files changed, 860 insertions(+), 81 deletions(-) create mode 100644 lib/features/modeling/behavior/CreateDataObjectBehavior.js create mode 100644 test/spec/features/modeling/behavior/CreateDataObjectBehavior.create-data-association.bpmn create mode 100644 test/spec/features/modeling/behavior/CreateDataObjectBehavior.data-object-reference.bpmn create mode 100644 test/spec/features/modeling/behavior/CreateDataObjectBehavior.remove-data-association.bpmn create mode 100644 test/spec/features/modeling/behavior/CreateDataObjectBehaviorSpec.js create mode 100644 test/spec/features/modeling/behavior/ReconnectConnection.data-association.bpmn create mode 100644 test/spec/features/modeling/behavior/ReconnectConnectionSpec.js diff --git a/lib/features/context-pad/ContextPadProvider.js b/lib/features/context-pad/ContextPadProvider.js index 0b027515..c6a36a94 100644 --- a/lib/features/context-pad/ContextPadProvider.js +++ b/lib/features/context-pad/ContextPadProvider.js @@ -183,6 +183,20 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) { }); } + if (is(bpmnElement, 'bpmn:DataObjectReference')) { + assign(actions, { + 'connect': { + group: 'connect', + className: 'icon-connection-multi', + title: 'Connect using DataInputAssociation', + action: { + click: startConnect, + dragstart: startConnect + } + } + }); + } + // Delete Element Entry assign(actions, { 'delete': { diff --git a/lib/features/modeling/BpmnFactory.js b/lib/features/modeling/BpmnFactory.js index 5b691924..b0d1538f 100644 --- a/lib/features/modeling/BpmnFactory.js +++ b/lib/features/modeling/BpmnFactory.js @@ -16,6 +16,7 @@ BpmnFactory.prototype._needsId = function(element) { return element.$instanceOf('bpmn:RootElement') || element.$instanceOf('bpmn:FlowElement') || element.$instanceOf('bpmn:MessageFlow') || + element.$instanceOf('bpmn:DataAssociation') || element.$instanceOf('bpmn:Artifact') || element.$instanceOf('bpmn:Participant') || element.$instanceOf('bpmn:Lane') || diff --git a/lib/features/modeling/BpmnUpdater.js b/lib/features/modeling/BpmnUpdater.js index 57f4d8af..066de58f 100644 --- a/lib/features/modeling/BpmnUpdater.js +++ b/lib/features/modeling/BpmnUpdater.js @@ -276,8 +276,28 @@ BpmnUpdater.prototype.updateParent = function(element, oldParent) { this.updateFlowNodeRefs(businessObject, parentBusinessObject, oldParent && oldParent.businessObject); } + if (is(element, 'bpmn:DataOutputAssociation')) { + if (element.source) { + parentBusinessObject = element.source.businessObject; + } else { + parentBusinessObject = null; + } + } + + if (is(element, 'bpmn:DataInputAssociation')) { + if (element.target) { + parentBusinessObject = element.target.businessObject; + } else { + parentBusinessObject = null; + } + } + this.updateSemanticParent(businessObject, parentBusinessObject); + if (is(element, 'bpmn:DataObjectReference') && businessObject.dataObjectRef) { + this.updateSemanticParent(businessObject.dataObjectRef, parentBusinessObject); + } + this.updateDiParent(businessObject.di, parentDi); }; @@ -462,6 +482,14 @@ BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent) process.$parent = definitions; } } + } else + + if (is(businessObject, 'bpmn:DataOutputAssociation')) { + containment = 'dataOutputAssociations'; + } else + + if (is(businessObject, 'bpmn:DataInputAssociation')) { + containment = 'dataInputAssociations'; } if (!containment) { @@ -500,28 +528,48 @@ BpmnUpdater.prototype.updateConnection = function(context) { newSource = getBusinessObject(connection.source), newTarget = getBusinessObject(connection.target); - var inverseSet = is(businessObject, 'bpmn:SequenceFlow'); + if (!is(businessObject, 'bpmn:DataAssociation')) { - if (businessObject.sourceRef !== newSource) { - if (inverseSet) { - Collections.remove(businessObject.sourceRef && businessObject.sourceRef.get('outgoing'), businessObject); + var inverseSet = is(businessObject, 'bpmn:SequenceFlow'); - if (newSource && newSource.get('outgoing')) { - newSource.get('outgoing').push(businessObject); + if (businessObject.sourceRef !== newSource) { + if (inverseSet) { + Collections.remove(businessObject.sourceRef && businessObject.sourceRef.get('outgoing'), businessObject); + + if (newSource && newSource.get('outgoing')) { + newSource.get('outgoing').push(businessObject); + } } + + businessObject.sourceRef = newSource; } - businessObject.sourceRef = newSource; - } - if (businessObject.targetRef !== newTarget) { - if (inverseSet) { - Collections.remove(businessObject.targetRef && businessObject.targetRef.get('incoming'), businessObject); + if (businessObject.targetRef !== newTarget) { + if (inverseSet) { + Collections.remove(businessObject.targetRef && businessObject.targetRef.get('incoming'), businessObject); - if (newTarget && newTarget.get('incoming')) { - newTarget.get('incoming').push(businessObject); + if (newTarget && newTarget.get('incoming')) { + newTarget.get('incoming').push(businessObject); + } } - } + businessObject.targetRef = newTarget; + } + } else + + if (is(businessObject, 'bpmn:DataInputAssociation')) { + // handle obnoxious isMany sourceRef + businessObject.get('sourceRef')[0] = newSource; + + // targetRef = new parent + this.updateSemanticParent(businessObject, newTarget); + } else + + if (is(businessObject, 'bpmn:DataOutputAssociation')) { + // sourceRef = new parent + this.updateSemanticParent(businessObject, newSource); + + // targetRef = new target businessObject.targetRef = newTarget; } diff --git a/lib/features/modeling/ElementFactory.js b/lib/features/modeling/ElementFactory.js index eba30bbe..3596cad9 100644 --- a/lib/features/modeling/ElementFactory.js +++ b/lib/features/modeling/ElementFactory.js @@ -132,6 +132,10 @@ ElementFactory.prototype._getDefaultSize = function(semantic) { return { width: 400, height: 100 }; } + if (semantic.$instanceOf('bpmn:DataObjectReference')) { + return { width: 36, height: 50 }; + } + return { width: 100, height: 80 }; }; diff --git a/lib/features/modeling/Modeling.js b/lib/features/modeling/Modeling.js index eb993653..9197018e 100644 --- a/lib/features/modeling/Modeling.js +++ b/lib/features/modeling/Modeling.js @@ -52,20 +52,7 @@ Modeling.prototype.connect = function(source, target, attrs) { var bpmnRules = this._bpmnRules; if (!attrs) { - if (bpmnRules.canConnectMessageFlow(source, target)) { - attrs = { - type: 'bpmn:MessageFlow' - }; - } else - if (bpmnRules.canConnectSequenceFlow(source, target)) { - attrs = { - type: 'bpmn:SequenceFlow' - }; - } else { - attrs = { - type: 'bpmn:Association' - }; - } + attrs = bpmnRules.canConnect(source, target) || { type: 'bpmn:Association' }; } return this.createConnection(source, target, attrs, source.parent); diff --git a/lib/features/modeling/behavior/CreateDataObjectBehavior.js b/lib/features/modeling/behavior/CreateDataObjectBehavior.js new file mode 100644 index 00000000..105752ed --- /dev/null +++ b/lib/features/modeling/behavior/CreateDataObjectBehavior.js @@ -0,0 +1,37 @@ +'use strict'; + +var inherits = require('inherits'); + +var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor'); + +var is = require('../../../util/ModelUtil').is; + +/** + * BPMN specific create data object behavior + */ +function CreateDataObjectBehavior(eventBus, bpmnFactory, moddle) { + + CommandInterceptor.call(this, eventBus); + + this.preExecute('shape.create', function(event) { + + var context = event.context, + shape = context.shape; + + if(is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') { + + // create a DataObject every time a DataObjectReference is created + var dataObjectShape = bpmnFactory.create('bpmn:DataObject'); + + // set the reference to the DataObject + shape.businessObject.dataObjectRef = dataObjectShape; + } + }); + +} + +CreateDataObjectBehavior.$inject = [ 'eventBus', 'bpmnFactory', 'moddle' ]; + +inherits(CreateDataObjectBehavior, CommandInterceptor); + +module.exports = CreateDataObjectBehavior; diff --git a/lib/features/modeling/behavior/index.js b/lib/features/modeling/behavior/index.js index 9a01af93..514d1197 100644 --- a/lib/features/modeling/behavior/index.js +++ b/lib/features/modeling/behavior/index.js @@ -2,6 +2,7 @@ module.exports = { __init__: [ 'appendBehavior', 'createBoundaryEventBehavior', + 'createDataObjectBehavior', 'createLaneBehavior', 'createOnFlowBehavior', 'createParticipantBehavior', @@ -12,6 +13,7 @@ module.exports = { ], appendBehavior: [ 'type', require('./AppendBehavior') ], createBoundaryEventBehavior: [ 'type', require('./CreateBoundaryEventBehavior') ], + createDataObjectBehavior: [ 'type', require('./CreateDataObjectBehavior') ], createLaneBehavior: [ 'type', require('./CreateLaneBehavior') ], createOnFlowBehavior: [ 'type', require('./CreateOnFlowBehavior') ], createParticipantBehavior: [ 'type', require('./CreateParticipantBehavior') ], diff --git a/lib/features/palette/PaletteProvider.js b/lib/features/palette/PaletteProvider.js index bf387f93..2b61e302 100644 --- a/lib/features/palette/PaletteProvider.js +++ b/lib/features/palette/PaletteProvider.js @@ -102,6 +102,9 @@ PaletteProvider.prototype.getPaletteEntries = function(element) { 'create.task': createAction( 'bpmn:Task', 'activity', 'icon-task' ), + 'create.data-object': createAction( + 'bpmn:DataObjectReference', 'data-object', 'icon-data-object' + ), 'create.subprocess-expanded': createAction( 'bpmn:SubProcess', 'activity', 'icon-subprocess-expanded', 'Create expanded SubProcess', { isExpanded: true } diff --git a/lib/features/rules/BpmnRules.js b/lib/features/rules/BpmnRules.js index df0ee7fc..102b02bb 100644 --- a/lib/features/rules/BpmnRules.js +++ b/lib/features/rules/BpmnRules.js @@ -96,6 +96,8 @@ BpmnRules.prototype.canConnectMessageFlow = canConnectMessageFlow; BpmnRules.prototype.canConnectSequenceFlow = canConnectSequenceFlow; +BpmnRules.prototype.canConnectDataAssociation = canConnectDataAssociation; + BpmnRules.prototype.canConnectAssociation = canConnectAssociation; BpmnRules.prototype.canMove = canMove; @@ -268,14 +270,24 @@ function canConnect(source, target, connection) { return false; } - if (canConnectMessageFlow(source, target) || - canConnectSequenceFlow(source, target)) { - return true; + if (canConnectMessageFlow(source, target) && !is(connection, 'bpmn:DataAssociation')) { + return { type: 'bpmn:MessageFlow' }; } + if (canConnectSequenceFlow(source, target) && !is(connection, 'bpmn:DataAssociation')) { + return { type: 'bpmn:SequenceFlow' }; + } - if (is(connection, 'bpmn:Association')) { - return canConnectAssociation(source, target); + var dataAssociation = canConnectDataAssociation(source, target); + + if (dataAssociation && (!connection || is(connection, 'bpmn:DataAssociation'))) { + return dataAssociation; + } + + if (is(connection, 'bpmn:Association') && !is(connection, 'bpmn:DataAssociation')) { + if (canConnectAssociation(source, target)) { + return { type: 'bpmn:Association' }; + } } return false; @@ -556,6 +568,18 @@ function canConnectSequenceFlow(source, target) { !(is(source, 'bpmn:EventBasedGateway') && !isEventBasedTarget(target)); } +function canConnectDataAssociation(source, target) { + if (is(source, 'bpmn:DataObjectReference') && is(target, 'bpmn:Activity')) { + return { type: 'bpmn:DataInputAssociation' }; + } + + if (is(target, 'bpmn:DataObjectReference') && is(source, 'bpmn:Activity')) { + return { type: 'bpmn:DataOutputAssociation' }; + } + + return false; +} + function canInsert(shape, flow, position) { // return true if we can drop on the diff --git a/test/spec/features/modeling/behavior/CreateDataObjectBehavior.create-data-association.bpmn b/test/spec/features/modeling/behavior/CreateDataObjectBehavior.create-data-association.bpmn new file mode 100644 index 00000000..140533e3 --- /dev/null +++ b/test/spec/features/modeling/behavior/CreateDataObjectBehavior.create-data-association.bpmn @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/modeling/behavior/CreateDataObjectBehavior.data-object-reference.bpmn b/test/spec/features/modeling/behavior/CreateDataObjectBehavior.data-object-reference.bpmn new file mode 100644 index 00000000..668542f0 --- /dev/null +++ b/test/spec/features/modeling/behavior/CreateDataObjectBehavior.data-object-reference.bpmn @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/test/spec/features/modeling/behavior/CreateDataObjectBehavior.remove-data-association.bpmn b/test/spec/features/modeling/behavior/CreateDataObjectBehavior.remove-data-association.bpmn new file mode 100644 index 00000000..c27cf263 --- /dev/null +++ b/test/spec/features/modeling/behavior/CreateDataObjectBehavior.remove-data-association.bpmn @@ -0,0 +1,41 @@ + + + + + + DataObjectReference_1 + + + + + + + DataObjectReference_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/modeling/behavior/CreateDataObjectBehaviorSpec.js b/test/spec/features/modeling/behavior/CreateDataObjectBehaviorSpec.js new file mode 100644 index 00000000..d2b61908 --- /dev/null +++ b/test/spec/features/modeling/behavior/CreateDataObjectBehaviorSpec.js @@ -0,0 +1,300 @@ +'use strict'; + +var TestHelper = require('../../../../TestHelper'); +/* global bootstrapModeler, inject */ + +var is = require('../../../../../lib/util/ModelUtil').is; + +var modelingModule = require('../../../../../lib/features/modeling'), + coreModule = require('../../../../../lib/core'); + + +describe('features/modeling/behavior - data objects -', function() { + + var testModules = [ coreModule, modelingModule ]; + + var rootShape; + + describe('DataObjectReference', function() { + var processDiagramXML = require('./CreateDataObjectBehavior.data-object-reference.bpmn'); + + beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules })); + + var subProcess1; + + beforeEach(inject(function(canvas, elementRegistry) { + subProcess1 = elementRegistry.get('SubProcess_1'); + rootShape = canvas.getRootElement(); + })); + + it('should create the corresponding DataObject', inject(function(modeling) { + + // when + var dataObjectRefShape = modeling.createShape({ type: 'bpmn:DataObjectReference' }, + { x: 220, y: 220 }, rootShape); + + var dataObject = dataObjectRefShape.businessObject.dataObjectRef; + + // then + expect(dataObject).to.exist; + expect(is(dataObject, 'bpmn:DataObject')).to.be.true; + expect(dataObject.id).to.exist; + + })); + + + it('should have the right parents', inject(function(modeling) { + + // when + var dataObjectRefShape1 = modeling.createShape({ type: 'bpmn:DataObjectReference' }, + { x: 220, y: 220 }, rootShape); + var dataObjectRefShape2 = modeling.createShape({ type: 'bpmn:DataObjectReference' }, + { x: 380, y: 220 }, subProcess1); + + var dataObject1 = dataObjectRefShape1.businessObject.dataObjectRef; + var dataObject2 = dataObjectRefShape2.businessObject.dataObjectRef; + + // then + expect(dataObject1.$parent.id).to.equal(rootShape.id); + expect(dataObjectRefShape1.parent.id).to.equal(rootShape.id); + + expect(dataObject2.$parent.id).to.equal(subProcess1.id); + expect(dataObjectRefShape2.parent.id).to.equal(subProcess1.id); + + })); + + }); + + describe('create', function() { + + var processDiagramXML = require('./CreateDataObjectBehavior.create-data-association.bpmn'); + + beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules })); + + var dataObjectRefShape1, + taskShape; + + beforeEach(inject(function(canvas, elementRegistry) { + + rootShape = canvas.getRootElement(); + dataObjectRefShape1 = elementRegistry.get('DataObjectReference_1'); + taskShape = elementRegistry.get('Task_1'); + + })); + + describe('dataOutputAssociation', function() { + + it('should execute', inject(function(modeling) { + + // when + var outputAssociation = modeling.connect(taskShape, dataObjectRefShape1); + + var dataOutputAssociations = taskShape.businessObject.get('dataOutputAssociations'); + + // then + expect(dataOutputAssociations[0].$parent).to.equal(taskShape.businessObject); + expect(dataOutputAssociations).to.include(outputAssociation.businessObject); + expect(taskShape.businessObject.get('dataInputAssociations')).to.be.empty; + + })); + + + it('should undo', inject(function(modeling, commandStack) { + + // when + modeling.connect(taskShape, dataObjectRefShape1); + commandStack.undo(); + + // then + expect(taskShape.businessObject.get('dataOutputAssociations')).to.be.empty; + expect(taskShape.businessObject.get('dataInputAssociations')).to.be.empty; + + })); + + + it('should redo', inject(function(modeling, commandStack) { + + // when + var outputAssociation = modeling.connect(taskShape, dataObjectRefShape1); + commandStack.undo(); + commandStack.redo(); + + var dataOutputAssociations = taskShape.businessObject.get('dataOutputAssociations'); + + // then + expect(dataOutputAssociations[0].$parent).to.equal(taskShape.businessObject); + expect(dataOutputAssociations).to.include(outputAssociation.businessObject); + expect(taskShape.businessObject.get('dataInputAssociations')).to.be.empty; + + })); + + }); + + describe('dataInputAssociation', function() { + + it('should execute', inject(function(modeling) { + + // when + var inputAssociation = modeling.connect(dataObjectRefShape1, taskShape); + + var dataInputAssociations = taskShape.businessObject.get('dataInputAssociations'); + + // then + expect(dataInputAssociations[0].$parent).to.equal(taskShape.businessObject); + expect(dataInputAssociations).to.include(inputAssociation.businessObject); + expect(taskShape.businessObject.get('dataOutputAssociations')).to.be.empty; + + })); + + + it('should undo', inject(function(modeling, commandStack) { + + // when + modeling.connect(dataObjectRefShape1, taskShape); + commandStack.undo(); + + // then + expect(taskShape.businessObject.get('dataOutputAssociations')).to.be.empty; + expect(taskShape.businessObject.get('dataInputAssociations')).to.be.empty; + + })); + + + it('should redo', inject(function(modeling, commandStack) { + + // when + var inputAssociation = modeling.connect(dataObjectRefShape1, taskShape); + commandStack.undo(); + commandStack.redo(); + + var dataInputAssociations = taskShape.businessObject.get('dataInputAssociations'); + + // then + expect(dataInputAssociations[0].$parent).to.equal(taskShape.businessObject); + expect(dataInputAssociations).to.include(inputAssociation.businessObject); + expect(taskShape.businessObject.get('dataOutputAssociations')).to.be.empty; + + })); + + }); + + }); + + describe('remove', function() { + + var processDiagramXML = require('./CreateDataObjectBehavior.remove-data-association.bpmn'); + + beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules })); + + var task1Shape, + task2Shape, + outputAssociation, + inputAssociation; + + beforeEach(inject(function(canvas, elementRegistry) { + + rootShape = canvas.getRootElement(); + task1Shape = elementRegistry.get('Task_1'); + task2Shape = elementRegistry.get('Task_2'); + outputAssociation = elementRegistry.get('DataOutputAssociation_1'); + inputAssociation = elementRegistry.get('DataInputAssociation_1'); + + })); + + describe('DataOutputAssociation', function() { + + it('should execute', inject(function(modeling) { + + // when + modeling.removeConnection(outputAssociation); + + // then + expect(task1Shape.businessObject.get('dataOutputAssociations')).to.be.empty; + expect(task1Shape.businessObject.get('dataInputAssociations')).to.be.empty; + + })); + + + it('should undo', inject(function(modeling, commandStack) { + + // when + modeling.removeConnection(outputAssociation); + commandStack.undo(); + + var dataOutputAssociations = task1Shape.businessObject.get('dataOutputAssociations'); + + // then + expect(dataOutputAssociations[0].$parent).to.equal(task1Shape.businessObject); + expect(dataOutputAssociations).to.be.include(outputAssociation.businessObject); + expect(task1Shape.businessObject.get('dataInputAssociations')).to.be.empty; + + })); + + + it('should redo', inject(function(modeling, commandStack) { + + // when + modeling.removeConnection(outputAssociation); + commandStack.undo(); + commandStack.redo(); + + // then + expect(task1Shape.businessObject.get('dataOutputAssociations')).to.be.empty; + expect(task1Shape.businessObject.get('dataInputAssociations')).to.be.empty; + + })); + + }); + + describe('dataInputAssociation', function() { + + it('should execute', inject(function(modeling) { + + // when + modeling.removeConnection(inputAssociation); + + // then + expect(task2Shape.businessObject.get('dataInputAssociations')).to.be.empty; + expect(task2Shape.businessObject.get('dataOutputAssociations')).to.be.empty; + + })); + + + it('should undo', inject(function(modeling, commandStack) { + + // when + modeling.removeConnection(inputAssociation); + commandStack.undo(); + + var dataInputAssociations = task2Shape.businessObject.get('dataInputAssociations'); + + // then + expect(dataInputAssociations[0].$parent).to.equal(task2Shape.businessObject); + expect(dataInputAssociations).to.include(inputAssociation.businessObject); + expect(task2Shape.businessObject.get('dataOutputAssociations')).to.be.empty; + + })); + + + it('should redo', inject(function(modeling, commandStack) { + + // when + modeling.removeConnection(inputAssociation); + commandStack.undo(); + commandStack.redo(); + + // then + expect(task2Shape.businessObject.get('dataInputAssociations')).to.be.empty; + expect(task2Shape.businessObject.get('dataOutputAssociations')).to.be.empty; + + })); + + }); + + }); + + describe('reconnect', function() { + + }); + +}); diff --git a/test/spec/features/modeling/behavior/ReconnectConnection.data-association.bpmn b/test/spec/features/modeling/behavior/ReconnectConnection.data-association.bpmn new file mode 100644 index 00000000..256300e3 --- /dev/null +++ b/test/spec/features/modeling/behavior/ReconnectConnection.data-association.bpmn @@ -0,0 +1,41 @@ + + + + + + DataObjectReference_1 + + + + + DataObjectReference_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/modeling/behavior/ReconnectConnectionSpec.js b/test/spec/features/modeling/behavior/ReconnectConnectionSpec.js new file mode 100644 index 00000000..25d63616 --- /dev/null +++ b/test/spec/features/modeling/behavior/ReconnectConnectionSpec.js @@ -0,0 +1,120 @@ +'use strict'; + +var TestHelper = require('../../../../TestHelper'); + +/* global bootstrapModeler, inject */ + +var modelingModule = require('../../../../../lib/features/modeling'), + coreModule = require('../../../../../lib/core'); + + +describe('features/modeling - reconnect connection', function() { + + var testModules = [ coreModule, modelingModule ]; + + var processDiagramXML = require('./ReconnectConnection.data-association.bpmn'); + + beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules })); + + var task1Shape, + task2Shape, + dataInputAssociation, + dataOutputAssociation, + newWaypoints; + + beforeEach(inject(function(elementRegistry) { + task1Shape = elementRegistry.get('Task_1'); + task2Shape = elementRegistry.get('Task_2'); + dataInputAssociation = elementRegistry.get('DataInputAssociation_1'); + dataOutputAssociation = elementRegistry.get('DataOutputAssociation_1'); + })); + + + describe('reconnect DataOutputAssociations', function() { + + beforeEach(function(){ + newWaypoints = [{ x: task2Shape.x, y: task2Shape.y+30 }, dataOutputAssociation.waypoints[1]]; + }); + + it('should execute', inject(function(modeling){ + + // when + modeling.reconnectStart(dataOutputAssociation, task2Shape, newWaypoints); + + // then + expect(task1Shape.businessObject.dataOutputAssociations).to.be.empty; + expect(task2Shape.businessObject.dataOutputAssociations).to.include(dataOutputAssociation.businessObject); + + })); + + + it('should undo', inject(function(modeling, commandStack) { + + // when + modeling.reconnectStart(dataOutputAssociation, task2Shape, newWaypoints); + commandStack.undo(); + + // then + expect(task1Shape.businessObject.dataOutputAssociations).to.include(dataOutputAssociation.businessObject); + expect(task2Shape.businessObject.dataOutputAssociations).to.be.empty; + + })); + + + it('should redo', inject(function(modeling, commandStack) { + + // when + modeling.reconnectStart(dataOutputAssociation, task2Shape, newWaypoints); + commandStack.undo(); + commandStack.redo(); + + // then + expect(task1Shape.businessObject.dataOutputAssociations).to.be.empty; + expect(task2Shape.businessObject.dataOutputAssociations).to.include(dataOutputAssociation.businessObject); + + })); + }); + + describe('reconnect DataInputAssociations', function() { + + beforeEach(function(){ + newWaypoints = [dataInputAssociation.waypoints[0], { x: task1Shape.x, y: task1Shape.y-30 }]; + }); + + it('should execute', inject(function(modeling){ + + // when + modeling.reconnectEnd(dataInputAssociation, task1Shape, newWaypoints); + + // then + expect(task1Shape.businessObject.dataInputAssociations).to.include(dataInputAssociation.businessObject); + expect(task2Shape.businessObject.dataInputAssociations).to.be.empty; + })); + + + it('should undo', inject(function(modeling, commandStack) { + + // when + modeling.reconnectEnd(dataInputAssociation, task1Shape, newWaypoints); + commandStack.undo(); + + // then + expect(task1Shape.businessObject.dataInputAssociations).to.be.empty; + expect(task2Shape.businessObject.dataInputAssociations).to.include(dataInputAssociation.businessObject); + })); + + + it('should redo', inject(function(modeling, commandStack) { + + // when + modeling.reconnectEnd(dataInputAssociation, task1Shape, newWaypoints); + commandStack.undo(); + commandStack.redo(); + + // then + expect(task1Shape.businessObject.dataInputAssociations).to.include(dataInputAssociation.businessObject); + expect(task2Shape.businessObject.dataInputAssociations).to.be.empty; + })); + }); + +}); diff --git a/test/spec/features/palette/PaletteProviderSpec.js b/test/spec/features/palette/PaletteProviderSpec.js index 36bb5a61..fc7cb8d9 100644 --- a/test/spec/features/palette/PaletteProviderSpec.js +++ b/test/spec/features/palette/PaletteProviderSpec.js @@ -27,7 +27,7 @@ describe('features/palette', function() { var entries = domQuery.all('.entry', paletteElement); // then - expect(entries.length).to.equal(10); + expect(entries.length).to.equal(11); })); }); diff --git a/test/spec/features/rules/BpmnRules.process.bpmn b/test/spec/features/rules/BpmnRules.process.bpmn index 933648dc..c7b97fa1 100644 --- a/test/spec/features/rules/BpmnRules.process.bpmn +++ b/test/spec/features/rules/BpmnRules.process.bpmn @@ -18,6 +18,8 @@ + + @@ -67,6 +69,12 @@ + + + + + + diff --git a/test/spec/features/rules/BpmnRulesSpec.js b/test/spec/features/rules/BpmnRulesSpec.js index fd68aaea..66a20906 100644 --- a/test/spec/features/rules/BpmnRulesSpec.js +++ b/test/spec/features/rules/BpmnRulesSpec.js @@ -29,7 +29,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('StartEvent_None', 'Task', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -39,7 +40,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('StartEvent_None', 'TextAnnotation', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -49,7 +51,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('Task', 'IntermediateThrowEvent_Link', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -59,7 +62,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('IntermediateThrowEvent_Link', 'EndEvent_None', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -69,7 +73,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('StartEvent_None', 'IntermediateCatchEvent_Link', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -79,7 +84,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('IntermediateCatchEvent_Link', 'Task', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -89,6 +95,71 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanDrop('TextAnnotation', 'Process', true); })); + + it('connect DataObjectReference -> StartEvent_None', inject(function() { + + expectCanConnect('DataObjectReference', 'StartEvent_None', { + sequenceFlow: false, + messageFlow: false, + association: true, + dataAssociation: false + }); + })); + + + it('connect StartEvent_None -> DataObjectReference', inject(function() { + + expectCanConnect('StartEvent_None', 'DataObjectReference', { + sequenceFlow: false, + messageFlow: false, + association: true, + dataAssociation: false + }); + })); + + + it('connect Task -> DataObjectReference', inject(function() { + + expectCanConnect('Task', 'DataObjectReference', { + sequenceFlow: false, + messageFlow: false, + association: true, + dataAssociation: { type: 'bpmn:DataOutputAssociation' } + }); + })); + + + it('connect DataObjectReference -> Task', inject(function() { + + expectCanConnect('DataObjectReference', 'Task', { + sequenceFlow: false, + messageFlow: false, + association: true, + dataAssociation: { type: 'bpmn:DataInputAssociation' } + }); + })); + + + it('connect SubProcess -> DataObjectReference', inject(function() { + + expectCanConnect('SubProcess', 'DataObjectReference', { + sequenceFlow: false, + messageFlow: false, + association: true, + dataAssociation: { type: 'bpmn:DataOutputAssociation' } + }); + })); + + + it('connect DataObjectReference -> SubProcess', inject(function() { + + expectCanConnect('DataObjectReference', 'SubProcess', { + sequenceFlow: false, + messageFlow: false, + association: true, + dataAssociation: { type: 'bpmn:DataInputAssociation' } + }); + })); }); @@ -104,7 +175,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('BoundaryEvent_on_SubProcess', 'Task', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -114,7 +186,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('BoundaryEvent_on_SubProcess', 'ExclusiveGateway', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -124,7 +197,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('BoundaryEvent_on_SubProcess', 'SubProcess', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -134,7 +208,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('BoundaryEvent_on_SubProcess', 'BoundaryEvent_on_Task', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -144,7 +219,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('BoundaryEvent_on_SubProcess', 'StartEvent_None', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -154,7 +230,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('StartEvent_None', 'BoundaryEvent_on_SubProcess', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -164,7 +241,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('BoundaryEvent_nested', 'Task', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -174,7 +252,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('BoundaryEvent_nested', 'EndEvent_nested', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -184,7 +263,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('BoundaryEvent_on_SubProcess', 'BoundaryEvent_in_OtherProcess', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -194,7 +274,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('BoundaryEvent_on_SubProcess', 'Task_in_OtherProcess', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -204,7 +285,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('Task_in_OtherProcess', 'BoundaryEvent_on_SubProcess', { sequenceFlow: false, messageFlow: true, - association: true + association: true, + dataAssociation: false }); })); @@ -223,7 +305,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EventBasedGateway', 'IntermediateCatchEvent_Message', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -233,7 +316,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EventBasedGateway', 'IntermediateCatchEvent_Message', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -243,7 +327,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EventBasedGateway', 'IntermediateCatchEvent_Signal', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -253,7 +338,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EventBasedGateway', 'IntermediateCatchEvent_Condition', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -263,7 +349,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EventBasedGateway', 'IntermediateCatchEvent_Timer', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -273,7 +360,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EventBasedGateway', 'IntermediateCatchEvent', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -283,7 +371,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EventBasedGateway', 'IntermediateThrowEvent_Message', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -293,7 +382,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EventBasedGateway', 'ReceiveTask', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -303,7 +393,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EventBasedGateway', 'Task_None', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -313,7 +404,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EventBasedGateway', 'ParallelGateway', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -323,7 +415,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EventBasedGateway', 'ParallelGateway', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -342,7 +435,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('StartEvent_None', 'IntermediateThrowEvent_Message', { sequenceFlow: true, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -352,7 +446,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('StartEvent_None', 'OtherParticipant', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -362,7 +457,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('OtherParticipant', 'StartEvent_None', { sequenceFlow: false, messageFlow: true, - association: true + association: true, + dataAssociation: false }); })); @@ -373,7 +469,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('OtherParticipant', 'StartEvent_Timer', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -384,7 +481,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('OtherParticipant', 'StartEvent_Message', { sequenceFlow: false, messageFlow: true, - association: true + association: true, + dataAssociation: false }); })); @@ -395,7 +493,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EndEvent_None', 'OtherParticipant', { sequenceFlow: false, messageFlow: true, - association: true + association: true, + dataAssociation: false }); })); @@ -405,7 +504,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EndEvent_Cancel', 'OtherParticipant', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -415,7 +515,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EndEvent_Message', 'OtherParticipant', { sequenceFlow: false, messageFlow: true, - association: true + association: true, + dataAssociation: false }); })); @@ -425,7 +526,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('OtherParticipant', 'EndEvent_None', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -435,7 +537,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('IntermediateThrowEvent_Message', 'OtherParticipant', { sequenceFlow: false, messageFlow: true, - association: true + association: true, + dataAssociation: false }); })); @@ -445,7 +548,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('IntermediateThrowEvent_None', 'OtherParticipant', { sequenceFlow: false, messageFlow: true, - association: true + association: true, + dataAssociation: false }); })); @@ -455,7 +559,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('IntermediateThrowEvent_Signal', 'OtherParticipant', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -465,7 +570,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('OtherParticipant', 'IntermediateThrowEvent_Message', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); @@ -475,7 +581,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('Task_in_SubProcess', 'OtherParticipant', { sequenceFlow: false, messageFlow: true, - association: true + association: true, + dataAssociation: false }); })); @@ -485,7 +592,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('EndEvent_None_in_SubProcess', 'OtherParticipant', { sequenceFlow: false, messageFlow: true, - association: true + association: true, + dataAssociation: false }); })); @@ -495,7 +603,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('OtherParticipant', 'Task_in_SubProcess', { sequenceFlow: false, messageFlow: true, - association: true + association: true, + dataAssociation: false }); })); @@ -505,7 +614,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('Participant', 'OtherParticipant', { sequenceFlow: false, messageFlow: true, - association: true + association: true, + dataAssociation: false }); })); @@ -515,7 +625,8 @@ describe('features/modeling/rules - BpmnRules', function() { expectCanConnect('StartEvent_None', 'TextAnnotation_OtherParticipant', { sequenceFlow: false, messageFlow: false, - association: true + association: true, + dataAssociation: false }); })); diff --git a/test/spec/features/rules/Helper.js b/test/spec/features/rules/Helper.js index c9ec014d..cdbd1ae3 100644 --- a/test/spec/features/rules/Helper.js +++ b/test/spec/features/rules/Helper.js @@ -26,6 +26,10 @@ function expectCanConnect(source, target, rules) { if ('association' in rules) { results.association = bpmnRules.canConnectAssociation(source, target); } + + if ('dataAssociation' in rules) { + results.dataAssociation = bpmnRules.canConnectDataAssociation(source, target); + } }); expect(results).to.eql(rules); @@ -75,4 +79,4 @@ function expectCanMove(elements, target, rules) { expect(results).to.eql(rules); } -module.exports.expectCanMove = expectCanMove; \ No newline at end of file +module.exports.expectCanMove = expectCanMove;