diff --git a/lib/features/modeling/rules/ModelingRules.js b/lib/features/modeling/rules/ModelingRules.js index f86c9734..a1580324 100644 --- a/lib/features/modeling/rules/ModelingRules.js +++ b/lib/features/modeling/rules/ModelingRules.js @@ -1,7 +1,8 @@ 'use strict'; var groupBy = require('lodash/collection/groupBy'), - size = require('lodash/collection/size'); + size = require('lodash/collection/size'), + forEach = require('lodash/collection/forEach'); var RuleProvider = require('diagram-js/lib/features/rules/RuleProvider'); @@ -32,9 +33,11 @@ ModelingRules.prototype.init = function() { var sourceBo = source.businessObject, targetBo = target.businessObject; + // handle start and end event cases var startEventCheck = targetBo.$instanceOf('bpmn:StartEvent') || sourceBo.$instanceOf('bpmn:EndEvent'); + // handle event based gateway cases var eventBasedGatewayCheck = false; // Ensure target of event based gateway is one of: @@ -45,18 +48,38 @@ ModelingRules.prototype.init = function() { if (targetBo.$instanceOf('bpmn:ReceiveTask')) { eventBasedGatewayCheck = false; - } else if (targetBo.$instanceOf('bpmn:IntermediateCatchEvent') && - targetBo.eventDefinitions && ( - targetBo.eventDefinitions[0].$type === 'bpmn:MessageEventDefinition' || - targetBo.eventDefinitions[0].$type === 'bpmn:TimerEventDefinition' || - targetBo.eventDefinitions[0].$type === 'bpmn:ConditionalEventDefinition' || - targetBo.eventDefinitions[0].$type === 'bpmn:SignalEventDefinition')) { + } else if (isEventType(targetBo, 'bpmn:IntermediateCatchEvent', 'bpmn:MessageEventDefinition') || + isEventType(targetBo, 'bpmn:IntermediateCatchEvent', 'bpmn:TimerEventDefinition') || + isEventType(targetBo, 'bpmn:IntermediateCatchEvent', 'bpmn:ConditionalEventDefinition') || + isEventType(targetBo, 'bpmn:IntermediateCatchEvent', 'bpmn:SignalEventDefinition')) { eventBasedGatewayCheck = false; } } - return startEventCheck || eventBasedGatewayCheck; + // handle link event + var linkEventCheck = false; + if (isEventType(targetBo, 'bpmn:IntermediateCatchEvent', 'bpmn:LinkEventDefinition') || + isEventType(sourceBo, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition')) { + linkEventCheck = true; + } + + return startEventCheck || eventBasedGatewayCheck || linkEventCheck; + } + + function isEventType(eventBo, type, definition) { + + var isType = eventBo.$instanceOf(type); + var isDefinition = false; + + var definitions = eventBo.eventDefinitions || []; + forEach(definitions, function(def) { + if (def.$type === definition) { + isDefinition = true; + } + }); + + return isType && isDefinition; } diff --git a/test/fixtures/bpmn/features/rules/link-event.bpmn b/test/fixtures/bpmn/features/rules/link-event.bpmn new file mode 100644 index 00000000..99d3e4f5 --- /dev/null +++ b/test/fixtures/bpmn/features/rules/link-event.bpmn @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/rules/ModelingRulesSpec.js b/test/spec/features/rules/ModelingRulesSpec.js index 88ccf34f..cb077cff 100644 --- a/test/spec/features/rules/ModelingRulesSpec.js +++ b/test/spec/features/rules/ModelingRulesSpec.js @@ -22,6 +22,7 @@ describe('features/ModelingRules', function() { var sequenceXML = fs.readFileSync('test/fixtures/bpmn/sequence-flows.bpmn', 'utf8'); var eventGatewaysEdgeXML = fs.readFileSync('test/fixtures/bpmn/features/rules/event-based-gateway-outgoing-edge.bpmn', 'utf8'); + var linkEventXML = fs.readFileSync('test/fixtures/bpmn/features/rules/link-event.bpmn', 'utf8'); var testModules = [ coreModule, modelingModule, rulesModule ]; @@ -188,4 +189,93 @@ describe('features/ModelingRules', function() { })); }); + + + + describe('catch link events', function() { + + beforeEach(bootstrapModeler(linkEventXML, { modules: testModules })); + + it('should not have incoming sequence flows ', inject(function(elementRegistry, modeling, rules) { + + // given + var catchEvent = elementRegistry.get('IntermediateCatchEvent'), + incomingTask = elementRegistry.get('Task_incoming'); + + + // when + var allowed = rules.allowed('connection.create', { + connection: null, + source: incomingTask, + target: catchEvent + }); + + // then + // connection should not be allowed + expect(allowed).toBe(false); + })); + + it('should be allowed to have outgoing sequence flows ', inject(function(elementRegistry, modeling, rules) { + + // given + var catchEvent = elementRegistry.get('IntermediateCatchEvent'), + outgoingTask = elementRegistry.get('Task_outgoing'); + + // when + var allowed = rules.allowed('connection.create', { + connection: null, + source: catchEvent, + target: outgoingTask + }); + + // then + // connection should not be allowed + expect(allowed).toBe(true); + })); + }); + + + + describe('throwing link events', function() { + + beforeEach(bootstrapModeler(linkEventXML, { modules: testModules })); + + it('should not have outgoing sequence flows ', inject(function(elementRegistry, modeling, rules) { + + // given + var catchEvent = elementRegistry.get('IntermediateThrowEvent'), + outgoingTask = elementRegistry.get('Task_outgoing'); + + + // when + var allowed = rules.allowed('connection.create', { + connection: null, + source: catchEvent, + target: outgoingTask + }); + + // then + // connection should not be allowed + expect(allowed).toBe(false); + })); + + it('should be allowed to have incoming sequence flows ', inject(function(elementRegistry, modeling, rules) { + + // given + var catchEvent = elementRegistry.get('IntermediateThrowEvent'), + incomingTask = elementRegistry.get('Task_incoming'); + + + // when + var allowed = rules.allowed('connection.create', { + connection: null, + source: incomingTask, + target: catchEvent + }); + + // then + // connection should not be allowed + expect(allowed).toBe(true); + })); + }); });