chore(replace): separate popup menu from replace logic

This commit makes use of the provider concept introduced
in diagram-js. The replace menu entries are now created
in the replace menu provider. This separates BpmnReplace
from the entry creation in the popup menu.

Closes #428
This commit is contained in:
pedesen 2015-12-10 12:48:40 +01:00 committed by Nico Rehwaldt
parent 93d4bade22
commit eb7b5d7bb4
15 changed files with 1859 additions and 1657 deletions

View File

@ -14,7 +14,7 @@ var assign = require('lodash/object/assign'),
* A provider for BPMN 2.0 elements context pad * A provider for BPMN 2.0 elements context pad
*/ */
function ContextPadProvider(contextPad, modeling, elementFactory, function ContextPadProvider(contextPad, modeling, elementFactory,
connect, create, bpmnReplace, connect, create, popupMenu,
canvas, rules) { canvas, rules) {
contextPad.registerProvider(this); contextPad.registerProvider(this);
@ -26,7 +26,7 @@ function ContextPadProvider(contextPad, modeling, elementFactory,
this._elementFactory = elementFactory; this._elementFactory = elementFactory;
this._connect = connect; this._connect = connect;
this._create = create; this._create = create;
this._bpmnReplace = bpmnReplace; this._popupMenu = popupMenu;
this._canvas = canvas; this._canvas = canvas;
this._rules = rules; this._rules = rules;
} }
@ -37,7 +37,7 @@ ContextPadProvider.$inject = [
'elementFactory', 'elementFactory',
'connect', 'connect',
'create', 'create',
'bpmnReplace', 'popupMenu',
'canvas', 'canvas',
'rules' 'rules'
]; ];
@ -53,7 +53,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
elementFactory = this._elementFactory, elementFactory = this._elementFactory,
connect = this._connect, connect = this._connect,
create = this._create, create = this._create,
bpmnReplace = this._bpmnReplace, popupMenu = this._popupMenu,
canvas = this._canvas, canvas = this._canvas,
rules = this._rules; rules = this._rules;
@ -225,9 +225,14 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
} }
} }
var replaceOptions = bpmnReplace.getReplaceOptions(element); var replaceMenu;
if (popupMenu._providers['bpmn-replace']) {
replaceMenu = popupMenu.create('bpmn-replace', element);
}
if (replaceMenu && !replaceMenu.isEmpty()) {
if (replaceOptions.length) {
// Replace menu entry // Replace menu entry
assign(actions, { assign(actions, {
'replace': { 'replace': {
@ -236,7 +241,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
title: 'Change type', title: 'Change type',
action: { action: {
click: function(event, element) { click: function(event, element) {
bpmnReplace.openChooser(getReplaceMenuPosition(element), element); replaceMenu.open(getReplaceMenuPosition(element));
} }
} }
} }

View File

@ -5,7 +5,7 @@ module.exports = {
require('diagram-js/lib/features/selection'), require('diagram-js/lib/features/selection'),
require('diagram-js/lib/features/connect'), require('diagram-js/lib/features/connect'),
require('diagram-js/lib/features/create'), require('diagram-js/lib/features/create'),
require('../replace') require('../popup-menu')
], ],
__init__: [ 'contextPadProvider' ], __init__: [ 'contextPadProvider' ],
contextPadProvider: [ 'type', require('./ContextPadProvider') ] contextPadProvider: [ 'type', require('./ContextPadProvider') ]

View File

@ -0,0 +1,452 @@
'use strict';
var is = require('../../util/ModelUtil').is,
isEventSubProcess = require('../../util/DiUtil').isEventSubProcess,
getBusinessObject = require('../../util/ModelUtil').getBusinessObject,
isExpanded = require('../../util/DiUtil').isExpanded,
isDifferentType = require('./util/TypeUtil').isDifferentType;
var forEach = require('lodash/collection/forEach'),
filter = require('lodash/collection/filter');
var replaceOptions = require ('../replace/ReplaceOptions');
/**
* This module is an element agnostic replace menu provider for the popup menu.
*/
function ReplaceMenuProvider(popupMenu, modeling, moddle, bpmnReplace, rules) {
this._popupMenu = popupMenu;
this._modeling = modeling;
this._moddle = moddle;
this._bpmnReplace = bpmnReplace;
this._rules = rules;
this.register();
}
ReplaceMenuProvider.$inject = [ 'popupMenu', 'modeling', 'moddle', 'bpmnReplace', 'rules' ];
/**
* Register replace menu provider in the popup menu
*/
ReplaceMenuProvider.prototype.register = function() {
this._popupMenu.registerProvider('bpmn-replace', this);
};
/**
* Get all entries from replaceOptions for the given element and apply filters
* on them. Get for example only elements, which are different from the current one.
*
* @param {djs.model.Base} element
*
* @return {Array<Object>} a list of menu entry items
*/
ReplaceMenuProvider.prototype.getEntries = function(element) {
var businessObject = element.businessObject;
var rules = this._rules;
var entries;
if (!rules.allowed('shape.replace', { element: element })) {
return [];
}
var differentType = isDifferentType(element);
// start events outside event sub processes
if (is(businessObject, 'bpmn:StartEvent') && !isEventSubProcess(businessObject.$parent)) {
entries = filter(replaceOptions.START_EVENT, differentType);
return this._createEntries(element, entries);
}
// start events inside event sub processes
if (is(businessObject, 'bpmn:StartEvent') && isEventSubProcess(businessObject.$parent)) {
entries = filter(replaceOptions.EVENT_SUB_PROCESS_START_EVENT, function(entry) {
var target = entry.target;
var isInterrupting = target.isInterrupting !== false;
var isInterruptingEqual = getBusinessObject(element).isInterrupting === isInterrupting;
// filters elements which types and event definition are equal but have have different interrupting types
return differentType(entry) || !differentType(entry) && !isInterruptingEqual;
});
return this._createEntries(element, entries);
}
// end events
if (is(businessObject, 'bpmn:EndEvent')) {
entries = filter(replaceOptions.END_EVENT, function(entry) {
var target = entry.target;
// hide cancel end events outside transactions
if (target.eventDefinition == 'bpmn:CancelEventDefinition' && !is(businessObject.$parent, 'bpmn:Transaction')) {
return false;
}
return differentType(entry);
});
return this._createEntries(element, entries);
}
// boundary events
if (is(businessObject, 'bpmn:BoundaryEvent')) {
entries = filter(replaceOptions.BOUNDARY_EVENT, function(entry) {
var target = entry.target;
if (target.eventDefinition == 'bpmn:CancelEventDefinition' &&
!is(businessObject.attachedToRef, 'bpmn:Transaction')) {
return false;
}
var cancelActivity = target.cancelActivity !== false;
var isCancelActivityEqual = businessObject.cancelActivity == cancelActivity;
return differentType(entry) || !differentType(entry) && !isCancelActivityEqual;
});
return this._createEntries(element, entries);
}
// intermediate events
if (is(businessObject, 'bpmn:IntermediateCatchEvent') ||
is(businessObject, 'bpmn:IntermediateThrowEvent')) {
entries = filter(replaceOptions.INTERMEDIATE_EVENT, differentType);
return this._createEntries(element, entries);
}
// gateways
if (is(businessObject, 'bpmn:Gateway')) {
entries = filter(replaceOptions.GATEWAY, differentType);
return this._createEntries(element, entries);
}
// transactions
if (is(businessObject, 'bpmn:Transaction')) {
entries = filter(replaceOptions.TRANSACTION, differentType);
return this._createEntries(element, entries);
}
// expanded event sub processes
if (isEventSubProcess(businessObject) && isExpanded(businessObject)) {
entries = filter(replaceOptions.EVENT_SUB_PROCESS, differentType);
return this._createEntries(element, entries);
}
// expanded sub processes
if (is(businessObject, 'bpmn:SubProcess') && isExpanded(businessObject)) {
entries = filter(replaceOptions.SUBPROCESS_EXPANDED, differentType);
return this._createEntries(element, entries);
}
// collapsed ad hoc sub processes
if (is(businessObject, 'bpmn:AdHocSubProcess') && !isExpanded(businessObject)) {
entries = filter(replaceOptions.TASK, function(entry) {
var target = entry.target;
var isTargetSubProcess = target.type === 'bpmn:SubProcess';
return isDifferentType(element, target) && !isTargetSubProcess;
});
return this._createEntries(element, entries);
}
// sequence flows
if (is(businessObject, 'bpmn:SequenceFlow')) {
return this._createSequenceFlowEntries(element, replaceOptions.SEQUENCE_FLOW);
}
// flow nodes
if (is(businessObject, 'bpmn:FlowNode')) {
entries = filter(replaceOptions.TASK, differentType);
return this._createEntries(element, entries);
}
return [];
};
/**
* Get a list of header items for the given element. This includes buttons
* for multi instance markers and for the ad hoc marker.
*
* @param {djs.model.Base} element
*
* @return {Array<Object>} a list of menu entry items
*/
ReplaceMenuProvider.prototype.getHeaderEntries = function(element) {
var headerEntries = [];
if (is(element, 'bpmn:Activity') && !isEventSubProcess(element)) {
headerEntries = headerEntries.concat(this._getLoopEntries(element));
}
if (is(element, 'bpmn:SubProcess') &&
!is(element, 'bpmn:Transaction') &&
!isEventSubProcess(element)) {
headerEntries.push(this._getAdHocEntry(element));
}
return headerEntries;
};
/**
* Creates an array of menu entry objects for a given element and filters the replaceOptions
* according to a filter function.
*
* @param {djs.model.Base} element
* @param {Object} replaceOptions
*
* @return {Array<Object>} a list of menu items
*/
ReplaceMenuProvider.prototype._createEntries = function(element, replaceOptions) {
var menuEntries = [];
var self = this;
forEach(replaceOptions, function(definition) {
var entry = self._createMenuEntry(definition, element);
menuEntries.push(entry);
});
return menuEntries;
};
/**
* Creates an array of menu entry objects for a given sequence flow.
*
* @param {djs.model.Base} element
* @param {Object} replaceOptions
* @return {Array<Object>} a list of menu items
*/
ReplaceMenuProvider.prototype._createSequenceFlowEntries = function (element, replaceOptions) {
var businessObject = getBusinessObject(element);
var menuEntries = [];
var modeling = this._modeling,
moddle = this._moddle;
var self = this;
forEach(replaceOptions, function(entry) {
switch (entry.actionName) {
case 'replace-with-default-flow':
if (businessObject.sourceRef.default !== businessObject &&
(is(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
is(businessObject.sourceRef, 'bpmn:InclusiveGateway'))) {
menuEntries.push(self._createMenuEntry(entry, element, function() {
modeling.updateProperties(element.source, { default: businessObject });
}));
}
break;
case 'replace-with-conditional-flow':
if (!businessObject.conditionExpression && is(businessObject.sourceRef, 'bpmn:Activity')) {
menuEntries.push(self._createMenuEntry(entry, element, function() {
var conditionExpression = moddle.create('bpmn:FormalExpression', { body: '' });
modeling.updateProperties(element, { conditionExpression: conditionExpression });
}));
}
break;
default:
// default flows
if (is(businessObject.sourceRef, 'bpmn:Activity') && businessObject.conditionExpression) {
return menuEntries.push(self._createMenuEntry(entry, element, function() {
modeling.updateProperties(element, { conditionExpression: undefined });
}));
}
// conditional flows
if ((is(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
is(businessObject.sourceRef, 'bpmn:InclusiveGateway')) &&
businessObject.sourceRef.default === businessObject) {
return menuEntries.push(self._createMenuEntry(entry, element, function() {
modeling.updateProperties(element.source, { default: undefined });
}));
}
}
});
return menuEntries;
};
/**
* Creates and returns a single menu entry item.
*
* @param {Object} definition a single replace options definition object
* @param {djs.model.Base} element
* @param {Function} [action] an action callback function which gets called when
* the menu entry is being triggered.
*
* @return {Object} menu entry item
*/
ReplaceMenuProvider.prototype._createMenuEntry = function(definition, element, action) {
var replaceElement = this._bpmnReplace.replaceElement;
var replaceAction = function() {
return replaceElement(element, definition.target);
};
action = action || replaceAction;
var menuEntry = {
label: definition.label,
className: definition.className,
id: definition.actionName,
action: action
};
return menuEntry;
};
/**
* Get a list of menu items containing buttons for multi instance markers
*
* @param {djs.model.Base} element
*
* @return {Array<Object>} a list of menu items
*/
ReplaceMenuProvider.prototype._getLoopEntries = function(element) {
var self = this;
function toggleLoopEntry(event, entry) {
var loopCharacteristics;
if (entry.active) {
loopCharacteristics = undefined;
} else {
loopCharacteristics = self._moddle.create(entry.options.loopCharacteristics);
if (entry.options.isSequential) {
loopCharacteristics.isSequential = entry.options.isSequential;
}
}
self._modeling.updateProperties(element, { loopCharacteristics: loopCharacteristics });
}
var businessObject = getBusinessObject(element),
loopCharacteristics = businessObject.loopCharacteristics;
var isSequential,
isLoop,
isParallel;
if (loopCharacteristics) {
isSequential = loopCharacteristics.isSequential;
isLoop = loopCharacteristics.isSequential === undefined;
isParallel = loopCharacteristics.isSequential !== undefined && !loopCharacteristics.isSequential;
}
var loopEntries = [
{
id: 'toggle-parallel-mi',
className: 'bpmn-icon-parallel-mi-marker',
title: 'Parallel Multi Instance',
active: isParallel,
action: toggleLoopEntry,
options: {
loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
isSequential: false
}
},
{
id: 'toggle-sequential-mi',
className: 'bpmn-icon-sequential-mi-marker',
title: 'Sequential Multi Instance',
active: isSequential,
action: toggleLoopEntry,
options: {
loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
isSequential: true
}
},
{
id: 'toggle-loop',
className: 'bpmn-icon-loop-marker',
title: 'Loop',
active: isLoop,
action: toggleLoopEntry,
options: {
loopCharacteristics: 'bpmn:StandardLoopCharacteristics'
}
}
];
return loopEntries;
};
/**
* Get the menu items containing a button for the ad hoc marker
*
* @param {djs.model.Base} element
*
* @return {Object} a menu item
*/
ReplaceMenuProvider.prototype._getAdHocEntry = function(element) {
var businessObject = getBusinessObject(element);
var isAdHoc = is(businessObject, 'bpmn:AdHocSubProcess');
var replaceElement = this._bpmnReplace.replaceElement;
var adHocEntry = {
id: 'toggle-adhoc',
className: 'bpmn-icon-ad-hoc-marker',
title: 'Ad-hoc',
active: isAdHoc,
action: function(event, entry) {
if (isAdHoc) {
return replaceElement(element, { type: 'bpmn:SubProcess' });
} else {
return replaceElement(element, { type: 'bpmn:AdHocSubProcess' });
}
}
};
return adHocEntry;
};
module.exports = ReplaceMenuProvider;

View File

@ -0,0 +1,4 @@
module.exports = {
__init__: [ 'replaceMenuProvider' ],
replaceMenuProvider: [ 'type', require('./ReplaceMenuProvider') ]
};

View File

@ -0,0 +1,28 @@
'use strict';
var getBusinessObject = require('../../../util/ModelUtil').getBusinessObject;
/**
* Returns true, if an element is from a different type than a target definition.
* Takes into account the type and the event definition type.
*
* @param {djs.model.Base} element
*
* @return {Boolean}
*/
function isDifferentType(element) {
return function(entry) {
var target = entry.target;
var businessObject = getBusinessObject(element),
eventDefinition = businessObject.eventDefinitions && businessObject.eventDefinitions[0].$type;
var isEventDefinitionEqual = target.eventDefinition == eventDefinition,
isTypeEqual = businessObject.$type == target.type;
return (!isEventDefinitionEqual && isTypeEqual) || !isTypeEqual;
};
}
module.exports.isDifferentType = isDifferentType;

View File

@ -1,30 +1,12 @@
'use strict'; 'use strict';
var forEach = require('lodash/collection/forEach'), var pick = require('lodash/object/pick'),
filter = require('lodash/collection/filter'),
pick = require('lodash/object/pick'),
assign = require('lodash/object/assign'); assign = require('lodash/object/assign');
var REPLACE_OPTIONS = require ('./ReplaceOptions');
var startEventReplace = REPLACE_OPTIONS.START_EVENT,
intermediateEventReplace = REPLACE_OPTIONS.INTERMEDIATE_EVENT,
endEventReplace = REPLACE_OPTIONS.END_EVENT,
gatewayReplace = REPLACE_OPTIONS.GATEWAY,
taskReplace = REPLACE_OPTIONS.TASK,
subProcessExpandedReplace = REPLACE_OPTIONS.SUBPROCESS_EXPANDED,
transactionReplace = REPLACE_OPTIONS.TRANSACTION,
eventSubProcessReplace = REPLACE_OPTIONS.EVENT_SUB_PROCESS,
boundaryEventReplace = REPLACE_OPTIONS.BOUNDARY_EVENT,
eventSubProcessStartEventReplace = REPLACE_OPTIONS.EVENT_SUB_PROCESS_START_EVENT,
sequenceFlowReplace = REPLACE_OPTIONS.SEQUENCE_FLOW;
var is = require('../../util/ModelUtil').is, var is = require('../../util/ModelUtil').is,
getBusinessObject = require('../../util/ModelUtil').getBusinessObject,
isExpanded = require('../../util/DiUtil').isExpanded, isExpanded = require('../../util/DiUtil').isExpanded,
isEventSubProcess = require('../../util/DiUtil').isEventSubProcess; isEventSubProcess = require('../../util/DiUtil').isEventSubProcess;
var CUSTOM_PROPERTIES = [ var CUSTOM_PROPERTIES = [
'cancelActivity', 'cancelActivity',
'instantiate', 'instantiate',
@ -35,19 +17,9 @@ var CUSTOM_PROPERTIES = [
/** /**
* A replace menu provider that gives users the controls to choose * This module takes care of replacing BPMN elements
* and replace BPMN elements with each other.
*
* @param {BpmnFactory} bpmnFactory
* @param {Moddle} moddle
* @param {PopupMenu} popupMenu
* @param {Replace} replace
*/ */
function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modeling, eventBus, rules) { function BpmnReplace(bpmnFactory, replace, selection, modeling) {
var self = this,
currentElement;
/** /**
* Prepares a new business object for the replacement element * Prepares a new business object for the replacement element
@ -56,6 +28,7 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
* @param {djs.model.Base} element * @param {djs.model.Base} element
* @param {Object} target * @param {Object} target
* @param {Object} [hints] * @param {Object} [hints]
*
* @return {djs.model.Base} the newly created element * @return {djs.model.Base} the newly created element
*/ */
function replaceElement(element, target, hints) { function replaceElement(element, target, hints) {
@ -74,7 +47,7 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
// initialize custom BPMN extensions // initialize custom BPMN extensions
if (target.eventDefinition) { if (target.eventDefinition) {
var eventDefinitions = businessObject.get('eventDefinitions'), var eventDefinitions = businessObject.get('eventDefinitions'),
eventDefinition = moddle.create(target.eventDefinition); eventDefinition = bpmnFactory.create(target.eventDefinition);
eventDefinition.$parent = businessObject; eventDefinition.$parent = businessObject;
eventDefinitions.push(eventDefinition); eventDefinitions.push(eventDefinition);
@ -119,335 +92,9 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
return newElement; return newElement;
} }
function toggleLoopEntry(event, entry) {
var loopEntries = self.getLoopEntries(currentElement);
var loopCharacteristics;
if (entry.active) {
loopCharacteristics = undefined;
} else {
forEach(loopEntries, function(action) {
var options = action.options;
if (entry.id === action.id) {
loopCharacteristics = moddle.create(options.loopCharacteristics);
if (options.isSequential) {
loopCharacteristics.isSequential = options.isSequential;
}
}
});
}
modeling.updateProperties(currentElement, { loopCharacteristics: loopCharacteristics });
}
function getLoopEntries(element) {
currentElement = element;
var businessObject = getBusinessObject(element),
loopCharacteristics = businessObject.loopCharacteristics;
var isSequential,
isLoop,
isParallel;
if (loopCharacteristics) {
isSequential = loopCharacteristics.isSequential;
isLoop = loopCharacteristics.isSequential === undefined;
isParallel = loopCharacteristics.isSequential !== undefined && !loopCharacteristics.isSequential;
}
var loopEntries = [
{
id: 'toggle-parallel-mi',
className: 'bpmn-icon-parallel-mi-marker',
title: 'Parallel Multi Instance',
active: isParallel,
action: toggleLoopEntry,
options: {
loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
isSequential: false
}
},
{
id: 'toggle-sequential-mi',
className: 'bpmn-icon-sequential-mi-marker',
title: 'Sequential Multi Instance',
active: isSequential,
action: toggleLoopEntry,
options: {
loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
isSequential: true
}
},
{
id: 'toggle-loop',
className: 'bpmn-icon-loop-marker',
title: 'Loop',
active: isLoop,
action: toggleLoopEntry,
options: {
loopCharacteristics: 'bpmn:StandardLoopCharacteristics'
}
}
];
return loopEntries;
}
function getAdHocEntry(element) {
var businessObject = getBusinessObject(element);
var isAdHoc = is(businessObject, 'bpmn:AdHocSubProcess');
var adHocEntry = {
id: 'toggle-adhoc',
className: 'bpmn-icon-ad-hoc-marker',
title: 'Ad-hoc',
active: isAdHoc,
action: function(event, entry) {
if (isAdHoc) {
return replaceElement(element, { type: 'bpmn:SubProcess' });
} else {
return replaceElement(element, { type: 'bpmn:AdHocSubProcess' });
}
}
};
return adHocEntry;
}
this.getReplaceOptions = function(element) {
if (!rules.allowed('shape.replace', { element: element })) {
return [];
}
var menuEntries = [];
var businessObject = element.businessObject;
// start events outside event sub processes
if (is(businessObject, 'bpmn:StartEvent') && !isEventSubProcess(businessObject.$parent)) {
addEntries(startEventReplace, filterEvents);
} else
// start events inside event sub processes
if (is(businessObject, 'bpmn:StartEvent') && isEventSubProcess(businessObject.$parent)) {
addEntries(eventSubProcessStartEventReplace, filterEvents);
} else
if (is(businessObject, 'bpmn:IntermediateCatchEvent') ||
is(businessObject, 'bpmn:IntermediateThrowEvent')) {
addEntries(intermediateEventReplace, filterEvents);
} else
if (is(businessObject, 'bpmn:EndEvent')) {
addEntries(endEventReplace, filterEvents);
} else
if (is(businessObject, 'bpmn:Gateway')) {
addEntries(gatewayReplace, function(entry) {
return entry.target.type !== businessObject.$type;
});
} else
if (is(businessObject, 'bpmn:Transaction')) {
addEntries(transactionReplace);
} else
if (isEventSubProcess(businessObject) && isExpanded(businessObject)) {
addEntries(eventSubProcessReplace);
} else
if (is(businessObject, 'bpmn:SubProcess') && isExpanded(businessObject)) {
addEntries(subProcessExpandedReplace);
} else
if (is(businessObject, 'bpmn:AdHocSubProcess') && !isExpanded(businessObject)) {
addEntries(taskReplace, function(entry) {
return entry.target.type !== 'bpmn:SubProcess';
});
} else
if (is(businessObject, 'bpmn:BoundaryEvent')) {
addEntries(boundaryEventReplace, filterEvents);
} else
if (is(businessObject, 'bpmn:SequenceFlow')) {
addSequenceFlowEntries(sequenceFlowReplace);
} else
if (is(businessObject, 'bpmn:FlowNode')) {
addEntries(taskReplace, function(entry) {
return entry.target.type !== businessObject.$type;
});
}
function addSequenceFlowEntries(entries) {
forEach(entries, function(entry) {
switch (entry.actionName) {
case 'replace-with-default-flow':
if (businessObject.sourceRef.default !== businessObject &&
(is(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
is(businessObject.sourceRef, 'bpmn:InclusiveGateway'))) {
menuEntries.push(addMenuEntry(entry, function() {
modeling.updateProperties(element.source, { default: businessObject });
}));
}
break;
case 'replace-with-conditional-flow':
if (!businessObject.conditionExpression && is(businessObject.sourceRef, 'bpmn:Activity')) {
menuEntries.push(addMenuEntry(entry, function() {
var conditionExpression = moddle.create('bpmn:FormalExpression', { body: ''});
modeling.updateProperties(element, { conditionExpression: conditionExpression });
}));
}
break;
default:
// default flows
if (is(businessObject.sourceRef, 'bpmn:Activity') && businessObject.conditionExpression) {
return menuEntries.push(addMenuEntry(entry, function() {
modeling.updateProperties(element, { conditionExpression: undefined });
}));
}
// conditional flows
if ((is(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
is(businessObject.sourceRef, 'bpmn:InclusiveGateway')) &&
businessObject.sourceRef.default === businessObject) {
return menuEntries.push(addMenuEntry(entry, function() {
modeling.updateProperties(element.source, { default: undefined });
}));
}
}
});
}
function filterEvents(entry) {
var target = entry.target;
var eventDefinition = businessObject.eventDefinitions && businessObject.eventDefinitions[0].$type;
var isEventDefinitionEqual = target.eventDefinition == eventDefinition,
isEventTypeEqual = businessObject.$type == target.type;
// filter for boundary events
if (is(businessObject, 'bpmn:BoundaryEvent')) {
if (target.eventDefinition == 'bpmn:CancelEventDefinition' &&
!is(businessObject.attachedToRef, 'bpmn:Transaction')) {
return false;
}
var cancelActivity = target.cancelActivity !== false;
var isCancelActivityEqual = businessObject.cancelActivity == cancelActivity;
return !(isEventDefinitionEqual && isEventTypeEqual && isCancelActivityEqual);
}
// filter for start events inside event sub processes
if (is(businessObject, 'bpmn:StartEvent') && isEventSubProcess(businessObject.$parent)) {
var isInterrupting = target.isInterrupting !== false;
var isInterruptingEqual = businessObject.isInterrupting == isInterrupting;
return !(isEventDefinitionEqual && isEventDefinitionEqual && isInterruptingEqual);
}
if (is(businessObject, 'bpmn:EndEvent') && target.eventDefinition == 'bpmn:CancelEventDefinition' &&
!is(businessObject.$parent, 'bpmn:Transaction')) {
return false;
}
// filter for all other elements
return (!isEventDefinitionEqual && isEventTypeEqual) || !isEventTypeEqual;
}
function addEntries(entries, filterFun) {
// Filter selected type from the array
var filteredEntries = filter(entries, filterFun);
// Add entries to replace menu
forEach(filteredEntries, function(definition) {
var entry = addMenuEntry(definition);
menuEntries.push(entry);
});
}
function addMenuEntry(definition, action) {
var menuEntry = {
label: definition.label,
className: definition.className,
id: definition.actionName,
action: action
};
if (!menuEntry.action) {
menuEntry.action = function() {
return replaceElement(element, definition.target);
};
}
return menuEntry;
}
return menuEntries;
};
/**
* Open a replace chooser for an element on the specified position.
*
* @param {Object} position
* @param {Object} element
*/
this.openChooser = function(position, element) {
var entries = this.getReplaceOptions(element),
headerEntries = [];
if (is(element, 'bpmn:Activity') && !isEventSubProcess(element)) {
headerEntries = headerEntries.concat(this.getLoopEntries(element));
}
if (is(element, 'bpmn:SubProcess') &&
!is(element, 'bpmn:Transaction') &&
!isEventSubProcess(element)) {
headerEntries.push(this.getAdHocEntry(element));
}
popupMenu.open({
className: 'replace-menu',
element: element,
position: position,
headerEntries: headerEntries,
entries: entries
});
};
this.getLoopEntries = getLoopEntries;
this.getAdHocEntry = getAdHocEntry;
this.replaceElement = replaceElement; this.replaceElement = replaceElement;
} }
BpmnReplace.$inject = [ 'bpmnFactory', 'moddle', 'popupMenu', 'replace', 'selection', 'modeling', 'eventBus', 'rules' ]; BpmnReplace.$inject = [ 'bpmnFactory', 'replace', 'selection', 'modeling' ];
module.exports = BpmnReplace; module.exports = BpmnReplace;

View File

@ -723,16 +723,16 @@ module.exports.SEQUENCE_FLOW = [
{ {
label: 'Sequence Flow', label: 'Sequence Flow',
actionName: 'replace-with-sequence-flow', actionName: 'replace-with-sequence-flow',
className: 'bpmn-icon-connection', className: 'bpmn-icon-connection'
}, },
{ {
label: 'Default Flow', label: 'Default Flow',
actionName: 'replace-with-default-flow', actionName: 'replace-with-default-flow',
className: 'bpmn-icon-default-flow', className: 'bpmn-icon-default-flow'
}, },
{ {
label: 'Conditional Flow', label: 'Conditional Flow',
actionName: 'replace-with-conditional-flow', actionName: 'replace-with-conditional-flow',
className: 'bpmn-icon-conditional-flow', className: 'bpmn-icon-conditional-flow'
} }
]; ];

View File

@ -13,6 +13,7 @@ var contextPadModule = require('../../../../lib/features/context-pad'),
coreModule = require('../../../../lib/core'), coreModule = require('../../../../lib/core'),
modelingModule = require('../../../../lib/features/modeling'), modelingModule = require('../../../../lib/features/modeling'),
popupModule = require('diagram-js/lib/features/popup-menu'), popupModule = require('diagram-js/lib/features/popup-menu'),
replaceMenuProvider = require('../../../../lib/features/popup-menu'),
replaceModule = require('diagram-js/lib/features/replace'), replaceModule = require('diagram-js/lib/features/replace'),
rulesModule = require('../../../util/custom-rules'); rulesModule = require('../../../util/custom-rules');
@ -21,14 +22,15 @@ describe('features - context-pad', function() {
var diagramXML = require('../../../fixtures/bpmn/simple.bpmn'); var diagramXML = require('../../../fixtures/bpmn/simple.bpmn');
var testModules = [ contextPadModule, coreModule, modelingModule, popupModule, replaceModule, rulesModule ]; var testModules = [ contextPadModule, coreModule, modelingModule, popupModule,
replaceModule, rulesModule, replaceMenuProvider ];
beforeEach(bootstrapViewer(diagramXML, { modules: testModules })); beforeEach(bootstrapViewer(diagramXML, { modules: testModules }));
describe('bootstrap', function() { describe('bootstrap', function() {
it('should bootstrap', inject(function(contextPadProvider) { it('should bootstrap', inject(function(contextPadProvider, replaceMenuProvider) {
expect(contextPadProvider).to.exist; expect(contextPadProvider).to.exist;
})); }));
@ -60,7 +62,8 @@ describe('features - context-pad', function() {
})); }));
it('should include delete action when rule returns true', inject(function (elementRegistry, contextPad, customRules) { it('should include delete action when rule returns true',
inject(function (elementRegistry, contextPad, customRules) {
// given // given
customRules.addRule('elements.delete', function() { customRules.addRule('elements.delete', function() {
@ -77,7 +80,8 @@ describe('features - context-pad', function() {
})); }));
it('should NOT include delete action when rule returns false', inject(function(elementRegistry, contextPad, customRules) { it('should NOT include delete action when rule returns false',
inject(function(elementRegistry, contextPad, customRules) {
// given // given
customRules.addRule('elements.delete', function() { customRules.addRule('elements.delete', function() {
@ -114,7 +118,8 @@ describe('features - context-pad', function() {
})); }));
it('should include delete action when [ element ] is returned from rule', inject(function(elementRegistry, contextPad, customRules) { it('should include delete action when [ element ] is returned from rule',
inject(function(elementRegistry, contextPad, customRules) {
// given // given
customRules.addRule('elements.delete', function(context) { customRules.addRule('elements.delete', function(context) {
@ -131,7 +136,8 @@ describe('features - context-pad', function() {
})); }));
it('should NOT include delete action when [ ] is returned from rule', inject(function(elementRegistry, contextPad, customRules) { it('should NOT include delete action when [ ] is returned from rule',
inject(function(elementRegistry, contextPad, customRules) {
// given // given
customRules.addRule('elements.delete', function() { customRules.addRule('elements.delete', function() {
@ -176,7 +182,7 @@ describe('features - context-pad', function() {
// when // when
contextPad.trigger('click', event); contextPad.trigger('click', event);
replaceMenuRect = domQuery('.replace-menu', container).getBoundingClientRect(); replaceMenuRect = domQuery('.bpmn-replace', container).getBoundingClientRect();
// then // then
expect(replaceMenuRect.left).to.be.at.most(padMenuRect.left); expect(replaceMenuRect.left).to.be.at.most(padMenuRect.left);

View File

@ -1,702 +0,0 @@
'use strict';
/* global bootstrapModeler, inject */
var TestHelper = require('../../../TestHelper');
var globalEvent = require('../../../util/MockEvents.js').createEvent;
var coreModule = require('../../../../lib/core'),
popupMenuModule = require('diagram-js/lib/features/popup-menu'),
modelingModule = require('../../../../lib/features/modeling'),
replaceModule = require('../../../../lib/features/replace');
var domQuery = require('min-dom/lib/query'),
domClasses = require('min-dom/lib/classes');
var is = require('../../../../lib/util/ModelUtil').is,
isExpanded = require('../../../../lib/util/DiUtil').isExpanded;
function queryEntry(popupMenu, id) {
return queryPopup(popupMenu, '[data-id="' + id + '"]');
}
function queryPopup(popupMenu, selector) {
return domQuery(selector, popupMenu._current.container);
}
describe('features/popup-menu', function() {
var diagramXMLMarkers = require('../../../fixtures/bpmn/draw/activity-markers-simple.bpmn'),
diagramXMLReplace = require('../../../fixtures/bpmn/features/replace/01_replace.bpmn');
var testModules = [ coreModule, modelingModule, popupMenuModule, replaceModule ];
var openPopup = function(element, offset) {
offset = offset || 100;
TestHelper.getBpmnJS().invoke(function(bpmnReplace){
bpmnReplace.openChooser({ x: element.x + offset, y: element.y + offset }, element);
});
};
describe('toggle', function(){
beforeEach(bootstrapModeler(diagramXMLMarkers, { modules: testModules }));
describe('active attribute', function(){
it('should be true for parallel marker', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('ParallelTask'),
loopCharacteristics = task.businessObject.loopCharacteristics;
// when
openPopup(task);
// then
expect(is(loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).to.be.true;
expect(loopCharacteristics.isSequential).to.be.false;
expect(loopCharacteristics.isSequential).to.exist;
expect(popupMenu._getEntry('toggle-parallel-mi').active).to.be.true;
}));
it('should be true for sequential marker', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask'),
loopCharacteristics = task.businessObject.loopCharacteristics;
// when
openPopup(task);
// then
expect(loopCharacteristics.isSequential).to.be.true;
expect(popupMenu._getEntry('toggle-sequential-mi').active).to.be.true;
expect(is(loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).to.be.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).not.to.exist;
expect(popupMenu._getEntry('toggle-loop').active).to.be.true;
expect(is(loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).to.be.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).to.be.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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(subProcess);
// then
var adHocEntry = queryEntry(popupMenu, 'toggle-adhoc');
expect(domClasses(adHocEntry).has('active')).to.be.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(globalEvent(parallelEntry, { x: 0, y: 0 }));
// toggle ad hoc on
openPopup(subProcess);
var adHocEntry = queryEntry(popupMenu, 'toggle-adhoc');
var adHocSubProcess = popupMenu.trigger(globalEvent(adHocEntry, { x: 0, y: 0 }));
openPopup(adHocSubProcess);
// then
parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
adHocEntry = queryEntry(popupMenu, 'toggle-adhoc');
expect(domClasses(parallelEntry).has('active')).to.be.true;
expect(domClasses(adHocEntry).has('active')).to.be.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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(task);
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(task.businessObject.loopCharacteristics).not.to.exist;
expect(domClasses(parallelEntry).has('active')).to.be.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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(task);
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(domClasses(parallelEntry).has('active')).to.be.true;
expect(task.businessObject.loopCharacteristics.isSequential).to.be.false;
expect(is(task.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).to.be.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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(task);
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(domClasses(sequentialEntry).has('active')).to.be.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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(task);
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(domClasses(loopEntry).has('active')).to.be.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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(task);
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(task.businessObject.loopCharacteristics).not.to.exist;
expect(domClasses(sequentialEntry).has('active')).to.be.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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(task);
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(domClasses(sequentialEntry).has('active')).to.be.true;
expect(task.businessObject.loopCharacteristics.isSequential).to.be.true;
expect(is(task.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).to.be.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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(task);
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(domClasses(loopEntry).has('active')).to.be.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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(task);
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(domClasses(parallelEntry).has('active')).to.be.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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(task);
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(domClasses(loopEntry).has('active')).to.be.false;
expect(is(task.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).not.to.exist;
}));
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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(task);
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(domClasses(loopEntry).has('active')).to.be.true;
expect(is(task.businessObject.loopCharacteristics, 'bpmn:StandardLoopCharacteristics')).to.be.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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(task);
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(domClasses(sequentialEntry).has('active')).to.be.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(globalEvent(entry, { x: 0, y: 0 }));
openPopup(task);
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(domClasses(parallelEntry).has('active')).to.be.false;
}));
});
});
describe('replacing', function() {
beforeEach(bootstrapModeler(diagramXMLMarkers, { modules: testModules }));
it('should retain the loop characteristics', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask');
openPopup(task);
var entry = queryEntry(popupMenu, 'replace-with-send-task');
// when
// replacing the task with a send task
var sendTask = popupMenu.trigger(globalEvent(entry, { x: 0, y: 0 }));
// then
expect(sendTask.businessObject.loopCharacteristics).to.exist;
expect(sendTask.businessObject.loopCharacteristics.isSequential).to.be.true;
expect(is(sendTask.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).to.be.true;
}));
it('should retain the loop characteristics for call activites',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask');
openPopup(task);
var entry = queryEntry(popupMenu, 'replace-with-call-activity');
// when
// replacing the task with a call activity
var callActivity = popupMenu.trigger(globalEvent(entry, { x: 0, y: 0 }));
// then
expect(callActivity.businessObject.loopCharacteristics).to.exist;
expect(callActivity.businessObject.loopCharacteristics.isSequential).to.be.true;
expect(is(callActivity.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).to.be.true;
}));
it('should retain expanded status for sub processes',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var subProcess = elementRegistry.get('SubProcess');
openPopup(subProcess);
var entry = queryEntry(popupMenu, 'replace-with-transaction');
// when
// replacing the expanded sub process with a transaction
var transaction = popupMenu.trigger(globalEvent(entry, { x: 0, y: 0 }));
// then
expect(isExpanded(transaction)).to.equal(isExpanded(subProcess));
}));
it('should retain the loop characteristics and the expanded status for transactions',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var transaction = elementRegistry.get('Transaction');
openPopup(transaction);
var entry = queryEntry(popupMenu, 'replace-with-subprocess');
// when
// replacing the transaction with an expanded sub process
var subProcess = popupMenu.trigger(globalEvent(entry, { x: 0, y: 0 }));
// then
expect(isExpanded(subProcess)).to.equal(isExpanded(transaction));
}));
it('should not retain the loop characteristics morphing to an event sub process',
inject(function(popupMenu, bpmnReplace, elementRegistry, modeling) {
// given
var transaction = elementRegistry.get('Transaction');
modeling.updateProperties(transaction, { loopCharacteristics: { isparallel: true } });
openPopup(transaction);
var entry = queryEntry(popupMenu, 'replace-with-event-subprocess');
// when
// replacing the transaction with an event sub process
var subProcess = popupMenu.trigger(globalEvent(entry, { x: 0, y: 0 }));
// then
expect(isExpanded(subProcess)).to.equal(isExpanded(transaction));
}));
it('should retain the expanded property morphing to an event sub processes',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var transaction = elementRegistry.get('Transaction');
openPopup(transaction);
var entry = queryEntry(popupMenu, 'replace-with-event-subprocess');
// when
// replacing the transaction with an expanded sub process
var eventSubProcess = popupMenu.trigger(globalEvent(entry, { x: 0, y: 0 }));
// then
expect(isExpanded(eventSubProcess)).to.equal(isExpanded(transaction));
}));
});
describe('replace menu', function() {
beforeEach(bootstrapModeler(diagramXMLReplace, { modules: testModules }));
it('should contain all start events except the current one',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var startEvent = elementRegistry.get('StartEvent_1');
// when
openPopup(startEvent);
var entriesContainer = queryPopup(popupMenu, '.djs-popup-body');
// then
expect(queryEntry(popupMenu, 'replace-with-none-start')).to.be.null;
expect(entriesContainer.childNodes.length).to.equal(6);
}));
it('should contain all intermediate events except the current one',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var intermediateEvent = elementRegistry.get('IntermediateThrowEvent_1');
// when
openPopup(intermediateEvent);
var entriesContainer = queryPopup(popupMenu, '.djs-popup-body');
// then
expect(queryEntry(popupMenu, 'replace-with-none-intermediate-throw')).to.be.null;
expect(entriesContainer.childNodes.length).to.equal(12);
}));
it('should contain all end events except the current one',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var endEvent = elementRegistry.get('EndEvent_1');
// when
openPopup(endEvent);
var entriesContainer = queryPopup(popupMenu, '.djs-popup-body');
// then
expect(queryEntry(popupMenu, 'replace-with-none-end')).to.be.null;
expect(entriesContainer.childNodes.length).to.equal(8);
}));
it('should contain all start events inside event sub process except the current one',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var startEvent = elementRegistry.get('StartEvent_3');
// when
openPopup(startEvent);
var entriesContainer = queryPopup(popupMenu, '.djs-popup-body');
// then
expect(queryEntry(popupMenu, 'replace-with-non-interrupting-message-start')).to.be.null;
expect(queryEntry(popupMenu, 'replace-with-message-start')).to.exist;
expect(entriesContainer.childNodes.length).to.equal(11);
}));
it('should contain all non interrupting start events inside event sub process except the current one',
inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var startEvent = elementRegistry.get('StartEvent_3');
var newElement = bpmnReplace.replaceElement(startEvent, {
type: 'bpmn:StartEvent',
eventDefinition: 'bpmn:ConditionalEventDefinition',
isInterrupting: false
});
// when
openPopup(newElement);
var entriesContainer = queryPopup(popupMenu, '.djs-popup-body');
// then
expect(queryEntry(popupMenu, 'replace-with-conditional-start')).to.exist;
expect(queryEntry(popupMenu, 'replace-with-non-interrupting-conditional-start')).to.be.null;
expect(entriesContainer.childNodes.length).to.equal(11);
}));
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(queryEntry(popupMenu, 'replace-with-conditional-intermediate-catch')).to.be.null;
expect(entriesContainer.childNodes.length).to.equal(10);
}));
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(queryEntry(popupMenu, 'replace-with-non-interrupting-message-intermediate-catch')).to.be.null;
expect(entriesContainer.childNodes.length).to.equal(10);
}));
});
});

File diff suppressed because it is too large Load Diff

View File

@ -881,520 +881,6 @@ describe('features/replace', function() {
}); });
describe('Cancel Events', function () {
var diagramXML = require('../../../fixtures/bpmn/features/replace/cancel-events.bpmn');
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
describe('- normal -', function() {
it('should show Cancel Event replace option',
inject(function(elementRegistry, bpmnReplace) {
// given
var endEvent = elementRegistry.get('EndEvent_1');
// when
var opts = bpmnReplace.getReplaceOptions(endEvent);
// then
expect(opts).to.have.length(9);
}));
it('should NOT show Cancel Event replace option',
inject(function(elementRegistry, bpmnReplace) {
// given
var endEvent = elementRegistry.get('EndEvent_2');
// when
var opts = bpmnReplace.getReplaceOptions(endEvent);
// then
expect(opts).to.have.length(8);
}));
});
describe('- boundary events -', function() {
it('should NOT show Cancel Event replace option',
inject(function(elementRegistry, bpmnReplace) {
// given
var boundaryEvent = elementRegistry.get('BoundaryEvent_1');
// when
var opts = bpmnReplace.getReplaceOptions(boundaryEvent);
// then
expect(opts).to.have.length(12);
}));
it('should NOT show Cancel Event replace option',
inject(function(elementRegistry, bpmnReplace) {
// given
var boundaryEvent = elementRegistry.get('BoundaryEvent_2');
// when
var opts = bpmnReplace.getReplaceOptions(boundaryEvent);
// then
expect(opts).to.have.length(11);
}));
});
});
describe('default flows from inclusive gateways', function () {
var diagramXML = require('./BpmnReplace.defaultFlowsFromInclusiveGateways.bpmn');
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
it ('should show Default replace option', inject(function (elementRegistry, bpmnReplace) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_2');
// when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// then
expect(opts).to.have.length(1);
}));
it ('should NOT show Default replace option', inject(function (elementRegistry, bpmnReplace) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_1');
// when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// then
expect(opts).to.have.length(1);
}));
});
describe('default flows', function() {
var diagramXML = require('./BpmnReplace.defaultFlows.bpmn');
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
it('should show Default replace option', inject(function(elementRegistry, bpmnReplace) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3');
// when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// then
expect(opts).to.have.length(1);
}));
it('should NOT show Default replace option', inject(function(elementRegistry, bpmnReplace) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_4');
// when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// then
expect(opts).to.have.length(0);
}));
it('should replace SequenceFlow with DefaultFlow', inject(function(elementRegistry, bpmnReplace) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3');
// when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// trigger DefaultFlow replacement
opts[0].action();
var gateway = elementRegistry.get('ExclusiveGateway_1');
// then
expect(gateway.businessObject.default).to.equal(sequenceFlow.businessObject);
}));
it('should replace SequenceFlow with DefaultFlow -> undo',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3');
// when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// trigger DefaultFlow replacement
opts[0].action();
commandStack.undo();
var gateway = elementRegistry.get('ExclusiveGateway_1');
// then
expect(gateway.businessObject.default).to.not.exist;
}));
it('should only have one DefaultFlow', inject(function(elementRegistry, bpmnReplace) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_1'),
sequenceFlow2 = elementRegistry.get('SequenceFlow_3');
// when
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow),
sequenceFlowOpts2 = bpmnReplace.getReplaceOptions(sequenceFlow2);
// trigger DefaultFlow replacement
sequenceFlowOpts2[0].action();
sequenceFlowOpts[0].action();
var gateway = elementRegistry.get('ExclusiveGateway_1');
// then
expect(gateway.businessObject.default).to.equal(sequenceFlow.businessObject);
}));
it('should replace DefaultFlow with SequenceFlow when changing source',
inject(function(elementRegistry, bpmnReplace, modeling) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_1'),
task = elementRegistry.get('Task_2');
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
// trigger DefaultFlow replacement
sequenceFlowOpts[0].action();
// when
modeling.reconnectStart(sequenceFlow, task, [
{ x: 686, y: 267, original: { x: 686, y: 307 } },
{ x: 686, y: 207, original: { x: 686, y: 187 } }
]);
var gateway = elementRegistry.get('ExclusiveGateway_1');
// then
expect(gateway.businessObject.default).to.not.exist;
}));
it('should replace DefaultFlow with SequenceFlow when changing source -> undo',
inject(function(elementRegistry, bpmnReplace, modeling, commandStack) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_1'),
task = elementRegistry.get('Task_2');
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
// trigger DefaultFlow replacement
sequenceFlowOpts[0].action();
// when
modeling.reconnectStart(sequenceFlow, task, [
{ x: 686, y: 267, original: { x: 686, y: 307 } },
{ x: 686, y: 207, original: { x: 686, y: 187 } }
]);
commandStack.undo();
var gateway = elementRegistry.get('ExclusiveGateway_1');
// then
expect(gateway.businessObject.default).equal(sequenceFlow.businessObject);
}));
it('should replace DefaultFlow with SequenceFlow when changing target',
inject(function(elementRegistry, elementFactory, canvas, bpmnReplace, modeling) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_1'),
root = canvas.getRootElement();
var intermediateEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent'});
modeling.createShape(intermediateEvent, { x: 686, y: 50 }, root);
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
// trigger DefaultFlow replacement
sequenceFlowOpts[0].action();
// when
modeling.reconnectEnd(sequenceFlow, intermediateEvent, [
{ x: 686, y: 267, original: { x: 686, y: 307 } },
{ x: 686, y: 50, original: { x: 686, y: 75 } }
]);
var gateway = elementRegistry.get('ExclusiveGateway_1');
// then
expect(gateway.businessObject.default).to.not.exist;
}));
it('should replace DefaultFlow with SequenceFlow when changing target -> undo',
inject(function(elementRegistry, elementFactory, canvas, bpmnReplace, modeling, commandStack) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_1'),
root = canvas.getRootElement();
var intermediateEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent'});
modeling.createShape(intermediateEvent, { x: 686, y: 50 }, root);
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
// trigger DefaultFlow replacement
sequenceFlowOpts[0].action();
// when
modeling.reconnectEnd(sequenceFlow, intermediateEvent, [
{ x: 686, y: 267, original: { x: 686, y: 307 } },
{ x: 686, y: 50, original: { x: 686, y: 75 } }
]);
commandStack.undo();
var gateway = elementRegistry.get('ExclusiveGateway_1');
// then
expect(gateway.businessObject.default).equal(sequenceFlow.businessObject);
}));
it('should keep DefaultFlow when morphing Gateway', inject(function(elementRegistry, bpmnReplace) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3'),
exclusiveGateway = elementRegistry.get('ExclusiveGateway_1');
// when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// trigger DefaultFlow replacement
opts[0].action();
var inclusiveGateway = bpmnReplace.replaceElement(exclusiveGateway, { type: 'bpmn:InclusiveGateway'});
// then
expect(inclusiveGateway.businessObject.default).to.equal(sequenceFlow.businessObject);
}));
it('should keep DefaultFlow when morphing Gateway -> undo',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3'),
exclusiveGateway = elementRegistry.get('ExclusiveGateway_1');
// when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// trigger DefaultFlow replacement
opts[0].action();
bpmnReplace.replaceElement(exclusiveGateway, { type: 'bpmn:InclusiveGateway'});
commandStack.undo();
// then
expect(exclusiveGateway.businessObject.default).to.equal(sequenceFlow.businessObject);
}));
});
describe('conditional flows', function() {
var diagramXML = require('./BpmnReplace.conditionalFlows.bpmn');
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
it('should show ConditionalFlow replace option', inject(function(elementRegistry, bpmnReplace) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3');
//when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// then
expect(opts).to.have.length(1);
}));
it('should NOT show ConditionalFlow replace option', inject(function(elementRegistry, bpmnReplace) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_1');
//when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// then
expect(opts).to.have.length(0);
}));
it('should morph into a ConditionalFlow', inject(function(elementRegistry, bpmnReplace) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_2');
//when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// replace with ConditionalFlow
opts[0].action();
// then
expect(sequenceFlow.businessObject.conditionExpression.$type).to.equal('bpmn:FormalExpression');
}));
it('should morph into a ConditionalFlow -> undo', inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_2');
//when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// replace with ConditionalFlow
opts[0].action();
commandStack.undo();
// then
expect(sequenceFlow.businessObject.conditionExpression).to.not.exist;
}));
it('should morph back into a SequenceFlow', inject(function(elementRegistry, bpmnReplace) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_2');
// when
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
// replace with ConditionalFlow
opts[0].action();
var conditionalOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
// replace with SequenceFlow
conditionalOpts[0].action();
// then
expect(sequenceFlow.businessObject.conditionExpression).to.not.exist;
}));
it('should replace ConditionalFlow with SequenceFlow when changing source',
inject(function(elementRegistry, bpmnReplace, modeling) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3'),
startEvent = elementRegistry.get('StartEvent_1');
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
// trigger ConditionalFlow replacement
sequenceFlowOpts[0].action();
// when
modeling.reconnectStart(sequenceFlow, startEvent, [
{ x: 196, y: 197, original: { x: 178, y: 197 } },
{ x: 497, y: 278, original: { x: 547, y: 278 } }
]);
// then
expect(sequenceFlow.businessObject.conditionExpression).to.not.exist;
}));
it('should replace ConditionalFlow with SequenceFlow when changing source -> undo',
inject(function(elementRegistry, bpmnReplace, modeling, commandStack) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3'),
startEvent = elementRegistry.get('StartEvent_1');
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
// trigger ConditionalFlow replacement
sequenceFlowOpts[0].action();
// when
modeling.reconnectStart(sequenceFlow, startEvent, [
{ x: 196, y: 197, original: { x: 178, y: 197 } },
{ x: 497, y: 278, original: { x: 547, y: 278 } }
]);
commandStack.undo();
// then
expect(sequenceFlow.businessObject.conditionExpression.$type).to.equal('bpmn:FormalExpression');
}));
it('should replace ConditionalFlow with SequenceFlow when changing target',
inject(function(elementRegistry, elementFactory, canvas, bpmnReplace, modeling) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3'),
root = canvas.getRootElement(),
intermediateEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent'});
modeling.createShape(intermediateEvent, { x: 497, y: 197 }, root);
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
// trigger ConditionalFlow replacement
sequenceFlowOpts[0].action();
// when
modeling.reconnectEnd(sequenceFlow, intermediateEvent, [
{ x: 389, y: 197, original: { x: 389, y: 197 } },
{ x: 497, y: 197, original: { x: 497, y: 197 } }
]);
// then
expect(sequenceFlow.businessObject.conditionExpression).to.not.exist;
}));
it('should replace ConditionalFlow with SequenceFlow when changing target -> undo',
inject(function(elementRegistry, elementFactory, canvas, bpmnReplace, modeling, commandStack) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3'),
root = canvas.getRootElement(),
intermediateEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent'});
modeling.createShape(intermediateEvent, { x: 497, y: 197 }, root);
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
// trigger ConditionalFlow replacement
sequenceFlowOpts[0].action();
// when
modeling.reconnectEnd(sequenceFlow, intermediateEvent, [
{ x: 389, y: 197, original: { x: 389, y: 197 } },
{ x: 497, y: 197, original: { x: 497, y: 197 } }
]);
commandStack.undo();
// then
expect(sequenceFlow.businessObject.conditionExpression.$type).to.equal('bpmn:FormalExpression');
}));
});
describe('events', function() { describe('events', function() {
var diagramXML = require('../../../fixtures/bpmn/basic.bpmn'); var diagramXML = require('../../../fixtures/bpmn/basic.bpmn');

View File

@ -1,64 +0,0 @@
'use strict';
var TestHelper = require('../../../TestHelper');
/* global bootstrapModeler, inject */
var modelingModule = require('../../../../lib/features/modeling'),
replaceModule = require('../../../../lib/features/replace'),
coreModule = require('../../../../lib/core');
describe('features/replace - chooser', function() {
var diagramXML = require('../../../fixtures/bpmn/features/replace/01_replace.bpmn');
var testModules = [ coreModule, modelingModule, replaceModule ];
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
describe('should show chooser', function() {
it('for task', inject(function(elementRegistry, modeling, bpmnReplace) {
// given
var element = elementRegistry.get('Task_1');
// when
bpmnReplace.openChooser({ x: 100, y: 100 }, element);
// then
expect(null).not.to.exist;
}));
it('for event event', inject(function(elementRegistry, modeling, bpmnReplace) {
// given
var element = elementRegistry.get('StartEvent_1');
// when
bpmnReplace.openChooser({ x: 100, y: 100 }, element);
// then
expect(null).not.to.exist;
}));
it('for gateway event', inject(function(elementRegistry, modeling, bpmnReplace) {
// given
var element = elementRegistry.get('ExclusiveGateway_1');
// when
bpmnReplace.openChooser({ x: 100, y: 100 }, element);
// then
expect(null).not.to.exist;
}));
});
});