feat(modeling): add ad-hoc sub processes

Closes #271
This commit is contained in:
pedesen 2015-07-03 10:48:32 +02:00 committed by Ricardo Matias
parent b76c1c8ded
commit 31ea6b2f91
5 changed files with 617 additions and 415 deletions

View File

@ -17,10 +17,6 @@ var is = require('../../util/ModelUtil').is,
getBusinessObject = require('../../util/ModelUtil').getBusinessObject,
isExpanded = require('../../util/DiUtil').isExpanded;
var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor');
var inherits = require('inherits');
/**
* A replace menu provider that gives users the controls to choose
* and replace BPMN elements with each other.
@ -32,7 +28,9 @@ var inherits = require('inherits');
*/
function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modeling, eventBus) {
CommandInterceptor.call(this, eventBus);
var self = this,
currentElement;
/**
* Prepares a new business object for the replacement element
@ -94,51 +92,34 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
return newElement;
}
function updateElementProperties(e) {
var context = e.context;
modeling.updateProperties(context.element, context.updatedProperties);
}
function toggleLoopEntry(event, entry) {
var loopEntries = self.getLoopEntries(currentElement);
this.postExecute([
'entries.update',
], updateElementProperties);
var toggleEntries,
toggledElement;
function toggleMi(event, entry) {
var loopCharacteristics,
updatedEntries = [];
var loopCharacteristics;
if (entry.active) {
loopCharacteristics = undefined;
updatedEntries.push({ id: entry.id, active: false });
} else {
forEach(toggleEntries, function(action) {
forEach(loopEntries, function(action) {
var options = action.options;
if (entry.id === action.id) {
updatedEntries.push({ id: entry.id, active: true });
loopCharacteristics = moddle.create(options.loopCharacteristics);
if (options.isSequential) {
loopCharacteristics.isSequential = options.isSequential;
}
} else {
updatedEntries.push({ id: action.id, active: false });
}
});
}
popupMenu.updateHeaderEntries(updatedEntries, toggledElement, { loopCharacteristics: loopCharacteristics });
modeling.updateProperties(currentElement, { loopCharacteristics: loopCharacteristics });
}
function getToggleOptions(element) {
function getLoopEntries(element) {
currentElement = element;
var businessObject = getBusinessObject(element),
loopCharacteristics = businessObject.loopCharacteristics;
@ -153,14 +134,12 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
isParallel = loopCharacteristics.isSequential !== undefined && !loopCharacteristics.isSequential;
}
toggledElement = element;
toggleEntries = [
var loopEntries = [
{
id: 'toggle-parallel-mi',
className: 'icon-parallel-mi-marker',
active: isParallel,
action: toggleMi,
action: toggleLoopEntry,
options: {
loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
isSequential: false
@ -170,7 +149,7 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
id: 'toggle-sequential-mi',
className: 'icon-sequential-mi-marker',
active: isSequential,
action: toggleMi,
action: toggleLoopEntry,
options: {
loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
isSequential: true
@ -180,14 +159,35 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
id: 'toggle-loop',
className: 'icon-loop-marker',
active: isLoop,
action: toggleMi,
action: toggleLoopEntry,
options: {
loopCharacteristics: 'bpmn:StandardLoopCharacteristics'
}
}
];
return loopEntries;
}
return toggleEntries;
function getAdHocEntry(element) {
var businessObject = getBusinessObject(element);
var isAdHoc = is(businessObject, 'bpmn:AdHocSubProcess');
var adHocEntry = {
id: 'toggle-adhoc',
className: 'icon-ad-hoc-marker',
active: isAdHoc,
action: function(event, entry) {
if (isAdHoc) {
return replaceElement(element, { type: 'bpmn:SubProcess' });
} else {
return replaceElement(element, { type: 'bpmn:AdHocSubProcess' });
}
}
};
return adHocEntry;
}
@ -229,12 +229,20 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
addEntries(subProcessExpandedReplace, filterEvents);
} else
if (is(businessObject, 'bpmn:AdHocSubProcess') && !isExpanded(businessObject)) {
addEntries(taskReplace, function(entry) {
return entry.target.type !== 'bpmn:SubProcess';
});
} else
if (is(businessObject, 'bpmn:FlowNode')) {
addEntries(taskReplace, function(entry) {
return entry.target.type !== businessObject.$type;
});
}
function filterEvents(entry) {
var target = entry.target;
@ -248,6 +256,7 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
!(isEventDefinitionEqual && isEventTypeEqual);
}
function addEntries(entries, filterFun) {
// Filter selected type from the array
var filteredEntries = filter(entries, filterFun);
@ -260,6 +269,7 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
});
}
function addMenuEntry(definition) {
return {
@ -267,7 +277,7 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
className: definition.className,
id: definition.actionName,
action: function() {
replaceElement(element, definition.target);
return replaceElement(element, definition.target);
}
};
}
@ -282,32 +292,34 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
*/
this.openChooser = function(position, element) {
var entries = this.getReplaceOptions(element),
headerEntries;
headerEntries = [];
if (is(element, 'bpmn:Activity')) {
headerEntries = this.getToggleOptions(element);
headerEntries = headerEntries.concat(this.getLoopEntries(element));
}
popupMenu.open(
{
className: 'replace-menu',
element: element,
position: position,
headerEntries: headerEntries,
entries: entries
}
);
if (is(element, 'bpmn:SubProcess') && !is(element, 'bpmn:Transaction')) {
headerEntries.push(this.getAdHocEntry(element));
}
popupMenu.open({
className: 'replace-menu',
element: element,
position: position,
headerEntries: headerEntries,
entries: entries
});
};
this.getReplaceOptions = getReplaceOptions;
this.getToggleOptions = getToggleOptions;
this.getLoopEntries = getLoopEntries;
this.getAdHocEntry = getAdHocEntry;
this.replaceElement = replaceElement;
}
inherits(BpmnReplace, CommandInterceptor);
BpmnReplace.$inject = [ 'bpmnFactory', 'moddle', 'popupMenu', 'replace', 'selection', 'modeling', 'eventBus' ];
module.exports = BpmnReplace;

View File

@ -13,6 +13,7 @@
<bpmn2:task id="Task" name="Task" />
<bpmn2:subProcess id="SubProcess" />
<bpmn2:transaction id="Transaction" />
<bpmn2:adHocSubProcess id="AdHocSubProcess" />
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
@ -34,6 +35,9 @@
<bpmndi:BPMNShape id="Transaction__di" bpmnElement="Transaction" isExpanded="true">
<dc:Bounds x="496" y="224" width="350" height="200" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="AdHocSubProcess_di" bpmnElement="AdHocSubProcess">
<dc:Bounds x="667" y="100" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>

View File

@ -28,73 +28,85 @@
<bpmn:sequenceFlow id="SequenceFlow_4" sourceRef="StartEvent_2" targetRef="Task_2" />
</bpmn:subProcess>
<bpmn:transaction id="Transaction_1" />
<bpmn:subProcess id="SubProcessCollapsed" />
<bpmn:adHocSubProcess id="AdHocSubProcessCollapsed" />
<bpmn:adHocSubProcess id="AdHocSubProcessExpanded" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="171" y="171" width="36" height="36" />
<dc:Bounds x="99" y="34" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="144" y="207" width="90" height="20" />
<dc:Bounds x="72" y="70" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_1_di" bpmnElement="Task_1">
<dc:Bounds x="346" y="149" width="100" height="80" />
<dc:Bounds x="274" y="12" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_1_di" bpmnElement="SequenceFlow_1">
<di:waypoint xsi:type="dc:Point" x="207" y="189" />
<di:waypoint xsi:type="dc:Point" x="346" y="189" />
<di:waypoint xsi:type="dc:Point" x="135" y="52" />
<di:waypoint xsi:type="dc:Point" x="274" y="52" />
<bpmndi:BPMNLabel>
<dc:Bounds x="311" y="179" width="90" height="20" />
<dc:Bounds x="239" y="42" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="ExclusiveGateway_1_di" bpmnElement="ExclusiveGateway_1" isMarkerVisible="true">
<dc:Bounds x="573" y="164" width="50" height="50" />
<dc:Bounds x="501" y="27" width="50" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="553" y="214" width="90" height="20" />
<dc:Bounds x="481" y="77" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_2_di" bpmnElement="SequenceFlow_2">
<di:waypoint xsi:type="dc:Point" x="446" y="189" />
<di:waypoint xsi:type="dc:Point" x="573" y="189" />
<di:waypoint xsi:type="dc:Point" x="374" y="52" />
<di:waypoint xsi:type="dc:Point" x="501" y="52" />
<bpmndi:BPMNLabel>
<dc:Bounds x="650" y="179" width="90" height="20" />
<dc:Bounds x="578" y="42" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="EndEvent_1_di" bpmnElement="EndEvent_1">
<dc:Bounds x="768" y="171" width="36" height="36" />
<dc:Bounds x="696" y="34" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="741" y="207" width="90" height="20" />
<dc:Bounds x="669" y="70" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_3_di" bpmnElement="SequenceFlow_3">
<di:waypoint xsi:type="dc:Point" x="623" y="189" />
<di:waypoint xsi:type="dc:Point" x="768" y="189" />
<di:waypoint xsi:type="dc:Point" x="551" y="52" />
<di:waypoint xsi:type="dc:Point" x="696" y="52" />
<bpmndi:BPMNLabel>
<dc:Bounds x="789.5" y="179" width="90" height="20" />
<dc:Bounds x="718" y="42" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="SubProcess_1_di" bpmnElement="SubProcess_1" isExpanded="true">
<dc:Bounds x="173" y="275" width="350" height="200" />
<dc:Bounds x="101" y="138" width="350" height="200" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Transaction_1_di" bpmnElement="Transaction_1" isExpanded="true">
<dc:Bounds x="562" y="275" width="350" height="200" />
<dc:Bounds x="490" y="138" width="350" height="200" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="StartEvent_2_di" bpmnElement="StartEvent_2">
<dc:Bounds x="225" y="355" width="36" height="36" />
<dc:Bounds x="153" y="218" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="198" y="391" width="90" height="20" />
<dc:Bounds x="126" y="254" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_2_di" bpmnElement="Task_2">
<dc:Bounds x="359" y="333" width="100" height="80" />
<dc:Bounds x="287" y="196" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_4_di" bpmnElement="SequenceFlow_4">
<di:waypoint xsi:type="dc:Point" x="261" y="373" />
<di:waypoint xsi:type="dc:Point" x="359" y="373" />
<di:waypoint xsi:type="dc:Point" x="189" y="236" />
<di:waypoint xsi:type="dc:Point" x="287" y="236" />
<bpmndi:BPMNLabel>
<dc:Bounds x="265" y="363" width="90" height="20" />
<dc:Bounds x="193" y="226" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="SubProcessCollapsed_di" bpmnElement="SubProcessCollapsed">
<dc:Bounds x="103" y="367" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="AdHocSubProcessCollapsed_di" bpmnElement="AdHocSubProcessCollapsed">
<dc:Bounds x="235" y="367" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="AdHocSubProcessExpanded_di" bpmnElement="AdHocSubProcessExpanded" isExpanded="true">
<dc:Bounds x="374" y="368" width="140" height="120" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -33,305 +33,414 @@ describe('features/popup-menu', function() {
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
afterEach(inject(function(popupMenu) {
popupMenu.close();
}));
describe('active attribute', function(){
var openPopup = function(element, offset) {
offset = offset || 100;
it('should be true for parallel marker', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('ParallelTask'),
loopCharacteristics = task.businessObject.loopCharacteristics;
// when
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
// then
expect(is(loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(loopCharacteristics.isSequential).not.toBe(undefined);
expect(loopCharacteristics.isSequential).toBe(false);
expect(popupMenu._getEntry('toggle-parallel-mi').active).toBe(true);
}));
TestHelper.getBpmnJS().invoke(function(bpmnReplace){
bpmnReplace.openChooser({ x: element.x + offset, y: element.y + offset }, element);
});
};
it('should be true for sequential marker', inject(function(popupMenu, bpmnReplace, elementRegistry) {
describe('toggle', function(){
// given
var task = elementRegistry.get('SequentialTask'),
loopCharacteristics = task.businessObject.loopCharacteristics;
describe('active attribute', function(){
// when
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
it('should be true for parallel marker', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// then
expect(is(loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(loopCharacteristics.isSequential).toBe(true);
expect(popupMenu._getEntry('toggle-sequential-mi').active).toBe(true);
}));
// given
var task = elementRegistry.get('ParallelTask'),
loopCharacteristics = task.businessObject.loopCharacteristics;
// when
openPopup(task);
// then
expect(is(loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(loopCharacteristics.isSequential).toBe(false);
expect(loopCharacteristics.isSequential).not.toBe(undefined);
expect(popupMenu._getEntry('toggle-parallel-mi').active).toBe(true);
}));
it('should be true for loop marker', inject(function(popupMenu, bpmnReplace, elementRegistry) {
it('should be true for sequential marker', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('LoopTask'),
loopCharacteristics = task.businessObject.loopCharacteristics;
// given
var task = elementRegistry.get('SequentialTask'),
loopCharacteristics = task.businessObject.loopCharacteristics;
// when
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
// when
openPopup(task);
// then
expect(loopCharacteristics.isSequential).toBe(true);
expect(popupMenu._getEntry('toggle-sequential-mi').active).toBe(true);
expect(is(loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
}));
it('should be true for loop marker', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('LoopTask'),
loopCharacteristics = task.businessObject.loopCharacteristics;
// when
openPopup(task);
// then
expect(loopCharacteristics.isSequential).toBe(undefined);
expect(popupMenu._getEntry('toggle-loop').active).toBe(true);
expect(is(loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(false);
}));
it('should be true for ad hoc marker', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var AdHocSubProcess = elementRegistry.get('AdHocSubProcess');
// when
openPopup(AdHocSubProcess);
// then
expect(popupMenu._getEntry('toggle-adhoc').active).toBe(true);
}));
});
describe('exclusive toggle buttons', function(){
it('should not toggle non exclusive buttons off', inject(function(popupMenu, bpmnReplace, elementRegistry) {
var subProcess = elementRegistry.get('AdHocSubProcess');
openPopup(subProcess);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(subProcess);
// then
var adHocEntry = queryEntry(popupMenu, 'toggle-adhoc');
expect(domClasses(adHocEntry).has('active')).toBe(true);
}));
});
describe('non exclusive toggle buttons', function(){
it('should not toggle exclusive buttons off',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var subProcess = elementRegistry.get('SubProcess');
// when
// toggle parallel on
openPopup(subProcess);
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
popupMenu.trigger(Events.create(parallelEntry, { x: 0, y: 0 }));
// toggle ad hoc on
openPopup(subProcess);
var adHocEntry = queryEntry(popupMenu, 'toggle-adhoc');
var adHocSubProcess = popupMenu.trigger(Events.create(adHocEntry, { x: 0, y: 0 }));
openPopup(adHocSubProcess);
// then
parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
adHocEntry = queryEntry(popupMenu, 'toggle-adhoc');
expect(domClasses(parallelEntry).has('active')).toBe(true);
expect(domClasses(adHocEntry).has('active')).toBe(true);
}));
});
describe('parallel toggle button', function(){
it('should toggle parallel marker off',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('ParallelTask');
openPopup(task);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(task);
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(task.businessObject.loopCharacteristics).toBe(undefined);
expect(domClasses(parallelEntry).has('active')).toBe(false);
}));
it('should toggle parallel marker on', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('Task');
openPopup(task);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(task);
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(domClasses(parallelEntry).has('active')).toBe(true);
expect(task.businessObject.loopCharacteristics.isSequential).toBe(false);
expect(is(task.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
}));
it('should set sequential button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask');
openPopup(task);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(task);
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(domClasses(sequentialEntry).has('active')).toBe(false);
}));
it('should set loop button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('LoopTask');
openPopup(task);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(task);
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(domClasses(loopEntry).has('active')).toBe(false);
}));
});
describe('sequential toggle button', function(){
it('should toggle sequential marker off', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask');
openPopup(task);
var entry = queryEntry(popupMenu, 'toggle-sequential-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(task);
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(task.businessObject.loopCharacteristics).toBe(undefined);
expect(domClasses(sequentialEntry).has('active')).toBe(false);
}));
it('should toggle sequential marker on', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('Task');
openPopup(task);
var entry = queryEntry(popupMenu, 'toggle-sequential-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(task);
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(domClasses(sequentialEntry).has('active')).toBe(true);
expect(task.businessObject.loopCharacteristics.isSequential).toBe(true);
expect(is(task.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
}));
it('should set loop button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('LoopTask');
openPopup(task);
var entry = queryEntry(popupMenu, 'toggle-sequential-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(task);
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(domClasses(loopEntry).has('active')).toBe(false);
}));
it('should set parallel button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('ParallelTask');
openPopup(task);
var entry = queryEntry(popupMenu, 'toggle-sequential-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(task);
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(domClasses(parallelEntry).has('active')).toBe(false);
}));
});
describe('loop toggle button', function(){
it('should toggle loop marker off', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('LoopTask');
openPopup(task);
var entry = queryEntry(popupMenu, 'toggle-loop');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(task);
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(domClasses(loopEntry).has('active')).toBe(false);
expect(is(task.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(undefined);
}));
it('should toggle loop marker on', inject(function(popupMenu, bpmnReplace, elementRegistry){
// given
var task = elementRegistry.get('Task');
openPopup(task);
var entry = queryEntry(popupMenu, 'toggle-loop');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(task);
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(domClasses(loopEntry).has('active')).toBe(true);
expect(is(task.businessObject.loopCharacteristics, 'bpmn:StandardLoopCharacteristics')).toBe(true);
}));
it('should set sequential button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask');
openPopup(task);
var entry = queryEntry(popupMenu, 'toggle-loop');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(task);
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(domClasses(sequentialEntry).has('active')).toBe(false);
}));
it('should set parallel button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('ParallelTask');
openPopup(task);
var entry = queryEntry(popupMenu, 'toggle-loop');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
openPopup(task);
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(domClasses(parallelEntry).has('active')).toBe(false);
}));
});
// then
expect(is(loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(false);
expect(loopCharacteristics.isSequential).toBe(undefined);
expect(popupMenu._getEntry('toggle-loop').active).toBe(true);
}));
});
describe('parallel toggle button', function(){
it('should toggle parallel marker off', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('ParallelTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(task.businessObject.loopCharacteristics).toBe(undefined);
expect(domClasses(parallelEntry).has('active')).toBe(false);
}));
it('should toggle parallel marker on', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('Task');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(is(task.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(task.businessObject.loopCharacteristics.isSequential).toBe(false);
expect(domClasses(parallelEntry).has('active')).toBe(true);
}));
it('should set sequential button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(domClasses(sequentialEntry).has('active')).toBe(false);
}));
it('should set loop button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('LoopTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(domClasses(loopEntry).has('active')).toBe(false);
}));
});
describe('sequential toggle button', function(){
it('should toggle sequential marker off', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-sequential-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(task.businessObject.loopCharacteristics).toBe(undefined);
expect(domClasses(sequentialEntry).has('active')).toBe(false);
}));
it('should toggle sequential marker on', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('Task');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-sequential-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(is(task.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(task.businessObject.loopCharacteristics.isSequential).toBe(true);
expect(domClasses(sequentialEntry).has('active')).toBe(true);
}));
it('should set loop button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('LoopTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-sequential-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(domClasses(loopEntry).has('active')).toBe(false);
}));
it('should set parallel button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('ParallelTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-sequential-mi');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(domClasses(parallelEntry).has('active')).toBe(false);
}));
});
describe('loop toggle button', function(){
it('should toggle loop marker off', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('LoopTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-loop');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(is(task.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(undefined);
expect(domClasses(loopEntry).has('active')).toBe(false);
}));
it('should toggle loop marker on', inject(function(popupMenu, bpmnReplace, elementRegistry){
// given
var task = elementRegistry.get('Task');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-loop');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(is(task.businessObject.loopCharacteristics, 'bpmn:StandardLoopCharacteristics')).toBe(true);
expect(domClasses(loopEntry).has('active')).toBe(true);
}));
it('should set sequential button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-loop');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(domClasses(sequentialEntry).has('active')).toBe(false);
}));
it('should set parallel button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('ParallelTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-loop');
// when
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(domClasses(parallelEntry).has('active')).toBe(false);
}));
});
describe('replacing', function() {
it('should retain the loop characteristics', inject(function(popupMenu, bpmnReplace, elementRegistry) {
@ -339,21 +448,18 @@ describe('features/popup-menu', function() {
// given
var task = elementRegistry.get('SequentialTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
openPopup(task);
var entry = queryEntry(popupMenu, 'replace-with-send-task');
// when
// replacing the task with a send task
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var sendTask = popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
// then
// get the send task from the registry
var sendTask = elementRegistry.filter(function(element, gfx) { return element.type === 'bpmn:SendTask';})[0];
expect(sendTask.businessObject.loopCharacteristics).toBeDefined();
expect(is(sendTask.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(sendTask.businessObject.loopCharacteristics.isSequential).toBe(true);
expect(is(sendTask.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
}));
@ -363,24 +469,18 @@ describe('features/popup-menu', function() {
// given
var task = elementRegistry.get('SequentialTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
openPopup(task);
var entry = queryEntry(popupMenu, 'replace-with-call-activity');
// when
// replacing the task with a call activity
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var callActivity = popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
// then
// get the send task from the registry
var callActivity = elementRegistry.filter(function(element, gfx) {
return element.type === 'bpmn:CallActivity';
})[0];
expect(callActivity.businessObject.loopCharacteristics).toBeDefined();
expect(is(callActivity.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(callActivity.businessObject.loopCharacteristics.isSequential).toBe(true);
expect(is(callActivity.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
}));
@ -390,22 +490,16 @@ describe('features/popup-menu', function() {
// given
var subProcess = elementRegistry.get('SubProcess');
bpmnReplace.openChooser({ x: subProcess.x + 100, y: subProcess.y + 100 }, subProcess);
openPopup(subProcess);
var entry = queryEntry(popupMenu, 'replace-with-transaction');
// when
// replacing the expanded sub process with a transaction
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var transaction = popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
// then
// get the morphed transacion from the registry
var transaction = elementRegistry.filter(function(element, gfx) {
return element.x === subProcess.x && element.y === subProcess.y;
})[0];
expect(isExpanded(transaction)).toBe(isExpanded(subProcess));
}));
@ -415,22 +509,16 @@ describe('features/popup-menu', function() {
// given
var transaction = elementRegistry.get('Transaction');
bpmnReplace.openChooser({ x: transaction.x + 100, y: transaction.y + 100 }, transaction);
openPopup(transaction);
var entry = queryEntry(popupMenu, 'replace-with-subprocess');
// when
// replacing the expanded sub process with a transaction
popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
var subProcess = popupMenu.trigger(Events.create(entry, { x: 0, y: 0 }));
// then
// get the morphed transacion from the registry
var subProcess = elementRegistry.filter(function(element, gfx) {
return element.x === transaction.x && element.y === transaction.y;
})[0];
expect(isExpanded(subProcess)).toBe(isExpanded(transaction));
}));
});

View File

@ -7,7 +7,8 @@ var TestHelper = require('../../../TestHelper');
var modelingModule = require('../../../../lib/features/modeling'),
replaceModule = require('../../../../lib/features/replace'),
coreModule = require('../../../../lib/core'),
is = require('../../../../lib/util/ModelUtil').is;
is = require('../../../../lib/util/ModelUtil').is,
isExpanded = require('../../../../lib/util/DiUtil').isExpanded;
describe('features/replace', function() {
@ -21,7 +22,7 @@ describe('features/replace', function() {
describe('should replace', function() {
it('task', inject(function(elementRegistry, modeling, bpmnReplace) {
it('task', inject(function(elementRegistry, bpmnReplace) {
// given
var task = elementRegistry.get('Task_1');
@ -40,7 +41,7 @@ describe('features/replace', function() {
}));
it('gateway', inject(function(elementRegistry, modeling, bpmnReplace) {
it('gateway', inject(function(elementRegistry, bpmnReplace) {
// given
var gateway = elementRegistry.get('ExclusiveGateway_1');
@ -111,8 +112,8 @@ describe('features/replace', function() {
var newElement = bpmnReplace.replaceElement(task, newElementData);
// then
expect(newElement.x).toBe(346);
expect(newElement.y).toBe(149);
expect(newElement.x).toBe(task.x);
expect(newElement.y).toBe(task.y);
}));
});
@ -120,7 +121,8 @@ describe('features/replace', function() {
describe('selection', function() {
it('should select after replace', inject(function(elementRegistry, selection, bpmnReplace) {
it('should select after replace',
inject(function(elementRegistry, selection, bpmnReplace) {
// given
var task = elementRegistry.get('Task_1');
@ -140,7 +142,8 @@ describe('features/replace', function() {
describe('label', function() {
it('should keep copy label', inject(function(elementRegistry, bpmnReplace) {
it('should keep copy label',
inject(function(elementRegistry, bpmnReplace) {
// given
var task = elementRegistry.get('Task_1');
@ -161,7 +164,8 @@ describe('features/replace', function() {
describe('undo support', function() {
it('should undo replace', inject(function(elementRegistry, modeling, bpmnReplace, commandStack) {
it('should undo replace',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var task = elementRegistry.get('Task_1');
@ -183,7 +187,8 @@ describe('features/replace', function() {
}));
it('should redo replace', inject(function(elementRegistry, modeling, bpmnReplace, commandStack, eventBus) {
it('should redo replace',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var task = elementRegistry.get('Task_1');
@ -216,7 +221,8 @@ describe('features/replace', function() {
describe('connection handling', function() {
it('should reconnect valid connections', inject(function(elementRegistry, modeling, bpmnReplace) {
it('should reconnect valid connections',
inject(function(elementRegistry, bpmnReplace) {
// given
var task = elementRegistry.get('Task_1');
@ -241,7 +247,8 @@ describe('features/replace', function() {
}));
it('should remove invalid incomming connections', inject(function(elementRegistry, modeling, bpmnReplace) {
it('should remove invalid incomming connections',
inject(function(elementRegistry, bpmnReplace) {
// given
var task = elementRegistry.get('StartEvent_1');
@ -262,7 +269,8 @@ describe('features/replace', function() {
}));
it('should remove invalid outgoing connections', inject(function(elementRegistry, modeling, bpmnReplace) {
it('should remove invalid outgoing connections',
inject(function(elementRegistry, bpmnReplace) {
// given
var task = elementRegistry.get('EndEvent_1');
@ -285,7 +293,8 @@ describe('features/replace', function() {
describe('undo support', function() {
it('should reconnect valid connections', inject(function(elementRegistry, modeling, bpmnReplace, commandStack) {
it('should reconnect valid connections',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var task = elementRegistry.get('Task_1');
@ -313,8 +322,8 @@ describe('features/replace', function() {
}));
it('should remove invalid incoming connections', inject(function(elementRegistry,
modeling, bpmnReplace, commandStack) {
it('should remove invalid incoming connections',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var startEvent = elementRegistry.get('StartEvent_1');
@ -340,8 +349,8 @@ describe('features/replace', function() {
}));
it('should remove invalid outgoing connections', inject(function(elementRegistry,
modeling, bpmnReplace, commandStack) {
it('should remove invalid outgoing connections',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var endEvent = elementRegistry.get('EndEvent_1');
@ -371,7 +380,8 @@ describe('features/replace', function() {
describe('redo support', function() {
it('should reconnect valid connections', inject(function(elementRegistry, modeling, bpmnReplace, commandStack) {
it('should reconnect valid connections',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var task = elementRegistry.get('Task_1');
@ -398,8 +408,8 @@ describe('features/replace', function() {
}));
it('should remove invalid incoming connections', inject(function(elementRegistry,
modeling, bpmnReplace, commandStack) {
it('should remove invalid incoming connections',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var startEvent = elementRegistry.get('StartEvent_1');
@ -422,8 +432,8 @@ describe('features/replace', function() {
}));
it('should remove invalid outgoing connections', inject(function(elementRegistry,
modeling, bpmnReplace, commandStack) {
it('should remove invalid outgoing connections',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var endEvent = elementRegistry.get('EndEvent_1');
@ -449,7 +459,6 @@ describe('features/replace', function() {
});
describe('children handling', function() {
it('should update bpmn containment properly', inject(function(elementRegistry, modeling, bpmnReplace) {
@ -485,4 +494,81 @@ describe('features/replace', function() {
});
describe('sub processes', function() {
it('should allow morphing expanded into expanded ad hoc',
inject(function(bpmnReplace, elementRegistry) {
// given
var element = elementRegistry.get('SubProcess_1');
var newElementData = {
type: 'bpmn:AdHocSubProcess'
};
// when
var newElement = bpmnReplace.replaceElement(element, newElementData);
// then
expect(is(newElement, 'bpmn:AdHocSubProcess')).toBe(true);
expect(isExpanded(newElement)).toBe(true);
}));
it('should allow morphing expanded ad hoc into expanded',
inject(function(bpmnReplace, elementRegistry) {
// given
var element = elementRegistry.get('AdHocSubProcessExpanded');
var newElementData = {
type: 'bpmn:SubProcess'
};
// when
var newElement = bpmnReplace.replaceElement(element, newElementData);
// then
expect(is(newElement, 'bpmn:SubProcess')).toBe(true);
expect(is(newElement, 'bpmn:AdHocSubProcess')).toBe(false);
expect(isExpanded(newElement)).toBe(true);
}));
it('should allow morphing collapsed into collapsed ad hoc',
inject(function(bpmnReplace, elementRegistry) {
// given
var element = elementRegistry.get('SubProcessCollapsed');
var newElementData = {
type: 'bpmn:AdHocSubProcess'
};
// when
var newElement = bpmnReplace.replaceElement(element, newElementData);
// then
expect(is(newElement, 'bpmn:AdHocSubProcess')).toBe(true);
expect(isExpanded(newElement)).not.toBe(true);
}));
it('should allow morphing collapsed ad hoc into collapsed',
inject(function(bpmnReplace, elementRegistry) {
// given
var element = elementRegistry.get('AdHocSubProcessCollapsed');
var newElementData = {
type: 'bpmn:SubProcess'
};
// when
var newElement = bpmnReplace.replaceElement(element, newElementData);
// then
expect(is(newElement, 'bpmn:SubProcess')).toBe(true);
expect(is(newElement, 'bpmn:AdHocSubProcess')).toBe(false);
expect(isExpanded(newElement)).not.toBe(true);
}));
});
});