feat(replace): morph boundary events

Closes #293
This commit is contained in:
pedesen 2015-07-23 13:59:47 +02:00
parent 90376cae87
commit 42c052f7ec
6 changed files with 280 additions and 19 deletions

View File

@ -1,7 +1,9 @@
'use strict';
var forEach = require('lodash/collection/forEach'),
filter = require('lodash/collection/filter');
var forEach = require('lodash/collection/forEach'),
filter = require('lodash/collection/filter'),
pick = require('lodash/object/pick'),
assign = require('lodash/object/assign');
var REPLACE_OPTIONS = require ('./ReplaceOptions');
@ -11,12 +13,20 @@ var startEventReplace = REPLACE_OPTIONS.START_EVENT,
gatewayReplace = REPLACE_OPTIONS.GATEWAY,
taskReplace = REPLACE_OPTIONS.TASK,
subProcessExpandedReplace = REPLACE_OPTIONS.SUBPROCESS_EXPANDED,
transactionReplace = REPLACE_OPTIONS.TRANSACTION;
transactionReplace = REPLACE_OPTIONS.TRANSACTION,
boundaryEventReplace = REPLACE_OPTIONS.BOUNDARY_EVENT;
var is = require('../../util/ModelUtil').is,
getBusinessObject = require('../../util/ModelUtil').getBusinessObject,
isExpanded = require('../../util/DiUtil').isExpanded;
var CUSTOM_PROPERTIES = [
'cancelActivity',
'instantiate',
'eventGatewayType'
];
/**
* A replace menu provider that gives users the controls to choose
* and replace BPMN elements with each other.
@ -60,13 +70,10 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
eventDefinitions.push(eventDefinition);
}
if (target.instantiate !== undefined) {
businessObject.instantiate = target.instantiate;
}
// initialize special properties defined in target definition
assign(businessObject, pick(target, CUSTOM_PROPERTIES));
if (target.eventGatewayType !== undefined) {
businessObject.eventGatewayType = target.eventGatewayType;
}
// copy size (for activities only)
if (is(oldBusinessObject, 'bpmn:Activity')) {
@ -77,6 +84,7 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
newElement.height = element.height;
}
if (is(oldBusinessObject, 'bpmn:SubProcess')) {
newElement.isExpanded = isExpanded(oldBusinessObject);
}
@ -236,6 +244,10 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
});
} else
if (is(businessObject, 'bpmn:BoundaryEvent')) {
addEntries(boundaryEventReplace, filterEvents);
} else
if (is(businessObject, 'bpmn:FlowNode')) {
addEntries(taskReplace, function(entry) {
return entry.target.type !== businessObject.$type;
@ -247,13 +259,16 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
var target = entry.target;
var eventDefinition = businessObject.eventDefinitions && businessObject.eventDefinitions[0].$type;
var isEventDefinitionEqual = target.eventDefinition == eventDefinition;
var isEventTypeEqual = businessObject.$type == target.type;
var eventDefinition = businessObject.eventDefinitions && businessObject.eventDefinitions[0].$type,
cancelActivity = target.cancelActivity !== false;
var isEventDefinitionEqual = target.eventDefinition == eventDefinition,
isEventTypeEqual = businessObject.$type == target.type,
isInterruptingEqual = businessObject.cancelActivity == cancelActivity;
return ((!isEventDefinitionEqual && isEventTypeEqual) ||
!isEventTypeEqual) ||
!(isEventDefinitionEqual && isEventTypeEqual);
!(isEventDefinitionEqual && isEventTypeEqual && isInterruptingEqual);
}

View File

@ -90,7 +90,7 @@ module.exports.INTERMEDIATE_EVENT = [
},
{
label: 'Message Intermediate Catch Event',
actionName: 'replace-with-intermediate-catch',
actionName: 'replace-with-message-intermediate-catch',
className: 'icon-intermediate-event-catch-message',
target: {
type: 'bpmn:IntermediateCatchEvent',
@ -99,7 +99,7 @@ module.exports.INTERMEDIATE_EVENT = [
},
{
label: 'Message Intermediate Throw Event',
actionName: 'replace-with-intermediate-throw',
actionName: 'replace-with-message-intermediate-throw',
className: 'icon-intermediate-event-throw-message',
target: {
type: 'bpmn:IntermediateThrowEvent',
@ -117,7 +117,7 @@ module.exports.INTERMEDIATE_EVENT = [
},
{
label: 'Escalation Intermediate Catch Event',
actionName: 'replace-with-escalation-catch',
actionName: 'replace-with-escalation-intermediate-catch',
className: 'icon-intermediate-event-catch-escalation',
target: {
type: 'bpmn:IntermediateCatchEvent',
@ -161,8 +161,8 @@ module.exports.INTERMEDIATE_EVENT = [
}
},
{
label: 'Signal Throw Catch Event',
actionName: 'replace-with-throw-intermediate-catch',
label: 'Signal Intermediate Catch Event',
actionName: 'replace-with-signal-intermediate-catch',
className: 'icon-intermediate-event-catch-signal',
target: {
type: 'bpmn:IntermediateCatchEvent',
@ -444,3 +444,110 @@ module.exports.TASK = [
}
}
];
module.exports.BOUNDARY_EVENT = [
{
label: 'Message Boundary Event',
actionName: 'replace-with-message-boundary',
className: 'icon-intermediate-event-catch-message',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Timer Boundary Event',
actionName: 'replace-with-timer-boundary',
className: 'icon-intermediate-event-catch-timer',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:TimerEventDefinition'
}
},
{
label: 'Escalation Boundary Event',
actionName: 'replace-with-escalation-boundary',
className: 'icon-intermediate-event-catch-escalation',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:EscalationEventDefinition'
}
},
{
label: 'Conditional Boundary Event',
actionName: 'replace-with-conditional-boundary',
className: 'icon-intermediate-event-catch-condition',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:ConditionalEventDefinition'
}
},
{
label: 'Error Boundary Event',
actionName: 'replace-with-error-boundary',
className: 'icon-intermediate-event-catch-error',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:ErrorEventDefinition'
}
},
{
label: 'Signal Boundary Event',
actionName: 'replace-with-signal-boundary',
className: 'icon-intermediate-event-catch-signal',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:SignalEventDefinition'
}
},
{
label: 'Message Boundary Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-message-boundary',
className: 'icon-intermediate-event-catch-non-interrupting-message',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:MessageEventDefinition',
cancelActivity: false
}
},
{
label: 'Timer Boundary Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-timer-boundary',
className: 'icon-intermediate-event-catch-non-interrupting-timer',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:TimerEventDefinition',
cancelActivity: false
}
},
{
label: 'Escalation Boundary Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-escalation-boundary',
className: 'icon-intermediate-event-catch-non-interrupting-escalation',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:EscalationEventDefinition',
cancelActivity: false
}
},
{
label: 'Conditional Boundary Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-conditional-boundary',
className: 'icon-intermediate-event-catch-non-interrupting-condition',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:ConditionalEventDefinition',
cancelActivity: false
}
},
{
label: 'Signal Boundary Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-signal-boundary',
className: 'icon-intermediate-event-catch-non-interrupting-signal',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:SignalEventDefinition',
cancelActivity: false
}
},
];

View File

@ -14,6 +14,12 @@
<bpmn2:subProcess id="SubProcess" />
<bpmn2:transaction id="Transaction" />
<bpmn2:adHocSubProcess id="AdHocSubProcess" />
<bpmn2:boundaryEvent id="BoundaryEvent_1" attachedToRef="Task">
<bpmn2:messageEventDefinition />
</bpmn2:boundaryEvent>
<bpmn2:boundaryEvent id="BoundaryEvent_2" cancelActivity="false" attachedToRef="Task">
<bpmn2:messageEventDefinition />
</bpmn2:boundaryEvent>
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
@ -38,6 +44,18 @@
<bpmndi:BPMNShape id="AdHocSubProcess_di" bpmnElement="AdHocSubProcess">
<dc:Bounds x="667" y="100" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_1_di" bpmnElement="BoundaryEvent_1">
<dc:Bounds x="532" y="162" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="505" y="198" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_2_di" bpmnElement="BoundaryEvent_2">
<dc:Bounds x="593" y="162" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="566" y="198" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>

View File

@ -31,6 +31,12 @@
<bpmn:subProcess id="SubProcessCollapsed" />
<bpmn:adHocSubProcess id="AdHocSubProcessCollapsed" />
<bpmn:adHocSubProcess id="AdHocSubProcessExpanded" />
<bpmn:boundaryEvent id="BoundaryEvent_1" cancelActivity="false" attachedToRef="Task_1">
<bpmn:timerEventDefinition />
</bpmn:boundaryEvent>
<bpmn:boundaryEvent id="BoundaryEvent_2" attachedToRef="Task_1">
<bpmn:conditionalEventDefinition />
</bpmn:boundaryEvent>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
@ -107,6 +113,18 @@
<bpmndi:BPMNShape id="AdHocSubProcessExpanded_di" bpmnElement="AdHocSubProcessExpanded" isExpanded="true">
<dc:Bounds x="374" y="368" width="140" height="120" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_1_di" bpmnElement="BoundaryEvent_1">
<dc:Bounds x="349" y="74" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="322" y="110" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_2_di" bpmnElement="BoundaryEvent_2">
<dc:Bounds x="262" y="74" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="235" y="110" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -514,7 +514,7 @@ describe('features/popup-menu', function() {
var entry = queryEntry(popupMenu, 'replace-with-subprocess');
// when
// replacing the expanded sub process with a transaction
// replacing the transaction with an expanded sub process
var subProcess = popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
// then
@ -523,4 +523,41 @@ describe('features/popup-menu', function() {
});
describe('replace menu', function() {
it('should contain all boundary events for an interrupting boundary event',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var boundaryEvent = elementRegistry.get('BoundaryEvent_1');
// when
openPopup(boundaryEvent, 40);
var entriesContainer = queryPopup(popupMenu, '.djs-popup-body');
// then
expect(entriesContainer.childNodes.length).to.equal(10);
expect(queryEntry(popupMenu, 'replace-with-message-intermediate-catch')).to.be.null;
}));
it('should contain all boundary events for a non interrupting boundary event',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var boundaryEvent = elementRegistry.get('BoundaryEvent_2');
// when
openPopup(boundaryEvent, 40);
var entriesContainer = queryPopup(popupMenu, '.djs-popup-body');
// then
expect(entriesContainer.childNodes.length).to.equal(10);
expect(queryEntry(popupMenu, 'replace-with-non-interrupting-message-intermediate-catch')).to.be.null;
}));
});
});

View File

@ -82,19 +82,85 @@ describe('features/replace', function() {
it('transaction', inject(function(elementRegistry, modeling, bpmnReplace, canvas) {
// given
var transaction = elementRegistry.get('Transaction_1'),
newElementData = {
type: 'bpmn:SubProcess',
isExpanded: true
};
// when
var newElement = bpmnReplace.replaceElement(transaction, newElementData);
// then
expect(newElement).to.be.defined;
expect(is(newElement.businessObject, 'bpmn:SubProcess')).to.be.true;
}));
it('non interrupting boundary event by interrupting boundary event',
inject(function(elementRegistry, modeling, bpmnReplace, canvas) {
// given
var boundaryEvent = elementRegistry.get('BoundaryEvent_1'),
newElementData = {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:EscalationEventDefinition'
};
// when
var newElement = bpmnReplace.replaceElement(boundaryEvent, newElementData);
// then
expect(newElement).to.be.defined;
expect(is(newElement.businessObject, 'bpmn:BoundaryEvent')).to.be.true;
expect(newElement.businessObject.eventDefinitions[0].$type).to.equal('bpmn:EscalationEventDefinition');
expect(newElement.businessObject.cancelActivity).to.be.true;
}));
it('interrupting boundary event by non interrupting boundary event',
inject(function(elementRegistry, modeling, bpmnReplace, canvas) {
// given
var boundaryEvent = elementRegistry.get('BoundaryEvent_2'),
newElementData = {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:SignalEventDefinition',
cancelActivity: false
};
// when
var newElement = bpmnReplace.replaceElement(boundaryEvent, newElementData);
// then
expect(newElement).to.be.defined;
expect(is(newElement.businessObject, 'bpmn:BoundaryEvent')).to.be.true;
expect(newElement.businessObject.eventDefinitions[0].$type).to.equal('bpmn:SignalEventDefinition');
expect(newElement.businessObject.cancelActivity).to.be.false;
}));
it('boundary event and update host',
inject(function(elementRegistry, modeling, bpmnReplace, canvas) {
// given
var boundaryEvent = elementRegistry.get('BoundaryEvent_1'),
host = elementRegistry.get('Task_1'),
newElementData = {
type: 'bpmn:BoundaryEvent',
eventDefinition: 'bpmn:ErrorEventDefinition',
};
// when
var newElement = bpmnReplace.replaceElement(boundaryEvent, newElementData);
// then
expect(newElement.host).to.be.defined;
expect(newElement.host).to.eql(host);
}));
});