feat(modeling/layout): improve sequence flow layouting behind Gateways
Closes bpmn-io/bpmn-js#227
This commit is contained in:
parent
359e0e01f4
commit
8e4f480868
|
@ -5,9 +5,13 @@ var inherits = require('inherits');
|
|||
var assign = require('lodash/object/assign');
|
||||
|
||||
var BaseLayouter = require('diagram-js/lib/layout/BaseLayouter'),
|
||||
LayoutUtil = require('diagram-js/lib/layout/LayoutUtil'),
|
||||
ManhattanLayout = require('diagram-js/lib/layout/ManhattanLayout');
|
||||
|
||||
var LayoutUtil = require('diagram-js/lib/layout/LayoutUtil');
|
||||
|
||||
var getMid = LayoutUtil.getMid,
|
||||
getOrientation = LayoutUtil.getOrientation;
|
||||
|
||||
var is = require('../../util/ModelUtil').is;
|
||||
|
||||
|
||||
|
@ -18,48 +22,108 @@ inherits(BpmnLayouter, BaseLayouter);
|
|||
module.exports = BpmnLayouter;
|
||||
|
||||
|
||||
function getAttachment(waypoints, idx, shape) {
|
||||
var point = waypoints && waypoints[idx];
|
||||
|
||||
return point ? (point.original || point) : LayoutUtil.getMidPoint(shape);
|
||||
}
|
||||
|
||||
|
||||
BpmnLayouter.prototype.layoutConnection = function(connection, hints) {
|
||||
BpmnLayouter.prototype.layoutConnection = function(connection, layoutHints) {
|
||||
var source = connection.source,
|
||||
target = connection.target,
|
||||
waypoints = connection.waypoints,
|
||||
start,
|
||||
end;
|
||||
|
||||
var layoutManhattan,
|
||||
var manhattanOptions,
|
||||
updatedWaypoints;
|
||||
|
||||
start = getAttachment(waypoints, 0, source);
|
||||
end = getAttachment(waypoints, waypoints && waypoints.length - 1, target);
|
||||
start = getConnectionDocking(waypoints, 0, source);
|
||||
end = getConnectionDocking(waypoints, waypoints && waypoints.length - 1, target);
|
||||
|
||||
// TODO (nre): support vertical modeling
|
||||
// and invert preferredLayouts accordingly
|
||||
|
||||
|
||||
// manhattan layout sequence / message flows
|
||||
if (is(connection, 'bpmn:MessageFlow')) {
|
||||
layoutManhattan = {
|
||||
preferStraight: true,
|
||||
preferVertical: true
|
||||
manhattanOptions = {
|
||||
preferredLayouts: [ 'straight', 'v:v' ]
|
||||
};
|
||||
}
|
||||
} else
|
||||
|
||||
|
||||
// layout all connection between flow elements h:h,
|
||||
//
|
||||
// except for
|
||||
//
|
||||
// (1) outgoing of BoundaryEvents -> layout h:v or v:h based on attach orientation
|
||||
// (2) incoming / outgoing of Gateway -> v:h (outgoing), h:v (incoming)
|
||||
//
|
||||
if (is(connection, 'bpmn:SequenceFlow')) {
|
||||
layoutManhattan = {};
|
||||
|
||||
// make sure boundary event connections do
|
||||
// not look ugly =:>
|
||||
if (is(source, 'bpmn:BoundaryEvent')) {
|
||||
|
||||
var orientation = getAttachOrientation(source);
|
||||
|
||||
if (/left|right/.test(orientation)) {
|
||||
manhattanOptions = {
|
||||
preferredLayouts: [ 'h:v' ]
|
||||
};
|
||||
} else
|
||||
|
||||
if (/top|bottom/.test(orientation)) {
|
||||
manhattanOptions = {
|
||||
preferredLayouts: [ 'v:h' ]
|
||||
};
|
||||
}
|
||||
} else
|
||||
|
||||
if (is(source, 'bpmn:Gateway')) {
|
||||
|
||||
manhattanOptions = {
|
||||
preferredLayouts: [ 'v:h' ]
|
||||
};
|
||||
} else
|
||||
|
||||
if (is(target, 'bpmn:Gateway')) {
|
||||
|
||||
manhattanOptions = {
|
||||
preferredLayouts: [ 'h:v' ]
|
||||
};
|
||||
}
|
||||
|
||||
// apply horizontal love <3
|
||||
else {
|
||||
manhattanOptions = {
|
||||
preferredLayouts: [ 'h:h' ]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (layoutManhattan) {
|
||||
if (manhattanOptions) {
|
||||
|
||||
layoutManhattan = assign(layoutManhattan, hints);
|
||||
manhattanOptions = assign(manhattanOptions, layoutHints);
|
||||
|
||||
updatedWaypoints =
|
||||
ManhattanLayout.repairConnection(
|
||||
source, target, start, end,
|
||||
source, target,
|
||||
start, end,
|
||||
waypoints,
|
||||
layoutManhattan);
|
||||
manhattanOptions);
|
||||
}
|
||||
|
||||
return updatedWaypoints || [ start, end ];
|
||||
};
|
||||
|
||||
|
||||
function getAttachOrientation(attachedElement) {
|
||||
|
||||
var hostElement = attachedElement.host,
|
||||
padding = -10;
|
||||
|
||||
return getOrientation(getMid(attachedElement), hostElement, padding);
|
||||
}
|
||||
|
||||
|
||||
function getConnectionDocking(waypoints, idx, shape) {
|
||||
var point = waypoints && waypoints[idx];
|
||||
|
||||
return point ? (point.original || point) : getMid(shape);
|
||||
}
|
||||
|
|
|
@ -41,22 +41,16 @@ describe('features/modeling - move shape', function() {
|
|||
expect(startEvent.di.bounds.x).to.equal(oldPosition.x);
|
||||
expect(startEvent.di.bounds.y).to.equal(oldPosition.y + 50);
|
||||
|
||||
// expect flow layout
|
||||
expect(sequenceFlowElement.waypoints).to.eql([
|
||||
{ original: { x: 388, y: 310 }, x: 388, y: 310 },
|
||||
{ x: 404, y: 310 },
|
||||
{ x: 404, y: 260 },
|
||||
{ original: { x: 420, y: 260 }, x: 420, y: 260 }
|
||||
]);
|
||||
var newWaypoints = sequenceFlowElement.waypoints;
|
||||
|
||||
var diWaypoints = bpmnFactory.createDiWaypoints([
|
||||
{x: 388, y: 310 },
|
||||
{x: 404, y: 310 },
|
||||
{x: 404, y: 260 },
|
||||
{x: 420, y: 260 }
|
||||
]);
|
||||
var expectedDiWaypoints = bpmnFactory.createDiWaypoints(newWaypoints.map(function(p) {
|
||||
return { x: p.x, y: p.y };
|
||||
}));
|
||||
|
||||
expect(sequenceFlow.di.waypoint).to.eql(diWaypoints);
|
||||
// see LayoutSpec for actual connection layouting tests
|
||||
|
||||
// expect di waypoints update
|
||||
expect(sequenceFlow.di.waypoint).to.eql(expectedDiWaypoints);
|
||||
}));
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
var TestHelper = require('../../../../TestHelper');
|
||||
|
||||
|
||||
function connect(source, target, attrs) {
|
||||
|
||||
var elementRegistry = TestHelper.getBpmnJS().get('elementRegistry'),
|
||||
modeling = TestHelper.getBpmnJS().get('modeling');
|
||||
|
||||
var sourceElement = typeof source === 'string' ? elementRegistry.get(source) : source;
|
||||
var targetElement = typeof target === 'string' ? elementRegistry.get(target) : target;
|
||||
|
||||
// assume
|
||||
expect(sourceElement).to.exist;
|
||||
expect(targetElement).to.exist;
|
||||
|
||||
return modeling.connect(sourceElement, targetElement, attrs);
|
||||
}
|
||||
|
||||
|
||||
function reconnectEnd(connection, target, docking) {
|
||||
|
||||
var elementRegistry = TestHelper.getBpmnJS().get('elementRegistry'),
|
||||
modeling = TestHelper.getBpmnJS().get('modeling');
|
||||
|
||||
var connectionElement = typeof connection === 'string' ? elementRegistry.get(connection) : connection;
|
||||
var targetElement = typeof target === 'string' ? elementRegistry.get(target) : target;
|
||||
|
||||
// assume
|
||||
expect(connectionElement).to.exist;
|
||||
expect(targetElement).to.exist;
|
||||
|
||||
return modeling.reconnectEnd(connectionElement, targetElement, docking);
|
||||
}
|
||||
|
||||
function element(id) {
|
||||
return TestHelper.getBpmnJS().get('elementRegistry').get(id);
|
||||
}
|
||||
|
||||
function move(shape, delta) {
|
||||
|
||||
var elementRegistry = TestHelper.getBpmnJS().get('elementRegistry'),
|
||||
modeling = TestHelper.getBpmnJS().get('modeling');
|
||||
|
||||
var shapeElement = typeof shape === 'string' ? elementRegistry.get(shape) : shape;
|
||||
|
||||
// assume
|
||||
expect(shapeElement).to.exist;
|
||||
|
||||
modeling.moveShapes([shapeElement], delta);
|
||||
|
||||
return shapeElement;
|
||||
}
|
||||
|
||||
|
||||
// API
|
||||
|
||||
module.exports.connect = connect;
|
||||
module.exports.reconnectEnd = reconnectEnd;
|
||||
module.exports.element = element;
|
||||
module.exports.move = move;
|
||||
|
||||
// debugging
|
||||
module.exports.inspect = function(element) {
|
||||
console.log(JSON.stringify(element));
|
||||
};
|
|
@ -1,17 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
var TestHelper = require('../../../TestHelper');
|
||||
var TestHelper = require('../../../../TestHelper');
|
||||
|
||||
/* global bootstrapModeler, inject */
|
||||
|
||||
|
||||
var modelingModule = require('../../../../lib/features/modeling'),
|
||||
coreModule = require('../../../../lib/core');
|
||||
var modelingModule = require('../../../../../lib/features/modeling'),
|
||||
coreModule = require('../../../../../lib/core');
|
||||
|
||||
|
||||
describe('features/modeling - layout connection', function() {
|
||||
|
||||
var diagramXML = require('../../../fixtures/bpmn/sequence-flows.bpmn');
|
||||
var diagramXML = require('../../../../fixtures/bpmn/sequence-flows.bpmn');
|
||||
|
||||
var testModules = [ coreModule, modelingModule ];
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
var TestHelper = require('../../../TestHelper');
|
||||
var TestHelper = require('../../../../TestHelper');
|
||||
|
||||
/* global bootstrapModeler, inject */
|
||||
|
||||
|
||||
var modelingModule = require('../../../../lib/features/modeling'),
|
||||
coreModule = require('../../../../lib/core');
|
||||
var modelingModule = require('../../../../../lib/features/modeling'),
|
||||
coreModule = require('../../../../../lib/core');
|
||||
|
||||
|
||||
describe('features/modeling - layout message flows', function() {
|
||||
|
||||
var diagramXML = require('../../../fixtures/bpmn/collaboration-message-flows.bpmn');
|
||||
var diagramXML = require('../../../../fixtures/bpmn/collaboration-message-flows.bpmn');
|
||||
|
||||
var testModules = [ coreModule, modelingModule ];
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
|
||||
<bpmn:process id="Process_1" isExecutable="false">
|
||||
<bpmn:subProcess id="SubProcess" />
|
||||
<bpmn:boundaryEvent id="BoundaryEvent_D" name="D" attachedToRef="SubProcess" />
|
||||
<bpmn:boundaryEvent id="BoundaryEvent_C" name="C" attachedToRef="SubProcess" />
|
||||
<bpmn:boundaryEvent id="BoundaryEvent_B" name="B" attachedToRef="SubProcess" />
|
||||
<bpmn:boundaryEvent id="BoundaryEvent_A" name="A" attachedToRef="SubProcess" />
|
||||
<bpmn:task id="Task_1" name="1" />
|
||||
<bpmn:task id="Task_2" name="2" />
|
||||
<bpmn:task id="Task_3" name="3" />
|
||||
<bpmn:task id="Task_4" name="4" />
|
||||
<bpmn:task id="Task_5" name="5" />
|
||||
<bpmn:task id="Task_6" name="6" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
|
||||
<bpmndi:BPMNShape id="SubProcess_di" bpmnElement="SubProcess" isExpanded="true">
|
||||
<dc:Bounds x="505" y="258" width="350" height="200" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="BoundaryEvent_D_di" bpmnElement="BoundaryEvent_D">
|
||||
<dc:Bounds x="797" y="440" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="744" y="424" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="BoundaryEvent_C_di" bpmnElement="BoundaryEvent_C">
|
||||
<dc:Bounds x="837" y="275" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="775" y="284" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="BoundaryEvent_B_di" bpmnElement="BoundaryEvent_B">
|
||||
<dc:Bounds x="568" y="240" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="540" y="280" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="BoundaryEvent_A_di" bpmnElement="BoundaryEvent_A">
|
||||
<dc:Bounds x="487" y="399" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="495" y="407" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_1_di" bpmnElement="Task_1">
|
||||
<dc:Bounds x="287" y="354" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_2_di" bpmnElement="Task_2">
|
||||
<dc:Bounds x="362" y="503" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_3_di" bpmnElement="Task_3">
|
||||
<dc:Bounds x="378" y="122" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_4_di" bpmnElement="Task_4">
|
||||
<dc:Bounds x="536" y="81" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_5_di" bpmnElement="Task_5">
|
||||
<dc:Bounds x="966" y="175" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_6_di" bpmnElement="Task_6">
|
||||
<dc:Bounds x="991" y="443" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
|
||||
<bpmn:process id="Process_1" isExecutable="false">
|
||||
<bpmn:startEvent id="StartEvent_1" />
|
||||
<bpmn:exclusiveGateway id="ExclusiveGateway_1" />
|
||||
<bpmn:task id="Task_1">
|
||||
<bpmn:outgoing>SequenceFlow_1</bpmn:outgoing>
|
||||
</bpmn:task>
|
||||
<bpmn:intermediateThrowEvent id="IntermediateThrowEvent_1" />
|
||||
<bpmn:serviceTask id="ServiceTask_1">
|
||||
<bpmn:incoming>SequenceFlow_1</bpmn:incoming>
|
||||
</bpmn:serviceTask>
|
||||
<bpmn:parallelGateway id="ParallelGateway_1" />
|
||||
<bpmn:endEvent id="EndEvent_1" />
|
||||
<bpmn:businessRuleTask id="BusinessRuleTask_1" />
|
||||
<bpmn:sequenceFlow id="SequenceFlow_1" sourceRef="Task_1" targetRef="ServiceTask_1" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
|
||||
<bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="152" y="284" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="125" y="320" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="ExclusiveGateway_1_di" bpmnElement="ExclusiveGateway_1" isMarkerVisible="true">
|
||||
<dc:Bounds x="653" y="277" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="633" y="327" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_1_di" bpmnElement="Task_1">
|
||||
<dc:Bounds x="282" y="180" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="IntermediateThrowEvent_1_di" bpmnElement="IntermediateThrowEvent_1">
|
||||
<dc:Bounds x="478" y="284" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="451" y="320" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="ServiceTask_1_di" bpmnElement="ServiceTask_1">
|
||||
<dc:Bounds x="628" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="ParallelGateway_1_di" bpmnElement="ParallelGateway_1" isMarkerVisible="true">
|
||||
<dc:Bounds x="980" y="277" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="960" y="327" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="EndEvent_1_di" bpmnElement="EndEvent_1">
|
||||
<dc:Bounds x="478" y="99" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="451" y="135" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="BusinessRuleTask_1_di" bpmnElement="BusinessRuleTask_1">
|
||||
<dc:Bounds x="790" y="180" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_1_di" bpmnElement="SequenceFlow_1">
|
||||
<di:waypoint xsi:type="dc:Point" x="382" y="241" />
|
||||
<di:waypoint xsi:type="dc:Point" x="559" y="241" />
|
||||
<di:waypoint xsi:type="dc:Point" x="559" y="138" />
|
||||
<di:waypoint xsi:type="dc:Point" x="628" y="138" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="460" y="158.5" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
|
@ -0,0 +1,326 @@
|
|||
'use strict';
|
||||
|
||||
var Helper = require('./Helper');
|
||||
|
||||
var connect = Helper.connect,
|
||||
element = Helper.element,
|
||||
move = Helper.move,
|
||||
reconnectEnd = Helper.reconnectEnd;
|
||||
|
||||
var Modeler = require('../../../../../lib/Modeler');
|
||||
|
||||
|
||||
/* global bootstrapModeler, inject */
|
||||
|
||||
var appendElement = require('../../../../util/ModelingUtil').appendElement,
|
||||
moveElements = require('../../../../util/ModelingUtil').moveElements,
|
||||
getElement = require('../../../../util/ModelingUtil').getElement;
|
||||
|
||||
|
||||
var modelingModule = require('../../../../../lib/features/modeling'),
|
||||
coreModule = require('../../../../../lib/core');
|
||||
|
||||
|
||||
describe('features/modeling - layout', function() {
|
||||
|
||||
|
||||
describe.skip('overall experience, flow elements', function() {
|
||||
|
||||
var diagramXML = require('./LayoutSequenceFlowSpec.flowElements.bpmn');
|
||||
|
||||
beforeEach(bootstrapModeler(diagramXML, { modules: Modeler.prototype._modules }));
|
||||
|
||||
|
||||
it('should feel awesome', inject(function() { }));
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe.skip('overall experience, boundary events', function() {
|
||||
|
||||
var diagramXML = require('./LayoutSequenceFlowSpec.boundaryEvents.bpmn');
|
||||
|
||||
beforeEach(bootstrapModeler(diagramXML, { modules: Modeler.prototype._modules }));
|
||||
|
||||
|
||||
it('should feel awesome', inject(function() { }));
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('flow elements', function() {
|
||||
|
||||
var diagramXML = require('./LayoutSequenceFlowSpec.flowElements.bpmn');
|
||||
|
||||
var testModules = [ coreModule, modelingModule ];
|
||||
|
||||
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
|
||||
|
||||
|
||||
describe('gateway layout', function() {
|
||||
|
||||
it('should layout v:h after Gateway', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('ExclusiveGateway_1', 'BusinessRuleTask_1');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":678,"y":302},"x":678,"y":277},
|
||||
{"x":678,"y":220},
|
||||
{"original":{"x":840,"y":220},"x":790,"y":220}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should layout h:v before Gateway', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('BusinessRuleTask_1', 'ParallelGateway_1');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":840,"y":220},"x":890,"y":220},
|
||||
{"x":1005,"y":220},
|
||||
{"original":{"x":1005,"y":302},"x":1005,"y":277}
|
||||
]);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('other elements layout', function() {
|
||||
|
||||
it('should layout h:h after StartEvent', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('StartEvent_1', 'Task_1');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":170,"y":302},"x":188,"y":302},
|
||||
{"x":235,"y":302},
|
||||
{"x":235,"y":220},
|
||||
{"original":{"x":332,"y":220},"x":282,"y":220}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should layout h:h after Task', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('ServiceTask_1', 'BusinessRuleTask_1');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":678,"y":117},"x":728,"y":117},
|
||||
{"x":759,"y":117},
|
||||
{"x":759,"y":220},
|
||||
{"original":{"x":840,"y":220},"x":790,"y":220}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should layout h:h after IntermediateEvent', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('IntermediateThrowEvent_1', 'ServiceTask_1');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":496,"y":302},"x":514,"y":302},
|
||||
{"x":571,"y":302},
|
||||
{"x":571,"y":117},
|
||||
{"original":{"x":678,"y":117},"x":628,"y":117}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should layout h:h after IntermediateEvent (right to left)', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('IntermediateThrowEvent_1', 'Task_1');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":496,"y":302},"x":478,"y":302},
|
||||
{"x":430,"y":302},
|
||||
{"x":430,"y":220},
|
||||
{"original":{"x":332,"y":220},"x":382,"y":220}
|
||||
]);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('relayout', function() {
|
||||
|
||||
it('should not repair after reconnect end', inject(function() {
|
||||
|
||||
// given
|
||||
var newDocking = { x: 660, y: 280 };
|
||||
var connection = element('SequenceFlow_1');
|
||||
|
||||
// when
|
||||
reconnectEnd(connection, 'ExclusiveGateway_1', newDocking);
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":382,"y":241},"x":382,"y":241},
|
||||
{"x":559,"y":241},
|
||||
{"x":559,"y":138},
|
||||
{"original":{"x":660,"y":280},"x":660,"y":280}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should repair after target move', inject(function() {
|
||||
|
||||
// given
|
||||
var delta = { x: -30, y: 20 };
|
||||
var connection = element('SequenceFlow_1');
|
||||
|
||||
// when
|
||||
move('ServiceTask_1', delta);
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([{"original":{"x":382,"y":241},"x":382,"y":241},
|
||||
{"x":559,"y":241},
|
||||
{"x":559,"y":158},
|
||||
{"original":{"x":598,"y":158},"x":598,"y":158}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should repair after source move', inject(function() {
|
||||
|
||||
// given
|
||||
var delta = { x: -30, y: 20 };
|
||||
var connection = element('SequenceFlow_1');
|
||||
|
||||
// when
|
||||
move('Task_1', delta);
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":352,"y":261},"x":352,"y":261},
|
||||
{"x":559,"y":261},
|
||||
{"x":559,"y":138},
|
||||
{"original":{"x":628,"y":138},"x":628,"y":138}
|
||||
]);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('boundary events', function() {
|
||||
|
||||
var diagramXML = require('./LayoutSequenceFlowSpec.boundaryEvents.bpmn');
|
||||
|
||||
var testModules = [ coreModule, modelingModule ];
|
||||
|
||||
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
|
||||
|
||||
|
||||
it('should layout h:h connecting BoundaryEvent -> left Task', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('BoundaryEvent_A', 'Task_1');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":505,"y":417},"x":487,"y":417},
|
||||
{"x":437,"y":417},
|
||||
{"x":437,"y":394},
|
||||
{"original":{"x":337,"y":394},"x":387,"y":394}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should layout h:v connecting BoundaryEvent -> bottom-left Task', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('BoundaryEvent_A', 'Task_2');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":505,"y":417},"x":487,"y":417},
|
||||
{"x":412,"y":417},
|
||||
{"original":{"x":412,"y":543},"x":412,"y":503}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should layout h:v connecting BoundaryEvent -> top-right Task', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('BoundaryEvent_A', 'Task_5');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":505,"y":417},"x":523,"y":417},
|
||||
{"x":1016,"y":417},
|
||||
{"original":{"x":1016,"y":215},"x":1016,"y":255}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should layout v:v connecting BoundaryEvent -> top Task', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('BoundaryEvent_B', 'Task_4');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":586,"y":258},"x":586,"y":240},
|
||||
{"original":{"x":586,"y":121},"x":586,"y":161}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should layout v:h connecting BoundaryEvent -> top-left Task', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('BoundaryEvent_B', 'Task_3');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":586,"y":258},"x":586,"y":258},
|
||||
{"x":586,"y":162},
|
||||
{"original":{"x":428,"y":162},"x":478,"y":162}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should layout h:v connecting BoundaryEvent -> bottom-right Task', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('BoundaryEvent_C', 'Task_6');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":855,"y":293},"x":873,"y":293},
|
||||
{"x":1041,"y":293},
|
||||
{"original":{"x":1041,"y":483},"x":1041,"y":443}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should layout v:h connecting BoundaryEvent -> bottom-left Task', inject(function() {
|
||||
|
||||
// when
|
||||
var connection = connect('BoundaryEvent_D', 'Task_2');
|
||||
|
||||
// then
|
||||
expect(connection.waypoints).to.eql([
|
||||
{"original":{"x":815,"y":458},"x":815,"y":476},
|
||||
{"x":815,"y":543},
|
||||
{"original":{"x":412,"y":543},"x":462,"y":543}
|
||||
]);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,118 @@
|
|||
'use strict';
|
||||
|
||||
var TestHelper = require('../helper');
|
||||
|
||||
var getCenter = require('diagram-js/lib/layout/LayoutUtil').getCenter;
|
||||
|
||||
var isArray = require('lodash/lang/isArray'),
|
||||
map = require('lodash/collection/map'),
|
||||
pick = require('lodash/object/pick'),
|
||||
assign = require('lodash/object/assign');
|
||||
|
||||
|
||||
function invoke(fn) {
|
||||
return TestHelper.getBpmnJS().invoke(fn);
|
||||
}
|
||||
|
||||
function normalizeDelta(delta) {
|
||||
return assign({ x: 0, y: 0 }, delta);
|
||||
}
|
||||
|
||||
|
||||
module.exports.getRelativeCenter = function(element, delta) {
|
||||
|
||||
var normalizedDelta = normalizeDelta(delta);
|
||||
|
||||
var center = getCenter(element);
|
||||
|
||||
return {
|
||||
x: center.x + normalizedDelta.x,
|
||||
y: center.y + normalizedDelta.y
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
function getElement(id) {
|
||||
|
||||
// assume
|
||||
expect(id).to.exist;
|
||||
|
||||
return invoke(function(elementRegistry) {
|
||||
return elementRegistry.get(id.id || id);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.getElement = getElement;
|
||||
|
||||
|
||||
module.exports.appendShape = function(source, shape, distance, target, connection, connectionParent) {
|
||||
|
||||
source = getElement(source);
|
||||
target = target && getElement(target);
|
||||
connectionParent = connectionParent && getElement(connectionParent);
|
||||
|
||||
return invoke(function(rules, modeling) {
|
||||
|
||||
var allowed = rules.allowed('shape.append', {
|
||||
source: source,
|
||||
shape: shape,
|
||||
target: target
|
||||
});
|
||||
|
||||
// assume append to be allowed
|
||||
expect(allowed).to.eql(true);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
module.exports.moveShapes = function(elements, distance, target, isAttach, hints) {
|
||||
|
||||
var actualElements = elements.map(function(e) {
|
||||
var actualElement = getElement(e);
|
||||
|
||||
// assume the elements we want to move exist
|
||||
expect(actualElement).to.exist;
|
||||
|
||||
return actualElement;
|
||||
});
|
||||
|
||||
|
||||
invoke(function(elementRegistry, modeling, rules) {
|
||||
|
||||
var delta = normalizeDelta(distance);
|
||||
|
||||
var allowed = rules.allowed('shapes.move', {
|
||||
shapes: actualElements,
|
||||
delta: delta,
|
||||
target: target
|
||||
});
|
||||
|
||||
// assume we can actually move
|
||||
expect(allowed).to.eql(typeof isAttach === 'boolean' ? 'attach' : true);
|
||||
|
||||
// perform actual move
|
||||
modeling.moveShapes(actualElements, delta, target, isAttach, hints);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
module.exports.getBounds = function() {
|
||||
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
|
||||
if (isArray(args[0])) {
|
||||
args = args[0];
|
||||
}
|
||||
|
||||
return map(args, function(e) {
|
||||
|
||||
e = getElement(e);
|
||||
|
||||
if (e.waypoints) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return pick(e, [ 'x', 'y', 'width', 'height' ]);
|
||||
});
|
||||
};
|
Loading…
Reference in New Issue