From 8f190bb53c0f86f0d5a82bd9853f1006ee89138e Mon Sep 17 00:00:00 2001 From: Maciej Barelkowski Date: Fri, 13 Mar 2020 13:26:16 +0100 Subject: [PATCH] fix(modeling): save bpmndi as drawn on the diagram Related to https://github.com/camunda/camunda-modeler/issues/1326 Closes #985 --- lib/features/di-ordering/BpmnDiOrdering.js | 37 ++ lib/features/di-ordering/index.js | 8 + lib/features/modeling/index.js | 2 + .../features/ordering/BpmnDiOrderingSpec.js | 211 +++++++++ .../features/ordering/wrong-di-order.bpmn | 406 ++++++++++++++++++ 5 files changed, 664 insertions(+) create mode 100644 lib/features/di-ordering/BpmnDiOrdering.js create mode 100644 lib/features/di-ordering/index.js create mode 100644 test/spec/features/ordering/BpmnDiOrderingSpec.js create mode 100644 test/spec/features/ordering/wrong-di-order.bpmn diff --git a/lib/features/di-ordering/BpmnDiOrdering.js b/lib/features/di-ordering/BpmnDiOrdering.js new file mode 100644 index 0000000000..b419ef0bee --- /dev/null +++ b/lib/features/di-ordering/BpmnDiOrdering.js @@ -0,0 +1,37 @@ +import { getDi } from '../../draw/BpmnRenderUtil'; +import { getBusinessObject } from '../../util/ModelUtil'; + +import { + filter, + map +} from 'min-dash'; + +import { selfAndAllChildren } from 'diagram-js/lib/util/Elements'; + + +var HIGH_PRIORITY = 2000; + +export default function BpmnDiOrdering(eventBus, canvas) { + + eventBus.on('saveXML.start', HIGH_PRIORITY, orderDi); + + function orderDi() { + var root = canvas.getRootElement(), + rootDi = getBusinessObject(root).di, + elements, + diElements; + + elements = selfAndAllChildren([ root ], false); + + // only bpmndi:Shape and bpmndi:Edge can be direct children of bpmndi:Plane + elements = filter(elements, function(element) { + return element !== root && !element.labelTarget; + }); + + diElements = map(elements, getDi); + + rootDi.set('planeElement', diElements); + } +} + +BpmnDiOrdering.$inject = [ 'eventBus', 'canvas' ]; diff --git a/lib/features/di-ordering/index.js b/lib/features/di-ordering/index.js new file mode 100644 index 0000000000..52bcbd0975 --- /dev/null +++ b/lib/features/di-ordering/index.js @@ -0,0 +1,8 @@ +import BpmnDiOrdering from '../di-ordering/BpmnDiOrdering'; + +export default { + __init__: [ + 'bpmnDiOrdering' + ], + bpmnDiOrdering: [ 'type', BpmnDiOrdering ] +}; \ No newline at end of file diff --git a/lib/features/modeling/index.js b/lib/features/modeling/index.js index 6bd5d18ddc..0d5b89b40c 100644 --- a/lib/features/modeling/index.js +++ b/lib/features/modeling/index.js @@ -1,5 +1,6 @@ import BehaviorModule from './behavior'; import RulesModule from '../rules'; +import DiOrderingModule from '../di-ordering'; import OrderingModule from '../ordering'; import ReplaceModule from '../replace'; @@ -27,6 +28,7 @@ export default { __depends__: [ BehaviorModule, RulesModule, + DiOrderingModule, OrderingModule, ReplaceModule, CommandModule, diff --git a/test/spec/features/ordering/BpmnDiOrderingSpec.js b/test/spec/features/ordering/BpmnDiOrderingSpec.js new file mode 100644 index 0000000000..645791794f --- /dev/null +++ b/test/spec/features/ordering/BpmnDiOrderingSpec.js @@ -0,0 +1,211 @@ +import { + bootstrapModeler, + getBpmnJS +} from 'test/TestHelper'; + +import { + add, + attach, + connect +} from './Helper'; + +import modelingModule from 'lib/features/modeling'; +import coreModule from 'lib/core'; + +import emptyProcessXML from 'test/fixtures/bpmn/collaboration/process-empty.bpmn'; + + +describe('features/modeling - di ordering', function() { + var testModules = [coreModule, modelingModule]; + + describe('boundary events', function() { + + beforeEach(bootstrapModeler(emptyProcessXML, { modules: testModules })); + + + it('should place after tasks', function(done) { + + // when + var task1 = add({ type: 'bpmn:Task' }, { x: 100, y: 100 }), + event = attach( + { type: 'bpmn:BoundaryEvent' }, + { x: 100, y: 140 }, + task1 + ), + task2 = add({ type: 'bpmn:Task' }, { x: 300, y: 100 }); + + // then + expectDiOrder([ 'Process_1', task1.id, task2.id, event.id ], done); + }); + }); + + + describe('collaboration', function() { + + beforeEach(bootstrapModeler(emptyProcessXML, { modules: testModules })); + + + it('should place di elements in correct order', function(done) { + + // given + var canvas = getBpmnJS().get('canvas'), + root; + + // when + var participant1 = add( + { type: 'bpmn:Participant', height: 300 }, { x: 300, y: 200 }, 'Process_1' + ); + + root = canvas.getRootElement(); + var participant2 = add({ type: 'bpmn:Participant' }, { x: 300, y: 500 }), + task1 = add({ type: 'bpmn:Task' }, { x: 400, y: 150 }, participant1.id), + task2 = add({ type: 'bpmn:Task' }, { x: 250, y: 150 }, participant1.id); + + var messageFlow1 = connect(task1, participant2), + messageFlow2 = connect(participant2, participant1), + sequenceFlow = connect(task2, task1); + + // then + expectDiOrder([ + root.id, + participant1.id, + sequenceFlow.id, + task1.id, + task2.id, + participant2.id, + messageFlow1.id, + messageFlow2.id + ], done); + }); + }); + + + describe('subprocess', function() { + + beforeEach(bootstrapModeler(emptyProcessXML, { modules: testModules })); + + + it('should place di elements in correct order', function(done) { + + // given + var canvas = getBpmnJS().get('canvas'), + root; + + // when + var subProcess = add( + { type: 'bpmn:SubProcess', isExpanded: true, width: 300, height: 200 }, { x: 300, y: 200 } + ); + + var participant = add({ type: 'bpmn:Participant', width: 500, height: 300 }, { x: 300, y: 200 }), + task1 = add({ type: 'bpmn:Task' }, { x: 250, y: 200 }, subProcess.id); + + root = canvas.getRootElement(); + + // then + expectDiOrder([ + root.id, + participant.id, + subProcess.id, + task1.id, + ], done); + }); + }); + + + describe('wrong ordering in xml', function() { + var diagramXML = require('./wrong-di-order.bpmn'); + + beforeEach(bootstrapModeler(diagramXML, { modules: testModules })); + + it('should correctly order di elements on export', function(done) { + + // then + expectDiOrder( + [ + 'Page1Process', + 'SequenceFlow_13g7fzw', + 'SequenceFlow_1c22uay', + 'SequenceFlow_1fp5smb', + 'SequenceFlow_1kwx6kg', + 'SequenceFlow_1sv6jqd', + 'SequenceFlow_1ysz115', + 'SequenceFlow_0qen1he', + 'SequenceFlow_0nqtgik', + 'SequenceFlow_0zafwi9', + 'MakeBookingSubProcess', + 'SequenceFlow_3', + 'SequenceFlow_2', + 'SequenceFlow_1', + 'SequenceFlow', + 'SequenceFlow_1244t37', + 'SequenceFlow_0e0tkzl', + 'StartEvent_1', + 'ExclusiveGateway_1l6x19l', + 'BookFlightTask', + 'CancelFlightTask', + 'BookHotelTask', + 'ParalelGateway', + 'TravelBookedEndEvent', + 'CancelHotelTask', + 'HandleCompensationSubProcess', + 'SequenceFlow_0e6xitm', + 'SequenceFlow_0zpw5ma', + 'SequenceFlow_03663sw', + 'SequenceFlow_0i33vwg', + 'SequenceFlow_09qgqyw', + 'SequenceFlow_0cip1mz', + 'BookingStartEvent', + 'ParallelGateway_0vh9j6n', + 'FlightEvent', + 'HotelEvent', + 'ParallelGateway_1ycdyix', + 'EndEvent_0nr3cro', + 'FlightBoundaryEvent', + 'HotelBoundaryEvent', + 'Association_0qea76h', + 'Association_1', + 'CancelRequestEndEvent', + 'RequestCancelledEndEvent', + 'GatewayGateway', + 'OfferApprovedEvent', + 'N24HoursEvent', + 'N24HoursEvent1', + 'CancelRequestEvent', + 'MakeFlyAndHotelOfferTask', + 'RequestCreditCardInformationTask', + 'NotifyCustomerOfferExpiredTask', + 'UpdateCustomerRecordTask', + 'ReceiveTravelRequestStartEvent' + ], + done + ); + }); + }); +}); + +// helper +function expectDiOrder(expectedOrder, done) { + getBpmnJS().saveXML({ format: true }, function(error, xml) { + if (error) { + return done(error); + } + + var pattern = /bpmnElement="([^"]+)"/g, + exportedOrder = [], + match = pattern.exec(xml); + + while (match !== null) { + exportedOrder.push(match[1]); + + match = pattern.exec(xml); + } + + try { + expect(exportedOrder).to.eql(expectedOrder); + + done(); + } catch (err) { + return done(err); + } + }); +} diff --git a/test/spec/features/ordering/wrong-di-order.bpmn b/test/spec/features/ordering/wrong-di-order.bpmn new file mode 100644 index 0000000000..565bcd4cd7 --- /dev/null +++ b/test/spec/features/ordering/wrong-di-order.bpmn @@ -0,0 +1,406 @@ + + + + + SequenceFlow_13g7fzw + + SequenceFlow_0e0tkzl + + + SequenceFlow_0e0tkzl + SequenceFlow_1244t37 + SequenceFlow + + + SequenceFlow_1244t37 + SequenceFlow_1 + + + + SequenceFlow + SequenceFlow_2 + + + SequenceFlow_1 + SequenceFlow_2 + SequenceFlow_3 + + + SequenceFlow_3 + + + + + + + + + + + + + + + + + SequenceFlow_0e6xitm + + + + SequenceFlow_0e6xitm + SequenceFlow_03663sw + SequenceFlow_0zpw5ma + + + SequenceFlow_0zpw5ma + SequenceFlow_0i33vwg + + + + SequenceFlow_03663sw + SequenceFlow_09qgqyw + + + + SequenceFlow_09qgqyw + SequenceFlow_0i33vwg + SequenceFlow_0cip1mz + + + SequenceFlow_0cip1mz + + + + + + + + + + + + + + SequenceFlow_0zafwi9 + + + SequenceFlow_0nqtgik + SequenceFlow_0qen1he + SequenceFlow_1ysz115 + SequenceFlow_1sv6jqd + + + SequenceFlow_0qen1he + SequenceFlow_1kwx6kg + + + + SequenceFlow_1ysz115 + + + + + + + SequenceFlow_1sv6jqd + SequenceFlow_1fp5smb + + + + SequenceFlow_1c22uay + SequenceFlow_0nqtgik + + + SequenceFlow_1kwx6kg + SequenceFlow_13g7fzw + + + + SequenceFlow_1fp5smb + SequenceFlow_0zafwi9 + + + SequenceFlow_1c22uay + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +