feat(copy-paste): clone properties when morphing to new element
Closes #648
This commit is contained in:
parent
432d7f4b7c
commit
2ecb9aeae4
|
@ -4,9 +4,15 @@ var ModelUtil = require('../../util/ModelUtil'),
|
||||||
getBusinessObject = ModelUtil.getBusinessObject,
|
getBusinessObject = ModelUtil.getBusinessObject,
|
||||||
is = ModelUtil.is;
|
is = ModelUtil.is;
|
||||||
|
|
||||||
var map = require('lodash/collection/map'),
|
var ModelCloneHelper = require('../../util/model/ModelCloneHelper');
|
||||||
forEach = require('lodash/collection/forEach');
|
|
||||||
|
|
||||||
|
var ModelCloneUtils = require('../../util/model/ModelCloneUtils'),
|
||||||
|
getProperties = ModelCloneUtils.getProperties;
|
||||||
|
|
||||||
|
var IGNORED_PROPERTIES = ModelCloneUtils.IGNORED_PROPERTIES;
|
||||||
|
|
||||||
|
var filter = require('lodash/collection/filter'),
|
||||||
|
forEach = require('lodash/collection/forEach');
|
||||||
|
|
||||||
function setProperties(descriptor, data, properties) {
|
function setProperties(descriptor, data, properties) {
|
||||||
forEach(properties, function(property) {
|
forEach(properties, function(property) {
|
||||||
|
@ -26,61 +32,38 @@ function removeProperties(element, properties) {
|
||||||
|
|
||||||
function BpmnCopyPaste(bpmnFactory, eventBus, copyPaste, clipboard, moddle, canvas, bpmnRules) {
|
function BpmnCopyPaste(bpmnFactory, eventBus, copyPaste, clipboard, moddle, canvas, bpmnRules) {
|
||||||
|
|
||||||
|
var helper = new ModelCloneHelper();
|
||||||
|
|
||||||
copyPaste.registerDescriptor(function(element, descriptor) {
|
copyPaste.registerDescriptor(function(element, descriptor) {
|
||||||
var businessObject = getBusinessObject(element),
|
var businessObject = getBusinessObject(element),
|
||||||
conditionExpression,
|
newBusinessObject = bpmnFactory.create(businessObject.$type);
|
||||||
eventDefinitions;
|
|
||||||
|
var properties = getProperties(businessObject.$descriptor);
|
||||||
|
|
||||||
|
properties = filter(properties, function(property) {
|
||||||
|
return IGNORED_PROPERTIES.indexOf(property.replace(/bpmn:/, '')) === -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
descriptor.businessObject = helper.clone(businessObject, newBusinessObject, properties);
|
||||||
|
|
||||||
descriptor.type = element.type;
|
descriptor.type = element.type;
|
||||||
|
|
||||||
|
setProperties(descriptor, businessObject.di, [ 'isExpanded' ]);
|
||||||
|
|
||||||
if (element.type === 'label') {
|
if (element.type === 'label') {
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
setProperties(descriptor, businessObject, [
|
setProperties(descriptor, businessObject, [
|
||||||
'name',
|
'type',
|
||||||
'text',
|
|
||||||
'processRef',
|
'processRef',
|
||||||
'isInterrupting',
|
'triggeredByEvent'
|
||||||
'isForCompensation',
|
|
||||||
'associationDirection',
|
|
||||||
'triggeredByEvent',
|
|
||||||
'cancelActivity'
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (businessObject.default) {
|
if (businessObject.default) {
|
||||||
descriptor.default = businessObject.default.id;
|
descriptor.default = businessObject.default.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (businessObject.loopCharacteristics) {
|
|
||||||
|
|
||||||
descriptor.loopCharacteristics = {
|
|
||||||
type: businessObject.loopCharacteristics.$type,
|
|
||||||
isSequential: businessObject.loopCharacteristics.isSequential
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
setProperties(descriptor, businessObject.di, [ 'isExpanded' ]);
|
|
||||||
|
|
||||||
if (is(businessObject, 'bpmn:SequenceFlow')) {
|
|
||||||
conditionExpression = businessObject.get('conditionExpression');
|
|
||||||
|
|
||||||
if (conditionExpression) {
|
|
||||||
descriptor.conditionExpression = {
|
|
||||||
type: conditionExpression.$type,
|
|
||||||
body: conditionExpression.body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eventDefinitions = businessObject.get('eventDefinitions') || [];
|
|
||||||
|
|
||||||
if (eventDefinitions.length) {
|
|
||||||
descriptor.eventDefinitions = map(eventDefinitions, function(defs) {
|
|
||||||
return defs.$type;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return descriptor;
|
return descriptor;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -90,9 +73,6 @@ function BpmnCopyPaste(bpmnFactory, eventBus, copyPaste, clipboard, moddle, canv
|
||||||
parent = descriptor.parent,
|
parent = descriptor.parent,
|
||||||
rootElement = canvas.getRootElement(),
|
rootElement = canvas.getRootElement(),
|
||||||
businessObject,
|
businessObject,
|
||||||
newEventDefinition,
|
|
||||||
conditionExpression,
|
|
||||||
loopCharacteristics,
|
|
||||||
source,
|
source,
|
||||||
target,
|
target,
|
||||||
canConnect;
|
canConnect;
|
||||||
|
@ -132,59 +112,24 @@ function BpmnCopyPaste(bpmnFactory, eventBus, copyPaste, clipboard, moddle, canv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
descriptor.businessObject = businessObject = bpmnFactory.create(descriptor.type);
|
businessObject = descriptor.businessObject;
|
||||||
|
|
||||||
|
// remove the id or else we cannot paste multiple times
|
||||||
|
delete businessObject.id;
|
||||||
|
|
||||||
|
// assign an ID
|
||||||
|
bpmnFactory._ensureId(businessObject);
|
||||||
|
|
||||||
if (descriptor.type === 'bpmn:Participant' && descriptor.processRef) {
|
if (descriptor.type === 'bpmn:Participant' && descriptor.processRef) {
|
||||||
descriptor.processRef = businessObject.processRef = bpmnFactory.create('bpmn:Process');
|
descriptor.processRef = businessObject.processRef = bpmnFactory.create('bpmn:Process');
|
||||||
}
|
}
|
||||||
|
|
||||||
setProperties(businessObject, descriptor, [
|
setProperties(businessObject, descriptor, [
|
||||||
'name',
|
|
||||||
'text',
|
|
||||||
'isExpanded',
|
'isExpanded',
|
||||||
'isInterrupting',
|
|
||||||
'cancelActivity',
|
|
||||||
'triggeredByEvent'
|
'triggeredByEvent'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (descriptor.loopCharacteristics) {
|
|
||||||
loopCharacteristics = descriptor.loopCharacteristics;
|
|
||||||
|
|
||||||
businessObject.loopCharacteristics = moddle.create(loopCharacteristics.type);
|
|
||||||
|
|
||||||
if (loopCharacteristics.isSequential) {
|
|
||||||
businessObject.loopCharacteristics.isSequential = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
businessObject.loopCharacteristics.$parent = businessObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.conditionExpression) {
|
|
||||||
conditionExpression = descriptor.conditionExpression;
|
|
||||||
|
|
||||||
businessObject.conditionExpression = moddle.create(conditionExpression.type, { body: conditionExpression.body });
|
|
||||||
|
|
||||||
businessObject.conditionExpression.$parent = businessObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.eventDefinitions) {
|
|
||||||
businessObject.eventDefinitions = map(descriptor.eventDefinitions, function(type) {
|
|
||||||
newEventDefinition = moddle.create(type);
|
|
||||||
|
|
||||||
newEventDefinition.$parent = businessObject;
|
|
||||||
|
|
||||||
return newEventDefinition;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeProperties(descriptor, [
|
removeProperties(descriptor, [
|
||||||
'name',
|
|
||||||
'text',
|
|
||||||
'eventDefinitions',
|
|
||||||
'conditionExpression',
|
|
||||||
'loopCharacteristics',
|
|
||||||
'isInterrupting',
|
|
||||||
'cancelActivity',
|
|
||||||
'triggeredByEvent'
|
'triggeredByEvent'
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,3 +14,4 @@ TestHelper.insertCSS('diagram-js-testing.css',
|
||||||
// add suite specific matchers
|
// add suite specific matchers
|
||||||
global.chai.use(require('diagram-js/test/matchers/BoundsMatchers'));
|
global.chai.use(require('diagram-js/test/matchers/BoundsMatchers'));
|
||||||
global.chai.use(require('diagram-js/test/matchers/ConnectionMatchers'));
|
global.chai.use(require('diagram-js/test/matchers/ConnectionMatchers'));
|
||||||
|
global.chai.use(require('./matchers/JSONMatcher'));
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function(chai, utils) {
|
||||||
|
|
||||||
|
utils.addMethod(chai.Assertion.prototype, 'jsonEqual', function(comparison) {
|
||||||
|
|
||||||
|
var actual = JSON.stringify(this._obj);
|
||||||
|
var expected = JSON.stringify(comparison);
|
||||||
|
|
||||||
|
this.assert(
|
||||||
|
actual == expected,
|
||||||
|
'expected #{this} to deep equal #{act}',
|
||||||
|
'expected #{this} not to deep equal #{act}',
|
||||||
|
comparison, // expected
|
||||||
|
this._obj, // actual
|
||||||
|
true // show diff
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
|
@ -11,17 +11,20 @@ var bpmnCopyPasteModule = require('../../../../lib/features/copy-paste'),
|
||||||
coreModule = require('../../../../lib/core');
|
coreModule = require('../../../../lib/core');
|
||||||
|
|
||||||
var map = require('lodash/collection/map'),
|
var map = require('lodash/collection/map'),
|
||||||
|
filter = require('lodash/collection/filter'),
|
||||||
forEach = require('lodash/collection/forEach'),
|
forEach = require('lodash/collection/forEach'),
|
||||||
uniq = require('lodash/array/uniq');
|
uniq = require('lodash/array/uniq');
|
||||||
|
|
||||||
var DescriptorTree = require('./DescriptorTree');
|
var DescriptorTree = require('./DescriptorTree');
|
||||||
|
|
||||||
|
var is = require('../../../../lib/util/ModelUtil').is;
|
||||||
|
|
||||||
describe('features/copy-paste', function() {
|
describe('features/copy-paste', function() {
|
||||||
|
|
||||||
var testModules = [ bpmnCopyPasteModule, copyPasteModule, tooltipsModule, modelingModule, coreModule ];
|
var testModules = [ bpmnCopyPasteModule, copyPasteModule, tooltipsModule, modelingModule, coreModule ];
|
||||||
|
|
||||||
var basicXML = require('../../../fixtures/bpmn/features/copy-paste/basic.bpmn'),
|
var basicXML = require('../../../fixtures/bpmn/features/copy-paste/basic.bpmn'),
|
||||||
|
clonePropertiesXML = require('../../../fixtures/bpmn/features/replace/clone-properties.bpmn'),
|
||||||
propertiesXML = require('../../../fixtures/bpmn/features/copy-paste/properties.bpmn'),
|
propertiesXML = require('../../../fixtures/bpmn/features/copy-paste/properties.bpmn'),
|
||||||
collaborationXML = require('../../../fixtures/bpmn/features/copy-paste/collaboration.bpmn'),
|
collaborationXML = require('../../../fixtures/bpmn/features/copy-paste/collaboration.bpmn'),
|
||||||
collaborationMultipleXML = require('../../../fixtures/bpmn/features/copy-paste/collaboration-multiple.bpmn'),
|
collaborationMultipleXML = require('../../../fixtures/bpmn/features/copy-paste/collaboration-multiple.bpmn'),
|
||||||
|
@ -37,19 +40,10 @@ describe('features/copy-paste', function() {
|
||||||
|
|
||||||
it('selected elements', inject(function(elementRegistry, copyPaste) {
|
it('selected elements', inject(function(elementRegistry, copyPaste) {
|
||||||
|
|
||||||
// given
|
|
||||||
var subProcess,
|
|
||||||
startEvent,
|
|
||||||
boundaryEvent,
|
|
||||||
textAnnotation;
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
var tree = copy([ 'SubProcess_1kd6ist' ]);
|
var tree = copy([ 'SubProcess_1kd6ist' ]);
|
||||||
|
|
||||||
startEvent = tree.getElement('StartEvent_1');
|
var subProcess = tree.getElement('SubProcess_1kd6ist');
|
||||||
boundaryEvent = tree.getElement('BoundaryEvent_1c94bi9');
|
|
||||||
subProcess = tree.getElement('SubProcess_1kd6ist');
|
|
||||||
textAnnotation = tree.getElement('TextAnnotation_0h1hhgg');
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(tree.getLength()).to.equal(3);
|
expect(tree.getLength()).to.equal(3);
|
||||||
|
@ -59,9 +53,6 @@ describe('features/copy-paste', function() {
|
||||||
expect(tree.getDepthLength(2)).to.equal(15);
|
expect(tree.getDepthLength(2)).to.equal(15);
|
||||||
|
|
||||||
expect(subProcess.isExpanded).to.be.true;
|
expect(subProcess.isExpanded).to.be.true;
|
||||||
expect(startEvent.name).to.equal('hello');
|
|
||||||
expect(textAnnotation.text).to.equal('foo');
|
|
||||||
expect(boundaryEvent.eventDefinitions).to.contain('bpmn:TimerEventDefinition');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -512,6 +503,57 @@ describe('features/copy-paste', function() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('deep properties', function() {
|
||||||
|
|
||||||
|
var camundaPackage = require('../../../fixtures/json/model/camunda');
|
||||||
|
|
||||||
|
beforeEach(bootstrapModeler(clonePropertiesXML, {
|
||||||
|
modules: testModules,
|
||||||
|
moddleExtensions: {
|
||||||
|
camunda: camundaPackage
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('integration', inject(integrationTest([ 'Participant_0x9lnke' ])));
|
||||||
|
|
||||||
|
|
||||||
|
it('should copy UserTask properties',
|
||||||
|
inject(function(elementRegistry, copyPaste, canvas) {
|
||||||
|
|
||||||
|
var participant = elementRegistry.get('Participant_0x9lnke'),
|
||||||
|
task = elementRegistry.get('Task_1'),
|
||||||
|
newTask;
|
||||||
|
|
||||||
|
// when
|
||||||
|
copyPaste.copy([ task ]);
|
||||||
|
|
||||||
|
copyPaste.paste({
|
||||||
|
element: participant,
|
||||||
|
point: {
|
||||||
|
x: 500,
|
||||||
|
y: 50
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newTask = filter(participant.children, function(element) {
|
||||||
|
return is(element, 'bpmn:Task');
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
// then
|
||||||
|
var bo = task.businessObject;
|
||||||
|
var copiedBo = newTask.businessObject;
|
||||||
|
|
||||||
|
|
||||||
|
expect(copiedBo.asyncBefore).to.eql(bo.asyncBefore);
|
||||||
|
expect(copiedBo.documentation).to.jsonEqual(bo.documentation);
|
||||||
|
expect(copiedBo.extensionElements).to.jsonEqual(bo.extensionElements);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue