Skip to content

Commit 2baa067

Browse files
committed
feat: support ad-hoc sub-processes
Related to camunda/camunda-modeler#5591
1 parent d14855b commit 2baa067

File tree

7 files changed

+115
-61
lines changed

7 files changed

+115
-61
lines changed

lib/ElementConfig.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const DEFAULT_CONFIG = {
99
output: {}
1010
};
1111

12-
const SUPPORTED_ELEMENT_TYPES = [ 'bpmn:Task' ];
12+
const SUPPORTED_ELEMENT_TYPES = [ 'bpmn:Task', 'bpmn:SubProcess' ];
1313

1414
const DEFAULT_OUTPUT = undefined;
1515

lib/hooks/useSelectedElement.js

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
import { useState, useEffect } from 'react';
22

33
import {
4-
getBusinessObject,
5-
is,
64
isAny
75
} from 'bpmn-js/lib/util/ModelUtil';
86

9-
const SUPPORTED_ELEMENT_TYPES = [ 'bpmn:Task' ];
7+
import { isInsideAdHocSubProcess } from '../utils/element';
108

11-
export const SINGLE_TASK_SELECTION_REQUIRED_MESSAGE = 'Select a task to start testing.';
12-
export const TASK_SELECTION_REQUIRED_MESSAGE = 'Task testing is only supported for tasks. Select a task to start testing.';
13-
export const AD_HOC_SUBPROCESS_TASK_UNSUPPORTED_MESSAGE = 'Task testing is not supported for tasks inside an ad-hoc subprocess. Select a different task to start testing.';
9+
const SUPPORTED_ELEMENT_TYPES = [ 'bpmn:Task', 'bpmn:SubProcess' ];
10+
11+
export const SINGLE_TASK_SELECTION_REQUIRED_MESSAGE = 'Select a task or subprocess to start testing.';
12+
export const TASK_SELECTION_REQUIRED_MESSAGE = 'Task testing is only supported for tasks and subprocesses. Select one to start testing.';
13+
export const AD_HOC_SUBPROCESS_MESSAGE = 'Task testing is not supported for tasks inside ad-hoc subprocesses.';
1414

1515
/**
16-
* Get currently selected BPMN element, if it is a single `bpmn:Task`. If not,
17-
* return null and a message indicating what to do.
16+
* Get currently selected BPMN element, if it is a single supported element
17+
* (`bpmn:Task` or `bpmn:SubProcess`). If not, return null and a message
18+
* indicating what to do.
1819
*
1920
* @param {Object} injector
2021
* @return {[ Object|null, string|null ]}
@@ -74,27 +75,11 @@ function validateSelection(selection) {
7475
return TASK_SELECTION_REQUIRED_MESSAGE;
7576
}
7677

77-
if (isInAdHocSubprocess(selection[0])) {
78-
return AD_HOC_SUBPROCESS_TASK_UNSUPPORTED_MESSAGE;
78+
if (isInsideAdHocSubProcess(selection[0])) {
79+
return AD_HOC_SUBPROCESS_MESSAGE;
7980
}
8081

8182
return null;
8283
}
8384

84-
function isInAdHocSubprocess(element) {
85-
const bo = getBusinessObject(element);
86-
let parent = getParent(bo);
87-
88-
while (parent) {
89-
if (is(parent, 'bpmn:AdHocSubProcess')) {
90-
return true;
91-
}
92-
parent = getParent(parent);
93-
}
9485

95-
return false;
96-
}
97-
98-
function getParent(bo) {
99-
return bo.$parent;
100-
}

lib/utils/element.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@ export function getConcreteType(element) {
3131
return element.type.split(':')[1];
3232
}
3333

34+
/**
35+
* Check if an element is a child of an ad-hoc subprocess.
36+
*
37+
* @param {import('../types').Element} element
38+
*
39+
* @returns {boolean}
40+
*/
41+
export function isInsideAdHocSubProcess(element) {
42+
let businessObject = getBusinessObject(element).$parent;
43+
44+
while (businessObject) {
45+
if (is(businessObject, 'bpmn:AdHocSubProcess')) {
46+
return true;
47+
}
48+
49+
businessObject = businessObject.$parent;
50+
}
51+
52+
return false;
53+
}
54+
3455
/**
3556
* Get parent process of an element.
3657
*

test/components/TaskTesting/TaskTesting.spec.js

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -49,35 +49,7 @@ describe('TaskTesting', function() {
4949
}));
5050

5151

52-
it('should show task type and name (generic)', inject(async function(elementRegistry, selection) {
53-
54-
// given
55-
renderTaskTesting();
56-
57-
// when
58-
selection.select(elementRegistry.get('ServiceTask_1'));
59-
60-
// then
61-
await screen.findByText('Script Task');
62-
await screen.findByText('No inputs');
63-
}));
64-
65-
66-
it('should show task type and name (element template)', inject(async function(elementRegistry, selection) {
67-
68-
// given
69-
renderTaskTesting();
70-
71-
// when
72-
selection.select(elementRegistry.get('ServiceTask_3'));
73-
74-
// then
75-
await screen.findByText('REST Outbound Connector');
76-
await screen.findByText('REST');
77-
}));
78-
79-
80-
it('should display unsupported message for a task in ad-hoc subprocess', inject(
52+
it('should not support a task in ad-hoc subprocess', inject(
8153
async function(elementRegistry, selection) {
8254

8355
// given
@@ -87,12 +59,12 @@ describe('TaskTesting', function() {
8759
selection.select(elementRegistry.get('AdHocSubProcessTask'));
8860

8961
// then
90-
await screen.findByText(AD_HOC_SUBPROCESS_TASK_UNSUPPORTED_MESSAGE);
62+
await screen.findByText(AD_HOC_SUBPROCESS_MESSAGE);
9163
})
9264
);
9365

9466

95-
it('should display unsupported message for a task in a collapsed ad-hoc subprocess', inject(
67+
it('should not support a task in a collapsed ad-hoc subprocess', inject(
9668
async function(elementRegistry, selection, canvas) {
9769

9870
// given
@@ -103,7 +75,7 @@ describe('TaskTesting', function() {
10375
selection.select(elementRegistry.get('CollapsedAdHocSubProcessTask'));
10476

10577
// then
106-
await screen.findByText(AD_HOC_SUBPROCESS_TASK_UNSUPPORTED_MESSAGE);
78+
await screen.findByText(AD_HOC_SUBPROCESS_MESSAGE);
10779
})
10880
);
10981

test/fixtures/ElementConfig.bpmn

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@
7878
<bpmn:incoming>Flow_0ggbd4s</bpmn:incoming>
7979
<bpmn:outgoing>Flow_0hwbr17</bpmn:outgoing>
8080
</bpmn:serviceTask>
81+
<bpmn:adHocSubProcess id="AdHocSubProcess_1">
82+
<bpmn:task id="AdHocChild_1" name="In ad-hoc subprocess" />
83+
</bpmn:adHocSubProcess>
8184
</bpmn:process>
8285
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
8386
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
@@ -101,6 +104,12 @@
101104
<dc:Bounds x="650" y="80" width="100" height="80" />
102105
<bpmndi:BPMNLabel />
103106
</bpmndi:BPMNShape>
107+
<bpmndi:BPMNShape id="AdHocSubProcess_1_di" bpmnElement="AdHocSubProcess_1" isExpanded="true">
108+
<dc:Bounds x="230" y="200" width="200" height="120" />
109+
</bpmndi:BPMNShape>
110+
<bpmndi:BPMNShape id="AdHocChild_1_di" bpmnElement="AdHocChild_1">
111+
<dc:Bounds x="270" y="230" width="100" height="80" />
112+
</bpmndi:BPMNShape>
104113
<bpmndi:BPMNEdge id="Flow_0d202t1_di" bpmnElement="Flow_0d202t1">
105114
<di:waypoint x="188" y="120" />
106115
<di:waypoint x="230" y="120" />

test/hooks/useSelectedElement.spec.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { bootstrapModeler, inject } from '../util/Util';
55
import {
66
useSelectedElement,
77
SINGLE_TASK_SELECTION_REQUIRED_MESSAGE,
8-
TASK_SELECTION_REQUIRED_MESSAGE
8+
TASK_SELECTION_REQUIRED_MESSAGE,
9+
AD_HOC_SUBPROCESS_MESSAGE
910
} from '../../lib/hooks/useSelectedElement';
1011

1112
import diagramXML from '../fixtures/ElementConfig.bpmn';
@@ -86,4 +87,24 @@ describe('useSelectedElement', function() {
8687
expect(message).to.be.null;
8788
}));
8889

90+
91+
it('should return null and a message if element is inside ad-hoc subprocess', inject(function(elementRegistry, injector, selection) {
92+
93+
// given
94+
const { result } = renderHook(() => useSelectedElement(injector));
95+
96+
const element = elementRegistry.get('AdHocChild_1');
97+
98+
// when
99+
act(() => {
100+
selection.select(element);
101+
});
102+
103+
const [ selectedElement, message ] = result.current;
104+
105+
// then
106+
expect(selectedElement).to.be.null;
107+
expect(message).to.equal(AD_HOC_SUBPROCESS_MESSAGE);
108+
}));
109+
89110
});

test/utils/element.spec.js

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { bootstrapModeler, inject } from '../util/Util';
22

3-
import { getProcessId } from '../../lib/utils/element';
3+
import { getProcessId, isInsideAdHocSubProcess } from '../../lib/utils/element';
44

55
import processXML from '../fixtures/diagram.bpmn';
66
import collaborationXML from '../fixtures/collaboration.bpmn';
@@ -50,4 +50,50 @@ describe('element', function() {
5050

5151
});
5252

53+
54+
describe('isInsideAdHocSubProcess', function() {
55+
56+
beforeEach(bootstrapModeler(processXML));
57+
58+
59+
it('should return true for direct child of ad-hoc subprocess', inject(function(elementRegistry) {
60+
61+
// given
62+
const element = elementRegistry.get('AdHocSubProcessTask');
63+
64+
// when
65+
const result = isInsideAdHocSubProcess(element);
66+
67+
// then
68+
expect(result).to.be.true;
69+
}));
70+
71+
72+
it('should return true for nested child of ad-hoc subprocess', inject(function(elementRegistry) {
73+
74+
// given
75+
const element = elementRegistry.get('CollapsedAdHocSubProcessTask');
76+
77+
// when
78+
const result = isInsideAdHocSubProcess(element);
79+
80+
// then
81+
expect(result).to.be.true;
82+
}));
83+
84+
85+
it('should return false for element not in ad-hoc subprocess', inject(function(elementRegistry) {
86+
87+
// given
88+
const element = elementRegistry.get('ServiceTask_1');
89+
90+
// when
91+
const result = isInsideAdHocSubProcess(element);
92+
93+
// then
94+
expect(result).to.be.false;
95+
}));
96+
97+
});
98+
5399
});

0 commit comments

Comments
 (0)