diff --git a/lib/import/BpmnImporter.js b/lib/import/BpmnImporter.js index 68637e69..493e2f83 100644 --- a/lib/import/BpmnImporter.js +++ b/lib/import/BpmnImporter.js @@ -6,7 +6,8 @@ var LabelUtil = require('../util/Label'); var hasExternalLabel = LabelUtil.hasExternalLabel, getExternalLabelBounds = LabelUtil.getExternalLabelBounds, - isExpanded = require('../util/Di').isExpanded; + isExpanded = require('../util/Di').isExpanded, + elementToString = require('./Util').elementToString; function elementData(semantic, attrs) { @@ -89,10 +90,6 @@ BpmnImporter.prototype.add = function(semantic, parentElement) { var source = this._getSource(semantic), target = this._getTarget(semantic); - if (!source || !target) { - throw new Error('source or target not rendered for element <' + semantic.id + '>'); - } - element = this._elementFactory.createConnection(elementData(semantic, { source: source, target: target, @@ -101,7 +98,7 @@ BpmnImporter.prototype.add = function(semantic, parentElement) { this._canvas.addConnection(element, parentElement); } else { - throw new Error('unknown di <' + di.$type + '> for element <' + semantic.id + '>'); + throw new Error('unknown di ' + elementToString(di) + ' for element ' + elementToString(semantic)); } // (optional) LABEL @@ -118,7 +115,7 @@ BpmnImporter.prototype.add = function(semantic, parentElement) { /** * add label for an element */ -BpmnImporter.prototype.addLabel = function (semantic, element) { +BpmnImporter.prototype.addLabel = function(semantic, element) { var bounds = getExternalLabelBounds(semantic, element); var label = this._elementFactory.createLabel(elementData(semantic, { @@ -135,47 +132,53 @@ BpmnImporter.prototype.addLabel = function (semantic, element) { return this._canvas.addShape(label, element.parent); }; - -BpmnImporter.prototype._getSource = function(semantic) { +/** + * Return the drawn connection end based on the given side. + * + * @throws {Error} if the end is not yet drawn + */ +BpmnImporter.prototype._getEnd = function(semantic, side) { var element, - elementSemantic = semantic.sourceRef; + refSemantic, + refIsParent, + type = semantic.$type; + + refSemantic = semantic[side + 'Ref']; // handle mysterious isMany DataAssociation#sourceRef - if (_.isArray(elementSemantic)) { - elementSemantic = elementSemantic[0]; + if (side === 'source' && type === 'bpmn:DataInputAssociation') { + refSemantic = refSemantic && refSemantic[0]; } - if (elementSemantic && elementSemantic.$instanceOf('bpmn:DataOutput')) { - elementSemantic = elementSemantic.$parent.$parent; + // fix source / target for DataInputAssociation / DataOutputAssociation + if (side === 'source' && type === 'bpmn:DataOutputAssociation' || + side === 'target' && type === 'bpmn:DataInputAssociation') { + + refSemantic = semantic.$parent; } - element = elementSemantic && this._getElement(elementSemantic); + element = refSemantic && this._getElement(refSemantic); if (element) { return element; } - throw new Error('element <' + elementSemantic.id + '> referenced by <' + semantic.id + '> not yet drawn'); + if (refSemantic) { + throw new Error( + 'element ' + elementToString(refSemantic) + ' referenced by ' + + elementToString(semantic) + '#' + side + 'Ref not yet drawn'); + } else { + throw new Error(elementToString(semantic) + '#' + side + 'Ref not specified'); + } }; +BpmnImporter.prototype._getSource = function(semantic) { + return this._getEnd(semantic, 'source'); +}; BpmnImporter.prototype._getTarget = function(semantic) { - - var element, - elementSemantic = semantic.targetRef; - - if (elementSemantic && elementSemantic.$instanceOf('bpmn:DataInput')) { - elementSemantic = elementSemantic.$parent.$parent; - } - - element = elementSemantic && this._getElement(elementSemantic); - - if (element) { - return element; - } - - throw new Error('element <' + elementSemantic.id + '> referenced by <' + semantic.id + '> not yet drawn'); + return this._getEnd(semantic, 'target'); }; diff --git a/lib/import/BpmnTreeWalker.js b/lib/import/BpmnTreeWalker.js index c11d9542..4d5a3038 100644 --- a/lib/import/BpmnTreeWalker.js +++ b/lib/import/BpmnTreeWalker.js @@ -4,11 +4,10 @@ var _ = require('lodash'); var Refs = require('object-refs'); +var elementToString = require('./Util').elementToString; + var diRefs = new Refs({ name: 'bpmnElement', enumerable: true }, { name: 'di' }); -function elementToString(e) { - return '<' + e.$type + (e.id ? ' id="' + e.id : '') + '" />'; -} /** * Find a suitable display candidate for definitions where the DI does not @@ -61,9 +60,7 @@ function BpmnTreeWalker(handler) { function visitIfDi(element, ctx) { try { - if (element.di) { - return visit(element, ctx); - } + return element.di && visit(element, ctx); } catch (e) { logError(e.message, { element: element, error: e }); } @@ -225,7 +222,16 @@ function BpmnTreeWalker(handler) { } function handleArtifacts(artifacts, context) { - _.forEach(artifacts, contextual(handleArtifact, context)); + + _.forEach(artifacts, function(e) { + if (is(e, 'bpmn:Association')) { + deferred.push(function() { + handleArtifact(e, context); + }); + } else { + handleArtifact(e, context); + } + }); } function handleIoSpecification(ioSpecification, context) { @@ -346,7 +352,7 @@ function BpmnTreeWalker(handler) { } else { logError( 'unrecognized flowElement ' + elementToString(e) + ' in context ' + - (context ? elementToString(context) : null), + (context ? elementToString(context.businessObject) : null), { element: e, context: context }); } }); diff --git a/lib/import/Util.js b/lib/import/Util.js new file mode 100644 index 00000000..a011a36a --- /dev/null +++ b/lib/import/Util.js @@ -0,0 +1,7 @@ +module.exports.elementToString = function(e) { + if (!e) { + return ''; + } + + return '<' + e.$type + (e.id ? ' id="' + e.id : '') + '" />'; +}; \ No newline at end of file diff --git a/test/fixtures/bpmn/draw/data-objects.bpmn b/test/fixtures/bpmn/draw/data-objects.bpmn index 32c1c1c4..978839b3 100644 --- a/test/fixtures/bpmn/draw/data-objects.bpmn +++ b/test/fixtures/bpmn/draw/data-objects.bpmn @@ -1,249 +1,266 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DataInput_1 - sid-ADF95ACC-DEA6-4F6F-AF91-8F5BABB299AF - - - DataOutput_1 - sid-16646BD1-38D3-499A-95A6-42A75D8D2510 - - - - - - - - - - - - - - - - - - - - - - - - - sid-b4a08214-9dd4-40ad-8f82-4620ee384231 - sid-6c061a84-a3f2-4b69-93a5-8a3cafa27437 - - - sid-b4a589fa-6809-485d-ac79-9906084551f5 - sid-d5968987-50e5-4e42-9a3e-e8e3f6cc53bc - sid-bff296e6-46b4-4c63-9def-f68cdf80510d - - - - - - - DataObjectReference_1 - sid-b4a08214-9dd4-40ad-8f82-4620ee384231 - - - - - - Task_1 - _DataStoreReference_2 - - - - - - sid-b4a589fa-6809-485d-ac79-9906084551f5 - DataOutput_1 - - - - - - sid-d5968987-50e5-4e42-9a3e-e8e3f6cc53bc - DataObjectReference_1 - - - - - - - - - - - - - - - - - - - - - - _DataStoreReference_2 - Task_2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + DataInput_1 + sid-ADF95ACC-DEA6-4F6F-AF91-8F5BABB299AF + + + DataOutput_1 + sid-16646BD1-38D3-499A-95A6-42A75D8D2510 + + + + + + + + + + + + + + + + + + + + + + + + + sid-b4a08214-9dd4-40ad-8f82-4620ee384231 + sid-6c061a84-a3f2-4b69-93a5-8a3cafa27437 + sid-6c061a84-a3f2-4b69-93a5-8a3cafa27437 + + + sid-b4a589fa-6809-485d-ac79-9906084551f5 + sid-d5968987-50e5-4e42-9a3e-e8e3f6cc53bc + sid-bff296e6-46b4-4c63-9def-f68cdf80510d + sid-bff296e6-46b4-4c63-9def-f68cdf80510d + + + + + + + DataObjectReference_1 + sid-b4a08214-9dd4-40ad-8f82-4620ee384231 + + + sid-ADF95ACC-DEA6-4F6F-AF91-8F5BABB299AF + + + + + + _DataStoreReference_2 + + + + + + sid-b4a589fa-6809-485d-ac79-9906084551f5 + DataOutput_1 + + + + + + sid-d5968987-50e5-4e42-9a3e-e8e3f6cc53bc + DataObjectReference_1 + + + sid-16646BD1-38D3-499A-95A6-42A75D8D2510 + + + + + + + + + + + + + + + + + + + + + + _DataStoreReference_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/bpmn/draw/xor.bpmn b/test/fixtures/bpmn/draw/xor.bpmn index 56a8b9dc..8f38c24d 100644 --- a/test/fixtures/bpmn/draw/xor.bpmn +++ b/test/fixtures/bpmn/draw/xor.bpmn @@ -13,7 +13,7 @@ - + Should be blank diff --git a/test/fixtures/bpmn/import/association/compensation.bpmn b/test/fixtures/bpmn/import/association/compensation.bpmn new file mode 100644 index 00000000..098e37d8 --- /dev/null +++ b/test/fixtures/bpmn/import/association/compensation.bpmn @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/bpmn/import/association/data-association.bpmn b/test/fixtures/bpmn/import/association/data-association.bpmn new file mode 100644 index 00000000..6be73d9f --- /dev/null +++ b/test/fixtures/bpmn/import/association/data-association.bpmn @@ -0,0 +1,40 @@ + + + + + + + + DataObjectReference_1 + + + + + DataObjectReference_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/bpmn/import/association/data-input-output.bpmn b/test/fixtures/bpmn/import/association/data-input-output.bpmn new file mode 100644 index 00000000..00e29117 --- /dev/null +++ b/test/fixtures/bpmn/import/association/data-input-output.bpmn @@ -0,0 +1,41 @@ + + + + + + + + + + DataInput_1 + + + DataOutput_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/bpmn/import/association/text-annotation.bpmn b/test/fixtures/bpmn/import/association/text-annotation.bpmn new file mode 100644 index 00000000..8174dae0 --- /dev/null +++ b/test/fixtures/bpmn/import/association/text-annotation.bpmn @@ -0,0 +1,38 @@ + + + + + + + + + annotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/spec/features/context-pad/ContextPadProviderSpec.js b/test/spec/features/context-pad/ContextPadProviderSpec.js index 6a760ded..f97eb9c5 100644 --- a/test/spec/features/context-pad/ContextPadProviderSpec.js +++ b/test/spec/features/context-pad/ContextPadProviderSpec.js @@ -17,7 +17,7 @@ describe('features - context-pad', function() { beforeEach(Matchers.addDeepEquals); - var diagramXML = fs.readFileSync('test/fixtures/bpmn/complex.bpmn', 'utf-8'); + var diagramXML = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf-8'); var testModules = [ contextPadModule, bpmnModule ]; diff --git a/test/spec/import/ImporterSpec.js b/test/spec/import/ImporterSpec.js index 9d94fbab..c32b57bc 100644 --- a/test/spec/import/ImporterSpec.js +++ b/test/spec/import/ImporterSpec.js @@ -152,7 +152,6 @@ describe('import - importer', function() { done(err); }); }); - }); @@ -275,7 +274,27 @@ describe('import - importer', function() { done(); }); - }) + }); + + }); + + + describe('integration', function() { + + it('should import complex', function(done) { + + // given + var xml = fs.readFileSync('test/fixtures/bpmn/complex.bpmn', 'utf8'); + + // when + runImport(diagram, xml, function(err, warnings) { + + // then + expect(warnings.length).toBe(0); + + done(err); + }); + }); }); diff --git a/test/spec/import/elements/AssociationSpec.js b/test/spec/import/elements/AssociationSpec.js new file mode 100644 index 00000000..143ba992 --- /dev/null +++ b/test/spec/import/elements/AssociationSpec.js @@ -0,0 +1,130 @@ +'use strict'; + +var TestHelper = require('../../../TestHelper'); + +/* global bootstrapViewer, inject */ + + +var _ = require('lodash'); + +var fs = require('fs'); + + +describe('import - associations', function() { + + describe('should import association', function() { + + it('connecting task -> text annotation', function(done) { + + var xml = fs.readFileSync('test/fixtures/bpmn/import/association/text-annotation.bpmn', 'utf8'); + + // given + bootstrapViewer(xml)(function(err) { + + if (err) { + return done(err); + } + + // when + inject(function(elementRegistry) { + + var association = elementRegistry.getById('Association_1'); + + // then + expect(association).toBeDefined(); + + done(); + })(); + + }); + }); + + + it('connecting boundary -> compensate task', function(done) { + + var xml = fs.readFileSync('test/fixtures/bpmn/import/association/compensation.bpmn', 'utf8'); + + // given + bootstrapViewer(xml)(function(err) { + + if (err) { + return done(err); + } + + // when + inject(function(elementRegistry) { + + var association = elementRegistry.getById('Association_1'); + + // then + expect(association).toBeDefined(); + + done(); + })(); + + }); + }); + + }); + + + describe('should import data association', function() { + + it('task -> data object -> task', function(done) { + + var xml = fs.readFileSync('test/fixtures/bpmn/import/association/data-association.bpmn', 'utf8'); + + // given + bootstrapViewer(xml)(function(err) { + + if (err) { + return done(err); + } + + // when + inject(function(elementRegistry) { + + var dataInputAssociation = elementRegistry.getById('DataInputAssociation_1'); + var dataOutputAssociation = elementRegistry.getById('DataOutputAssociation_1'); + + // then + expect(dataInputAssociation).toBeDefined(); + expect(dataOutputAssociation).toBeDefined(); + + done(); + })(); + + }); + }); + + + it('data input -> task -> data output', function(done) { + + var xml = fs.readFileSync('test/fixtures/bpmn/import/association/data-input-output.bpmn', 'utf8'); + + // given + bootstrapViewer(xml)(function(err) { + + if (err) { + return done(err); + } + + // when + inject(function(elementRegistry) { + + var dataInputAssociation = elementRegistry.getById('DataInputAssociation_1'); + var dataOutputAssociation = elementRegistry.getById('DataOutputAssociation_1'); + + // then + expect(dataInputAssociation).toBeDefined(); + expect(dataOutputAssociation).toBeDefined(); + + done(); + })(); + + }); + }); + + }); + +}); \ No newline at end of file diff --git a/test/spec/import/elements/CollapsedImportSpec.js b/test/spec/import/elements/CollapsedSpec.js similarity index 100% rename from test/spec/import/elements/CollapsedImportSpec.js rename to test/spec/import/elements/CollapsedSpec.js diff --git a/test/spec/import/elements/LabelImportSpec.js b/test/spec/import/elements/LabelSpec.js similarity index 100% rename from test/spec/import/elements/LabelImportSpec.js rename to test/spec/import/elements/LabelSpec.js