From 31ea6b2f91b680e871c0b1bf10eb31c309ceb5f8 Mon Sep 17 00:00:00 2001 From: pedesen Date: Fri, 3 Jul 2015 10:48:32 +0200 Subject: [PATCH] feat(modeling): add ad-hoc sub processes Closes #271 --- lib/features/replace/BpmnReplace.js | 116 +-- .../bpmn/draw/activity-markers-simple.bpmn | 4 + .../bpmn/features/replace/01_replace.bpmn | 60 +- .../spec/features/popup-menu/PopupMenuSpec.js | 720 ++++++++++-------- test/spec/features/replace/BpmnReplaceSpec.js | 132 +++- 5 files changed, 617 insertions(+), 415 deletions(-) diff --git a/lib/features/replace/BpmnReplace.js b/lib/features/replace/BpmnReplace.js index 9ce5f02d..77ba0566 100644 --- a/lib/features/replace/BpmnReplace.js +++ b/lib/features/replace/BpmnReplace.js @@ -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; diff --git a/test/fixtures/bpmn/draw/activity-markers-simple.bpmn b/test/fixtures/bpmn/draw/activity-markers-simple.bpmn index d3a75a16..444714c1 100644 --- a/test/fixtures/bpmn/draw/activity-markers-simple.bpmn +++ b/test/fixtures/bpmn/draw/activity-markers-simple.bpmn @@ -13,6 +13,7 @@ + @@ -34,6 +35,9 @@ + + + diff --git a/test/fixtures/bpmn/features/replace/01_replace.bpmn b/test/fixtures/bpmn/features/replace/01_replace.bpmn index bd8746fe..711df8ec 100644 --- a/test/fixtures/bpmn/features/replace/01_replace.bpmn +++ b/test/fixtures/bpmn/features/replace/01_replace.bpmn @@ -28,73 +28,85 @@ + + + - + - + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - + - + - + - + - + - - + + - + + + + + + + + + + diff --git a/test/spec/features/popup-menu/PopupMenuSpec.js b/test/spec/features/popup-menu/PopupMenuSpec.js index 0373448b..5b43dcd2 100644 --- a/test/spec/features/popup-menu/PopupMenuSpec.js +++ b/test/spec/features/popup-menu/PopupMenuSpec.js @@ -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)); - })); }); diff --git a/test/spec/features/replace/BpmnReplaceSpec.js b/test/spec/features/replace/BpmnReplaceSpec.js index 71264daf..d57a6622 100644 --- a/test/spec/features/replace/BpmnReplaceSpec.js +++ b/test/spec/features/replace/BpmnReplaceSpec.js @@ -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); + })); + + }); + });