From 12a38da9c704f6cb07dd20545eceb4e18d10e3ad Mon Sep 17 00:00:00 2001 From: Philipp Fromme Date: Mon, 23 Apr 2018 08:52:35 +0200 Subject: [PATCH] feat(import): expose additional BpmnTreeWalker functionality * expose API needed for lazy sub-process imports * also changes #handleDeferred to NOT expect deferred as a parameter anymore Related to bpmn-io/bpmn-js-signavio-compat#1 --- lib/import/BpmnTreeWalker.js | 8 +- test/spec/import/BpmnTreeWalkerSpec.js | 176 +++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 test/spec/import/BpmnTreeWalkerSpec.js diff --git a/lib/import/BpmnTreeWalker.js b/lib/import/BpmnTreeWalker.js index b001bd7c..db8210d7 100644 --- a/lib/import/BpmnTreeWalker.js +++ b/lib/import/BpmnTreeWalker.js @@ -10,6 +10,7 @@ import { elementToString } from './Util'; +// TODO: should be configurable: true as well which would allow to remove binding var diRefs = new Refs({ name: 'bpmnElement', enumerable: true }, { name: 'di' }); /** @@ -230,7 +231,7 @@ export default function BpmnTreeWalker(handler, translate) { handleDeferred(deferred); } - function handleDeferred(deferred) { + function handleDeferred() { var fn; @@ -453,6 +454,9 @@ export default function BpmnTreeWalker(handler, translate) { // API ////////////////////// return { - handleDefinitions: handleDefinitions + handleDeferred: handleDeferred, + handleDefinitions: handleDefinitions, + handleSubProcess: handleSubProcess, + registerDi: registerDi }; } \ No newline at end of file diff --git a/test/spec/import/BpmnTreeWalkerSpec.js b/test/spec/import/BpmnTreeWalkerSpec.js new file mode 100644 index 00000000..6fcde912 --- /dev/null +++ b/test/spec/import/BpmnTreeWalkerSpec.js @@ -0,0 +1,176 @@ +/* global sinon */ + +import BpmnTreeWalker from 'lib/import/BpmnTreeWalker'; + +import BpmnModdle from 'bpmn-moddle'; + +import { find } from 'min-dash'; + +import simpleXML from 'test/fixtures/bpmn/simple.bpmn'; + + +describe('import - BpmnTreeWalker', function() { + + it('should expose functions', function() { + + // when + const walker = createWalker(); + + // then + expect(walker.handleDeferred).to.exist; + expect(walker.handleDefinitions).to.exist; + expect(walker.handleSubProcess).to.exist; + expect(walker.registerDi).to.exist; + }); + + + it('should walk bpmn:Definitions', function(done) { + + // given + const elementSpy = sinon.spy(), + rootSpy = sinon.spy(), + errorSpy = sinon.spy(); + + const walker = createWalker({ + element: elementSpy, + root: rootSpy, + error: errorSpy + }); + + createModdle(simpleXML, (err, definitions, context, moddle) => { + + // when + walker.handleDefinitions(definitions); + + // then + expect(elementSpy.callCount).to.equal(8); + expect(rootSpy.calledOnce).to.be.true; + expect(errorSpy.notCalled).to.be.true; + + done(); + }); + }); + + + it('should walk bpmn:SubProcess', function(done) { + + // given + const elementSpy = sinon.spy(), + rootSpy = sinon.spy(), + errorSpy = sinon.spy(); + + const walker = createWalker({ + element: elementSpy, + root: rootSpy, + error: errorSpy + }); + + createModdle(simpleXML, (err, definitions, context, moddle) => { + const subProcess = findElementWithId(definitions, 'SubProcess_1'); + + const plane = definitions.diagrams[0].plane, + planeElements = plane.planeElement; + + // register DI + planeElements.forEach(walker.registerDi); + + // when + walker.handleSubProcess(subProcess); + + walker.handleDeferred(); + + // then + expect(elementSpy.callCount).to.equal(3); + expect(rootSpy.notCalled).to.be.true; + expect(errorSpy.notCalled).to.be.true; + + done(); + }); + }); + + + it('should error', function(done) { + + // given + const elementSpy = sinon.spy(), + rootSpy = sinon.spy(), + errorSpy = sinon.spy(); + + const walker = createWalker({ + element: elementSpy, + root: rootSpy, + error: errorSpy + }); + + createModdle(simpleXML, (err, definitions, context, moddle) => { + + const element = findElementWithId(definitions, 'SubProcess_1'); + + // will error + element.di = 'DI'; + + // when + walker.handleDefinitions(definitions); + + // then + expect(elementSpy.callCount).to.equal(8); + expect(rootSpy.calledOnce).to.be.true; + expect(errorSpy.calledOnce).to.be.true; + + done(); + }); + }); + +}); + + +// helpers ////////// + +function createModdle(xml, done) { + const moddle = new BpmnModdle(); + + moddle.fromXML(xml, 'bpmn:Definitions', (err, definitions, context) => { + done(err, definitions, context, moddle); + }); +} + +function createWalker(listeners = {}) { + const visitor = { + element(element, parent) { + listeners.element && listeners.element(element, parent); + }, + root(root) { + listeners.root && listeners.root(root); + }, + error(message, context) { + listeners.error && listeners.error(message, context); + } + }; + + return new BpmnTreeWalker(visitor, function() {}); +} + +function findElementWithId(definitions, id) { + + function findElement(element) { + if (element.id === id) { + return element; + } + + if (element.flowElements) { + return find(element.flowElements, flowElement => { + const foundElement = findElement(flowElement); + + return foundElement && foundElement.id === id; + }); + } + } + + return definitions.rootElements.reduce((foundElement, rootElement) => { + if (rootElement.id === id) { + return rootElement; + } else { + return findElement(rootElement) || foundElement; + } + }, null); +} \ No newline at end of file