From ae02f4b6c575b86af234a5834ca2785d35cb8e67 Mon Sep 17 00:00:00 2001 From: Niklas Kiefer Date: Thu, 9 Sep 2021 11:56:19 +0200 Subject: [PATCH] feat(camunda-platform): add input & output props Closes #54 --- .../CamundaPlatformPropertiesProvider.js | 33 +- .../properties/InputOutputParameter.js | 236 ++++++++ .../camunda-platform/properties/InputProps.js | 128 +++++ .../camunda-platform/properties/ListProps.js | 149 +++++ .../camunda-platform/properties/MapProps.js | 188 +++++++ .../properties/OutputProps.js | 128 +++++ .../camunda-platform/properties/index.js | 2 + .../camunda-platform/utils/InputOutputUtil.js | 178 ++++++ .../CamundaPlatformPropertiesProvider.spec.js | 68 +++ .../InputOutputParameter.bpmn | 46 ++ .../InputOutputParameter.spec.js | 524 ++++++++++++++++++ .../provider/camunda-platform/InputProps.bpmn | 40 ++ .../camunda-platform/InputProps.spec.js | 229 ++++++++ .../provider/camunda-platform/ListProps.bpmn | 33 ++ .../camunda-platform/ListProps.spec.js | 362 ++++++++++++ .../provider/camunda-platform/MapProps.bpmn | 39 ++ .../camunda-platform/MapProps.spec.js | 452 +++++++++++++++ .../camunda-platform/OutputProps.bpmn | 40 ++ .../camunda-platform/OutputProps.spec.js | 229 ++++++++ 19 files changed, 3096 insertions(+), 8 deletions(-) create mode 100644 src/provider/camunda-platform/properties/InputOutputParameter.js create mode 100644 src/provider/camunda-platform/properties/InputProps.js create mode 100644 src/provider/camunda-platform/properties/ListProps.js create mode 100644 src/provider/camunda-platform/properties/MapProps.js create mode 100644 src/provider/camunda-platform/properties/OutputProps.js create mode 100644 src/provider/camunda-platform/utils/InputOutputUtil.js create mode 100644 test/spec/provider/camunda-platform/InputOutputParameter.bpmn create mode 100644 test/spec/provider/camunda-platform/InputOutputParameter.spec.js create mode 100644 test/spec/provider/camunda-platform/InputProps.bpmn create mode 100644 test/spec/provider/camunda-platform/InputProps.spec.js create mode 100644 test/spec/provider/camunda-platform/ListProps.bpmn create mode 100644 test/spec/provider/camunda-platform/ListProps.spec.js create mode 100644 test/spec/provider/camunda-platform/MapProps.bpmn create mode 100644 test/spec/provider/camunda-platform/MapProps.spec.js create mode 100644 test/spec/provider/camunda-platform/OutputProps.bpmn create mode 100644 test/spec/provider/camunda-platform/OutputProps.spec.js diff --git a/src/provider/camunda-platform/CamundaPlatformPropertiesProvider.js b/src/provider/camunda-platform/CamundaPlatformPropertiesProvider.js index 0fff43e2..61712497 100644 --- a/src/provider/camunda-platform/CamundaPlatformPropertiesProvider.js +++ b/src/provider/camunda-platform/CamundaPlatformPropertiesProvider.js @@ -17,8 +17,10 @@ import { HistoryCleanupProps, ImplementationProps, InitiatorProps, + InputProps, JobExecutionProps, MultiInstanceProps, + OutputProps, ProcessVariablesProps, ScriptTaskProps, TasklistProps, @@ -87,6 +89,8 @@ export default class CamundaPlatformPropertiesProvider { CallActivityGroup(element), CandidateStarterGroup(element), ConditionGroup(element), + InputGroup(element), + OutputGroup(element), ConnectorGroup(element), ExtensionPropertiesGroup(element), ExternalTaskGroup(element), @@ -94,7 +98,6 @@ export default class CamundaPlatformPropertiesProvider { FormKeyGroup(element), FormGroup(element), HistoryCleanupGroup(element), - InputOutputGroup(element), JobExecutionGroup(element), ListenerGroup(element), MultiInstanceGroup(element), @@ -479,16 +482,30 @@ function ListenerGroup(element) { return null; } -// @TODO: implement, hide with no entries in the meantime -function InputOutputGroup(element) { +function InputGroup(element) { const group = { - label: 'Input Output', - id: 'CamundaPlatform__InputOutput', - component: Group, - entries: [] + label: 'Inputs', + id: 'CamundaPlatform__Input', + component: ListGroup, + ...InputProps({ element }) }; - if (group.entries.length) { + if (group.items) { + return group; + } + + return null; +} + +function OutputGroup(element) { + const group = { + label: 'Outputs', + id: 'CamundaPlatform__Output', + component: ListGroup, + ...OutputProps({ element }) + }; + + if (group.items) { return group; } diff --git a/src/provider/camunda-platform/properties/InputOutputParameter.js b/src/provider/camunda-platform/properties/InputOutputParameter.js new file mode 100644 index 00000000..e3b28c6c --- /dev/null +++ b/src/provider/camunda-platform/properties/InputOutputParameter.js @@ -0,0 +1,236 @@ +import { + is +} from 'bpmn-js/lib/util/ModelUtil'; + +import TextArea, { isEdited as textAreaIsEdited } from '@bpmn-io/properties-panel/lib/components/entries/TextArea'; +import TextField, { isEdited as textFieldIsEdited } from '@bpmn-io/properties-panel/lib/components/entries/TextField'; +import Select, { isEdited as selectIsEdited } from '@bpmn-io/properties-panel/lib/components/entries/Select'; + +import { + ScriptProps +} from './ScriptProps'; + +import { + useService +} from '../../../hooks'; + +import { + getInputOutputType +} from '../utils/InputOutputUtil'; + +import { + createElement +} from '../../../utils/ElementUtil'; + +import { + ListProps +} from './ListProps'; + +import { + MapProps +} from './MapProps'; + +const DEFAULT_PROPS = { + value: undefined, + definition: undefined +}; + + +export default function InputOutputParameter(props) { + const { + idPrefix, + element, + parameter + } = props; + + const inputOutputType = getInputOutputType(parameter); + + let entries = [ + { + id: idPrefix + '-name', + component: , + isEdited: textFieldIsEdited + }, + { + id: idPrefix + '-type', + component: , + isEdited: selectIsEdited + } + ]; + + // (1) String or expression + if (inputOutputType === 'stringOrExpression') { + + entries.push({ + id: idPrefix + '-stringOrExpression', + component: , + isEdited: textAreaIsEdited + }); + + // (2) Script + } else if (inputOutputType === 'script') { + const script = parameter.get('definition'); + + entries = [ + ...entries, + ...ScriptProps({ element, prefix: idPrefix + '-', script }) + ]; + + // (3) List + } else if (inputOutputType === 'list') { + entries.push({ + id: `${idPrefix}-list`, + component: + }); + + // (4) Map + } else if (inputOutputType === 'map') { + entries.push({ + id: `${idPrefix}-map`, + component: + }); + } + + return entries; +} + +function Name(props) { + const { + idPrefix, + element, + parameter + } = props; + + const commandStack = useService('commandStack'); + const translate = useService('translate'); + const debounce = useService('debounceInput'); + + const setValue = (value) => { + commandStack.execute('properties-panel.update-businessobject', { + element, + businessObject: parameter, + properties: { + name: value + } + }); + }; + + const getValue = (parameter) => { + return parameter.get('name'); + }; + + return TextField({ + element: parameter, + id: idPrefix + '-name', + label: translate(isInput(parameter) ? 'Local variable name' : 'Process variable name'), + getValue, + setValue, + debounce + }); +} + +function Type(props) { + const { + idPrefix, + element, + parameter + } = props; + + const bpmnFactory = useService('bpmnFactory'); + const commandStack = useService('commandStack'); + const translate = useService('translate'); + + const createDefinitionElement = (type) => { + return createElement(type, {}, parameter, bpmnFactory); + }; + + const getValue = (mapping) => { + return getInputOutputType(mapping); + }; + + const setValue = (value) => { + let properties = { + ...DEFAULT_PROPS + }; + + if (value === 'script') { + properties.definition = createDefinitionElement('camunda:Script'); + } + else if (value === 'list') { + properties.definition = createDefinitionElement('camunda:List'); + } + else if (value === 'map') { + properties.definition = createDefinitionElement('camunda:Map'); + } + + commandStack.execute('properties-panel.update-businessobject', { + element: element, + businessObject: parameter, + properties: properties + }); + }; + + const getOptions = () => { + const options = [ + { label: translate('List'), value: 'list' }, + { label: translate('Map'), value: 'map' }, + { label: translate('Script'), value: 'script' }, + { label: translate('String or expression'), value: 'stringOrExpression' }, + ]; + + return options; + }; + + return Select({ + element: parameter, + id: idPrefix + '-type', + label: translate('Assignment type'), + getValue, + setValue, + getOptions + }); +} + +function StringOrExpression(props) { + const { + idPrefix, + element, + parameter + } = props; + + const commandStack = useService('commandStack'); + const translate = useService('translate'); + const debounce = useService('debounceInput'); + + const setValue = (value) => { + commandStack.execute('properties-panel.update-businessobject', { + element, + businessObject: parameter, + properties: { + value + } + }); + }; + + const getValue = (parameter) => { + return parameter.get('value'); + }; + + return TextArea({ + element: parameter, + id: idPrefix + '-stringOrExpression', + label: translate('Value'), + description: translate('Start typing "${}" to create an expression.'), + getValue, + setValue, + rows: 1, + debounce + }); +} + + +// helper ///////////////////// + +function isInput(parameter) { + return is(parameter, 'camunda:InputParameter'); +} \ No newline at end of file diff --git a/src/provider/camunda-platform/properties/InputProps.js b/src/provider/camunda-platform/properties/InputProps.js new file mode 100644 index 00000000..43fe4507 --- /dev/null +++ b/src/provider/camunda-platform/properties/InputProps.js @@ -0,0 +1,128 @@ +import { + useContext +} from 'preact/hooks'; + +import { + BpmnPropertiesPanelContext +} from '../../../context'; + +import { + useService +} from '../../../hooks'; + +import InputOutputParameter from './InputOutputParameter'; + +import { + AddParameterCmd, + areInputParametersSupported, + getInputParameters, + getInputOutput +} from '../utils/InputOutputUtil'; + + +export function InputProps(props) { + const { + element + } = props; + + if (!areInputParametersSupported(element)) { + return null; + } + + const inputParameters = getInputParameters(element) || []; + + const items = inputParameters.map((parameter, index) => { + const id = element.id + '-inputParameter-' + index; + + return { + id, + label: parameter.get('name') || '', + entries: InputOutputParameter({ + idPrefix: id, + element, + parameter + }), + autoFocusEntry: id + '-name', + remove: RemoveContainer({ parameter }) + }; + }); + + return { + items, + add: AddInputParameter + }; +} + +function RemoveContainer(props) { + const { + parameter + } = props; + + return function RemoveInputParameter(props) { + const { + children + } = props; + + const { + selectedElement: element + } = useContext(BpmnPropertiesPanelContext); + + const commandStack = useService('commandStack'); + + const removeElement = (event) => { + event.stopPropagation(); + + const inputOutput = getInputOutput(element); + + if (!inputOutput) { + return; + } + + commandStack.execute('properties-panel.update-businessobject-list', { + element: element, + currentObject: inputOutput, + propertyName: 'inputParameters', + objectsToRemove: [ parameter ] + }); + }; + + return ( +
+ { + children + } +
+ ); + }; +} + +function AddInputParameter(props) { + const { + children + } = props; + + const { + selectedElement: element + } = useContext(BpmnPropertiesPanelContext); + + const bpmnFactory = useService('bpmnFactory'); + + const commandStack = useService('commandStack'); + + const addElement = (event) => { + event.stopPropagation(); + + commandStack.execute( + 'properties-panel.multi-command-executor', + AddParameterCmd(element, 'camunda:InputParameter', bpmnFactory) + ); + }; + + return ( +
+ { + children + } +
+ ); +} diff --git a/src/provider/camunda-platform/properties/ListProps.js b/src/provider/camunda-platform/properties/ListProps.js new file mode 100644 index 00000000..ba0c7a39 --- /dev/null +++ b/src/provider/camunda-platform/properties/ListProps.js @@ -0,0 +1,149 @@ +import List from '@bpmn-io/properties-panel/lib/components/entries/List'; +import Simple from '@bpmn-io/properties-panel/lib/components/entries/Simple'; + +import { + is +} from 'bpmn-js/lib/util/ModelUtil'; + +import { + useService +} from '../../../hooks'; + +import { + createElement +} from '../../../utils/ElementUtil'; + + +export function ListProps(props) { + const { + idPrefix, + element, + parameter + } = props; + + const bpmnFactory = useService('bpmnFactory'); + const commandStack = useService('commandStack'); + const translate = useService('translate'); + + const list = parameter.get('definition'); + const items = list.get('items'); + + function renderItem(item, index) { + const itemId = `${idPrefix}-listItem-${index}`; + + return ( + + ); + } + + function addItem() { + commandStack.execute('properties-panel.update-businessobject-list', { + element: element, + currentObject: list, + objectsToAdd: [ createElement('camunda:Value', {}, parameter, bpmnFactory) ], + propertyName: 'items' + }); + } + + function removeItem(item) { + commandStack.execute('properties-panel.update-businessobject-list', { + element: element, + currentObject: list, + objectsToRemove: [ item ], + propertyName: 'items' + }); + } + + return List({ + id: idPrefix + '-list', + items, + label: translate('List values'), + renderItem, + onAdd: addItem, + onRemove: removeItem + }); +} + +function ListItem(props) { + const { + idPrefix, + element, + item + } = props; + + const commandStack = useService('commandStack'); + const translate = useService('translate'); + + const definitionLabels = { + 'camunda:Map': translate('Map'), + 'camunda:List': translate('List'), + 'camunda:Script': translate('Script') + }; + + const getValue = () => { + if (isDefinitionType(item)) { + return definitionLabels[item.$type]; + } + + return item.get('value'); + }; + + const setValue = (value) => { + commandStack.execute('properties-panel.update-businessobject', { + element, + businessObject: item, + properties: { + value + } + }); + }; + + return ListValue({ + id: idPrefix + '-value', + disabled: isDefinitionType(item), + getValue, + setValue + }); +} + +function ListValue(props) { + const { + id, + disabled, + getValue, + setValue + } = props; + + const debounce = useService('debounceInput', true); + + return ( + + ); +} + +// helper ////////////////////// + +function isScript(element) { + return is(element, 'camunda:Script'); +} + +function isList(element) { + return is(element, 'camunda:List'); +} + +function isMap(element) { + return is(element, 'camunda:Map'); +} + +function isDefinitionType(element) { + return isScript(element) || isList(element) || isMap(element); +} \ No newline at end of file diff --git a/src/provider/camunda-platform/properties/MapProps.js b/src/provider/camunda-platform/properties/MapProps.js new file mode 100644 index 00000000..889878cb --- /dev/null +++ b/src/provider/camunda-platform/properties/MapProps.js @@ -0,0 +1,188 @@ +import Collapsible from '@bpmn-io/properties-panel/lib/components/entries/Collapsible'; +import List from '@bpmn-io/properties-panel/lib/components/entries/List'; +import TextField from '@bpmn-io/properties-panel/lib/components/entries/TextField'; + +import { + is +} from 'bpmn-js/lib/util/ModelUtil'; + +import { + useService +} from '../../../hooks'; + +import { + createElement +} from '../../../utils/ElementUtil'; + +export function MapProps(props) { + const { + idPrefix, + element, + parameter + } = props; + + const bpmnFactory = useService('bpmnFactory'); + const commandStack = useService('commandStack'); + const translate = useService('translate'); + + const map = parameter.get('definition'); + const entries = map.get('entries'); + + function renderEntry(entry, index) { + const entryId = `${idPrefix}-mapEntry-${index}`; + + return ( + ' } + /> + ); + } + + function addEntry() { + commandStack.execute('properties-panel.update-businessobject-list', { + element: element, + currentObject: map, + objectsToAdd: [ createElement('camunda:Entry', {}, parameter, bpmnFactory) ], + propertyName: 'entries' + }); + } + + function removeEntry(entry) { + commandStack.execute('properties-panel.update-businessobject-list', { + element: element, + currentObject: map, + objectsToRemove: [ entry ], + propertyName: 'entries' + }); + } + + return List({ + id: idPrefix + '-map', + items: entries, + label: translate('Map entries'), + renderItem: renderEntry, + onAdd: addEntry, + onRemove: removeEntry + }); +} + +function MapEntry(props) { + const { + element, + entry, + idPrefix + } = props; + + const entries = [{ + id: idPrefix + '-key', + component: + },{ + id: idPrefix + '-value', + component: + }]; + + return entries; +} + +function MapKey(props) { + const { + idPrefix, + element, + entry + } = props; + + const commandStack = useService('commandStack'); + const translate = useService('translate'); + const debounce = useService('debounceInput'); + + const setValue = (value) => { + commandStack.execute('properties-panel.update-businessobject', { + element, + businessObject: entry, + properties: { + key: value + } + }); + }; + + const getValue = () => { + return entry.get('key'); + }; + + return TextField({ + element: entry, + id: idPrefix + '-key', + label: translate('Key'), + getValue, + setValue, + debounce + }); +} + +function MapValue(props) { + const { + idPrefix, + element, + entry + } = props; + + const commandStack = useService('commandStack'); + const translate = useService('translate'); + const debounce = useService('debounceInput'); + + const definition = entry.get('definition'); + const definitionLabels = { + 'camunda:Map': translate('Map'), + 'camunda:List': translate('List'), + 'camunda:Script': translate('Script') + }; + + const setValue = (value) => { + commandStack.execute('properties-panel.update-businessobject', { + element, + businessObject: entry, + properties: { + value + } + }); + }; + + const getValue = () => { + if (isDefinitionType(definition)) { + return definitionLabels[definition.$type]; + } + + return entry.get('value'); + }; + + return TextField({ + element: entry, + id: idPrefix + '-value', + label: translate('Value'), + getValue, + setValue, + disabled: isDefinitionType(definition), + debounce + }); +} + + +// helper /////////////////// + +function isScript(element) { + return is(element, 'camunda:Script'); +} + +function isList(element) { + return is(element, 'camunda:List'); +} + +function isMap(element) { + return is(element, 'camunda:Map'); +} + +function isDefinitionType(element) { + return isScript(element) || isList(element) || isMap(element); +} \ No newline at end of file diff --git a/src/provider/camunda-platform/properties/OutputProps.js b/src/provider/camunda-platform/properties/OutputProps.js new file mode 100644 index 00000000..486d13cf --- /dev/null +++ b/src/provider/camunda-platform/properties/OutputProps.js @@ -0,0 +1,128 @@ +import { + useContext +} from 'preact/hooks'; + +import { + BpmnPropertiesPanelContext +} from '../../../context'; + +import { + useService +} from '../../../hooks'; + +import InputOutputParameter from './InputOutputParameter'; + +import { + AddParameterCmd, + areOutputParametersSupported, + getOutputParameters, + getInputOutput +} from '../utils/InputOutputUtil'; + + +export function OutputProps(props) { + const { + element + } = props; + + if (!areOutputParametersSupported(element)) { + return null; + } + + const outputParameters = getOutputParameters(element) || []; + + const items = outputParameters.map((parameter, index) => { + const id = element.id + '-outputParameter-' + index; + + return { + id, + label: parameter.get('name') || '', + entries: InputOutputParameter({ + idPrefix: id, + element, + parameter + }), + autoFocusEntry: id + '-name', + remove: RemoveContainer({ parameter }) + }; + }); + + return { + items, + add: AddOutputParameter + }; +} + +function RemoveContainer(props) { + const { + parameter + } = props; + + return function RemoveOutputParameter(props) { + const { + children + } = props; + + const { + selectedElement: element + } = useContext(BpmnPropertiesPanelContext); + + const commandStack = useService('commandStack'); + + const removeElement = (event) => { + event.stopPropagation(); + + const inputOutput = getInputOutput(element); + + if (!inputOutput) { + return; + } + + commandStack.execute('properties-panel.update-businessobject-list', { + element: element, + currentObject: inputOutput, + propertyName: 'outputParameters', + objectsToRemove: [ parameter ] + }); + }; + + return ( +
+ { + children + } +
+ ); + }; +} + +function AddOutputParameter(props) { + const { + children + } = props; + + const { + selectedElement: element + } = useContext(BpmnPropertiesPanelContext); + + const bpmnFactory = useService('bpmnFactory'); + + const commandStack = useService('commandStack'); + + const addElement = (event) => { + event.stopPropagation(); + + commandStack.execute( + 'properties-panel.multi-command-executor', + AddParameterCmd(element, 'camunda:OutputParameter', bpmnFactory) + ); + }; + + return ( +
+ { + children + } +
+ ); +} diff --git a/src/provider/camunda-platform/properties/index.js b/src/provider/camunda-platform/properties/index.js index dbae8e9f..bd72c951 100644 --- a/src/provider/camunda-platform/properties/index.js +++ b/src/provider/camunda-platform/properties/index.js @@ -11,8 +11,10 @@ export { FormKeyProps } from './FormKeyProps'; export { HistoryCleanupProps } from './HistoryCleanupProps'; export { ImplementationProps } from './ImplementationProps'; export { InitiatorProps } from './InitiatorProps'; +export { InputProps } from './InputProps'; export { JobExecutionProps } from './JobExecutionProps'; export { MultiInstanceProps } from './MultiInstanceProps'; +export { OutputProps } from './OutputProps'; export { ProcessVariablesProps } from './ProcessVariablesProps'; export { ScriptTaskProps } from './ScriptTaskProps'; export { TasklistProps } from './TasklistProps'; diff --git a/src/provider/camunda-platform/utils/InputOutputUtil.js b/src/provider/camunda-platform/utils/InputOutputUtil.js new file mode 100644 index 00000000..8fc4b939 --- /dev/null +++ b/src/provider/camunda-platform/utils/InputOutputUtil.js @@ -0,0 +1,178 @@ +import { + isAny +} from 'bpmn-js/lib/features/modeling/util/ModelingUtil'; + +import { + getBusinessObject, + is +} from 'bpmn-js/lib/util/ModelUtil'; + +import { + createElement, + nextId +} from '../../../utils/ElementUtil'; + +import { + getExtensionElementsList +} from './ExtensionElementsUtil'; + + +function getElements(businessObject, type, property) { + const elements = getExtensionElementsList(businessObject, type); + return !property ? elements : (elements[0] || {})[property] || []; +} + +function getParameters(element, prop) { + const inputOutput = getInputOutput(element); + return (inputOutput && inputOutput.get(prop)) || []; +} + +/** + * Get a camunda:inputOutput from the business object + * + * @param {djs.model.Base} element + * + * @return {ModdleElement} the inputOutput object + */ +export function getInputOutput(element) { + const businessObject = getBusinessObject(element); + return (getElements(businessObject, 'camunda:InputOutput') || [])[0]; +} + + +/** + * Return all input parameters existing in the business object, and + * an empty array if none exist. + * + * @param {djs.model.Base} element + * + * @return {Array} a list of input parameter objects + */ +export function getInputParameters(element) { + return getParameters(element, 'inputParameters'); +} + +/** + * Return all output parameters existing in the business object, and + * an empty array if none exist. + * + * @param {djs.model.Base} element + * + * @return {Array} a list of output parameter objects + */ +export function getOutputParameters(element) { + return getParameters(element, 'outputParameters'); +} + + +export function isInputOutputSupported(element) { + const businessObject = getBusinessObject(element); + + return ( + is(businessObject, 'bpmn:FlowNode') && !( + isAny(businessObject, [ 'bpmn:StartEvent', 'bpmn:BoundaryEvent', 'bpmn:Gateway' ]) || + is(businessObject, 'bpmn:SubProcess') && businessObject.get('triggeredByEvent') + ) + ); +} + +export function areInputParametersSupported(element) { + return isInputOutputSupported(element); +} + +export function areOutputParametersSupported(element) { + const businessObject = getBusinessObject(element); + return ( + isInputOutputSupported(element) && + !is(businessObject, 'bpmn:EndEvent') && + !businessObject.loopCharacteristics + ); +} + +export function getInputOutputType(parameter) { + const definitionTypes = { + 'camunda:Map': 'map', + 'camunda:List': 'list', + 'camunda:Script': 'script' + }; + + let type = 'stringOrExpression'; + + const definition = parameter.get('definition'); + if (typeof definition !== 'undefined') { + type = definitionTypes[definition.$type]; + } + + return type; +} + +export function CreateParameterCmd(element, type, parent, bpmnFactory) { + const isInput = type === 'camunda:InputParameter'; + + const newParameter = createElement(type, { + name: nextId(isInput ? 'Input_' : 'Output_') + }, parent, bpmnFactory); + + return { + cmd: 'properties-panel.update-businessobject-list', + context: { + element: element, + currentObject: parent, + propertyName: isInput ? 'inputParameters' : 'outputParameters', + objectsToAdd: [ newParameter ] + } + }; +} + +export function AddParameterCmd(element, type, bpmnFactory) { + const commands = []; + const businessObject = getBusinessObject(element); + + let extensionElements = businessObject.get('extensionElements'); + + // (1) ensure extension elements + if (!extensionElements) { + extensionElements = createElement( + 'bpmn:ExtensionElements', + { values: [] }, + businessObject, + bpmnFactory + ); + + commands.push({ + cmd: 'properties-panel.update-businessobject', + context: { + element: element, + businessObject: businessObject, + properties: { extensionElements } + } + }); + } + + // (2) ensure inputOutput + let inputOutput = getInputOutput(element); + + if (!inputOutput) { + const parent = extensionElements; + + inputOutput = createElement('camunda:InputOutput', { + inputParameters: [], + outputParameters: [] + }, parent, bpmnFactory); + + commands.push({ + cmd: 'properties-panel.update-businessobject-list', + context: { + element: element, + currentObject: extensionElements, + propertyName: 'values', + objectsToAdd: [ inputOutput ] + } + }); + } + + // (3) create + add parameter + commands.push(CreateParameterCmd(element, type, inputOutput, bpmnFactory)); + + return commands; +} \ No newline at end of file diff --git a/test/spec/provider/camunda-platform/CamundaPlatformPropertiesProvider.spec.js b/test/spec/provider/camunda-platform/CamundaPlatformPropertiesProvider.spec.js index ebd51f80..b9ad29d4 100644 --- a/test/spec/provider/camunda-platform/CamundaPlatformPropertiesProvider.spec.js +++ b/test/spec/provider/camunda-platform/CamundaPlatformPropertiesProvider.spec.js @@ -565,6 +565,74 @@ describe('', function() { expect(formKeyGroup).to.not.exist; })); + + it('should show input group', inject(async function(elementRegistry, selection) { + + // given + const task = elementRegistry.get('Task_1'); + + await act(() => { + selection.select(task); + }); + + // when + const inputGroup = getGroup(container, 'CamundaPlatform__Input'); + + // then + expect(inputGroup).to.exist; + })); + + + it('should NOT show input group', inject(async function(elementRegistry, selection) { + + // given + const startEvent = elementRegistry.get('StartEvent_1'); + + await act(() => { + selection.select(startEvent); + }); + + // when + const inputGroup = getGroup(container, 'CamundaPlatform__Input'); + + // then + expect(inputGroup).to.not.exist; + })); + + + it('should show output group', inject(async function(elementRegistry, selection) { + + // given + const task = elementRegistry.get('Task_1'); + + await act(() => { + selection.select(task); + }); + + // when + const outputGroup = getGroup(container, 'CamundaPlatform__Output'); + + // then + expect(outputGroup).to.exist; + })); + + + it('should NOT show output group', inject(async function(elementRegistry, selection) { + + // given + const endEvent = elementRegistry.get('EndEvent_1'); + + await act(() => { + selection.select(endEvent); + }); + + // when + const outputGroup = getGroup(container, 'CamundaPlatform__Output'); + + // then + expect(outputGroup).to.not.exist; + })); + }); diff --git a/test/spec/provider/camunda-platform/InputOutputParameter.bpmn b/test/spec/provider/camunda-platform/InputOutputParameter.bpmn new file mode 100644 index 00000000..7fb261ba --- /dev/null +++ b/test/spec/provider/camunda-platform/InputOutputParameter.bpmn @@ -0,0 +1,46 @@ + + + + + + + ${expression} + + script + + + + value1 + value2 + value3 + value4 + + + + + value1 + value2 + value3 + value4 + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/provider/camunda-platform/InputOutputParameter.spec.js b/test/spec/provider/camunda-platform/InputOutputParameter.spec.js new file mode 100644 index 00000000..ccf09100 --- /dev/null +++ b/test/spec/provider/camunda-platform/InputOutputParameter.spec.js @@ -0,0 +1,524 @@ +import TestContainer from 'mocha-test-container-support'; +import { act } from '@testing-library/preact'; + +import { + bootstrapPropertiesPanel, + changeInput, + inject +} from 'test/TestHelper'; + +import { + query as domQuery +} from 'min-dom'; + +import CoreModule from 'bpmn-js/lib/core'; +import SelectionModule from 'diagram-js/lib/features/selection'; +import ModelingModule from 'bpmn-js/lib/features/modeling'; + +import BpmnPropertiesPanel from 'src/render'; + +import BpmnPropertiesProvider from 'src/provider/bpmn'; + +import CamundaPlatformPropertiesProvider from 'src/provider/camunda-platform'; + +import camundaModdleExtensions from 'camunda-bpmn-moddle/resources/camunda.json'; + +import { + getInputOutputType, + getInputParameters +} from 'src/provider/camunda-platform/utils/InputOutputUtil'; + +import diagramXML from './InputOutputParameter.bpmn'; + + +describe('provider/camunda-platform - InputOutputParameter', function() { + + const testModules = [ + CoreModule, SelectionModule, ModelingModule, + BpmnPropertiesPanel, + BpmnPropertiesProvider, + CamundaPlatformPropertiesProvider + ]; + + const moddleExtensions = { + camunda: camundaModdleExtensions + }; + + let container; + + beforeEach(function() { + container = TestContainer.get(this); + }); + + beforeEach(bootstrapPropertiesPanel(diagramXML, { + modules: testModules, + moddleExtensions, + debounceInput: false + })); + + + describe('bpmn:ServiceTask', function() { + + describe('#inputParameter.name', function() { + + it('should NOT display for empty parameters', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_empty'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name=ServiceTask_empty-inputParameter-1-name]', group); + + // then + expect(input).to.not.exist; + })); + + + it('should display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name=ServiceTask_1-inputParameter-0-name]', group); + + // then + expect(input.value).to.eql(getInputParameter(serviceTask, 0).get('name')); + })); + + + it('should update', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name=ServiceTask_1-inputParameter-0-name]', group); + changeInput(input, 'newValue'); + + // then + expect(getInputParameter(serviceTask, 0).get('name')).to.eql('newValue'); + })); + + + it('should update on external change', + inject(async function(elementRegistry, selection, commandStack) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + const originalValue = getInputParameter(serviceTask, 0).get('name'); + + await act(() => { + selection.select(serviceTask); + }); + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name=ServiceTask_1-inputParameter-0-name]', group); + changeInput(input, 'newValue'); + + // when + await act(() => { + commandStack.undo(); + }); + + // then + expect(input.value).to.eql(originalValue); + }) + ); + + }); + + + describe('#inputParameter.type', function() { + + it('should NOT display for empty parameters', + inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_empty'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const select = domQuery('select[name=ServiceTask_empty-inputParameter-0-type]', group); + + // then + expect(select).to.not.exist; + }) + ); + + + it('should display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const select = domQuery('select[name=ServiceTask_1-inputParameter-0-type]', group); + + // then + expect(select.value).to.eql(getInputOutputType(getInputParameter(serviceTask, 0))); + })); + + + it('should update - stringOrExpression', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const select = domQuery('select[name=ServiceTask_1-inputParameter-1-type]', group); + changeInput(select, 'stringOrExpression'); + + // then + const parameter = getInputParameter(serviceTask, 1); + expect(parameter.get('definition')).to.not.exist; + })); + + + it('should update - script', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const select = domQuery('select[name=ServiceTask_1-inputParameter-0-type]', group); + changeInput(select, 'script'); + + // then + const parameter = getInputParameter(serviceTask, 0); + expect(parameter.get('definition')).to.exist; + expect(parameter.get('definition').$type).to.eql('camunda:Script'); + expect(parameter.get('value')).to.not.exist; + })); + + + it('should update - list', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const select = domQuery('select[name=ServiceTask_1-inputParameter-0-type]', group); + changeInput(select, 'list'); + + // then + const parameter = getInputParameter(serviceTask, 0); + expect(parameter.get('definition')).to.exist; + expect(parameter.get('definition').$type).to.eql('camunda:List'); + expect(parameter.get('value')).to.not.exist; + })); + + + it('should update - map', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const select = domQuery('select[name=ServiceTask_1-inputParameter-0-type]', group); + changeInput(select, 'map'); + + // then + const parameter = getInputParameter(serviceTask, 0); + expect(parameter.get('definition')).to.exist; + expect(parameter.get('definition').$type).to.eql('camunda:Map'); + expect(parameter.get('value')).to.not.exist; + })); + + + it('should update on external change', + inject(async function(elementRegistry, selection, commandStack) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + const originalValue = getInputOutputType(getInputParameter(serviceTask, 0)); + + await act(() => { + selection.select(serviceTask); + }); + const group = getGroup(container, 'CamundaPlatform__Input'); + const select = domQuery('select[name=ServiceTask_1-inputParameter-0-type]', group); + changeInput(select, 'script'); + + // when + await act(() => { + commandStack.undo(); + }); + + // then + expect(select.value).to.eql(originalValue); + }) + ); + + }); + + + describe('#inputParameter.value', function() { + + it('should NOT display for empty parameters', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_empty'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const textarea = domQuery('textarea[name=ServiceTask_empty-inputParameter-1-stringOrExpression]', group); + + // then + expect(textarea).to.not.exist; + })); + + + it('should NOT display for script parameters', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const textarea = domQuery('textarea[name=ServiceTask_1-inputParameter-1-stringOrExpression]', group); + + // then + expect(textarea).to.not.exist; + })); + + + it('should display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const textarea = domQuery('textarea[name=ServiceTask_1-inputParameter-0-stringOrExpression]', group); + + // then + expect(textarea.value).to.eql(getInputParameter(serviceTask, 0).get('value')); + })); + + + it('should update', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const textarea = domQuery('textarea[name=ServiceTask_1-inputParameter-0-stringOrExpression]', group); + changeInput(textarea, 'newValue'); + + // then + expect(getInputParameter(serviceTask, 0).get('value')).to.eql('newValue'); + })); + + + it('should update on external change', + inject(async function(elementRegistry, selection, commandStack) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + const originalValue = getInputParameter(serviceTask, 0).get('value'); + + await act(() => { + selection.select(serviceTask); + }); + const group = getGroup(container, 'CamundaPlatform__Input'); + const textarea = domQuery('textarea[name=ServiceTask_1-inputParameter-0-stringOrExpression]', group); + changeInput(textarea, 'newValue'); + + // when + await act(() => { + commandStack.undo(); + }); + + // then + expect(textarea.value).to.eql(originalValue); + }) + ); + + }); + + + describe('#inputParameter.scriptFormat', function() { + + it('should NOT display for empty parameters', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_empty'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name=ServiceTask_empty-inputParameter-0-scriptFormat]', group); + + // then + expect(input).to.not.exist; + })); + + + it('should NOT display for list parameters', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name=ServiceTask_1-inputParameter-2-scriptFormat]', group); + + // then + expect(input).to.not.exist; + })); + + + it('should display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name=ServiceTask_1-inputParameter-1-scriptFormat]', group); + + const script = getDefinition(getInputParameter(serviceTask, 1)); + + // then + expect(input.value).to.eql(script.get('scriptFormat')); + })); + + + it('should update', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name=ServiceTask_1-inputParameter-1-scriptFormat]', group); + changeInput(input, 'newValue'); + + const script = getDefinition(getInputParameter(serviceTask, 1)); + + // then + expect(script.get('scriptFormat')).to.eql('newValue'); + })); + + + it('should update on external change', + inject(async function(elementRegistry, selection, commandStack) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + const script = getDefinition(getInputParameter(serviceTask, 1)); + const originalValue = script.get('scriptFormat'); + + await act(() => { + selection.select(serviceTask); + }); + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name=ServiceTask_1-inputParameter-1-scriptFormat]', group); + changeInput(input, 'newValue'); + + // when + await act(() => { + commandStack.undo(); + }); + + // then + expect(input.value).to.eql(originalValue); + }) + ); + + }); + + }); + +}); + + +// helper ////////////////// + +function getGroup(container, id) { + return domQuery(`[data-group-id="group-${id}"`, container); +} + +function getInputParameter(element, idx) { + return (getInputParameters(element) || [])[idx]; +} + +function getDefinition(parameter) { + return parameter.get('definition'); +} \ No newline at end of file diff --git a/test/spec/provider/camunda-platform/InputProps.bpmn b/test/spec/provider/camunda-platform/InputProps.bpmn new file mode 100644 index 00000000..10584f3b --- /dev/null +++ b/test/spec/provider/camunda-platform/InputProps.bpmn @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/provider/camunda-platform/InputProps.spec.js b/test/spec/provider/camunda-platform/InputProps.spec.js new file mode 100644 index 00000000..b57a07c0 --- /dev/null +++ b/test/spec/provider/camunda-platform/InputProps.spec.js @@ -0,0 +1,229 @@ +import TestContainer from 'mocha-test-container-support'; + +import { + act +} from '@testing-library/preact'; + +import { + bootstrapPropertiesPanel, + inject +} from 'test/TestHelper'; + +import { + query as domQuery, + queryAll as domQueryAll +} from 'min-dom'; + +import { + getBusinessObject +} from 'bpmn-js/lib/util/ModelUtil'; + +import CoreModule from 'bpmn-js/lib/core'; +import SelectionModule from 'diagram-js/lib/features/selection'; +import ModelingModule from 'bpmn-js/lib/features/modeling'; + +import BpmnPropertiesPanel from 'src/render'; + +import CamundaPlatformPropertiesProvider from 'src/provider/camunda-platform'; + +import camundaModdleExtensions from 'camunda-bpmn-moddle/resources/camunda.json'; + +import { + getInputParameters +} from 'src/provider/camunda-platform/utils/InputOutputUtil'; + +import diagramXML from './InputProps.bpmn'; + + +describe('provider/camunda-platform - InputProps', function() { + + const testModules = [ + CoreModule, SelectionModule, ModelingModule, + BpmnPropertiesPanel, + CamundaPlatformPropertiesProvider + ]; + + const moddleExtensions = { + camunda: camundaModdleExtensions + }; + + let container; + + beforeEach(function() { + container = TestContainer.get(this); + }); + + beforeEach(bootstrapPropertiesPanel(diagramXML, { + modules: testModules, + moddleExtensions, + debounceInput: false + })); + + + describe('bpmn:ServiceTask#inputParameters', function() { + + it('should display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const listItems = getInputListItems(group); + + // then + expect(group).to.exist; + expect(listItems.length).to.equal(getInputParameters(serviceTask).length); + })); + + + it('should add new input parameter', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const addEntry = domQuery('.bio-properties-panel-add-entry', group); + + // when + await act(() => { + addEntry.parentNode.click(); + }); + + // then + expect(getInputParameters(serviceTask)).to.have.length(5); + })); + + + it('should create non existing extension elements', + inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_empty'); + + await act(() => { + selection.select(serviceTask); + }); + + // assume + expect(getBusinessObject(serviceTask).get('extensionElements')).not.to.exist; + + const group = getGroup(container, 'CamundaPlatform__Input'); + const addEntry = domQuery('.bio-properties-panel-add-entry', group); + + // when + await act(() => { + addEntry.parentNode.click(); + }); + + // then + expect(getBusinessObject(serviceTask).get('extensionElements')).to.exist; + expect(getInputParameters(serviceTask)).to.have.length(1); + }) + ); + + + it('should re-use existing extensionElements', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_withExtensionElements'); + + await act(() => { + selection.select(serviceTask); + }); + + // assume + expect(getBusinessObject(serviceTask).get('extensionElements')).to.exist; + + const group = getGroup(container, 'CamundaPlatform__Input'); + const addEntry = domQuery('.bio-properties-panel-add-entry', group); + + // when + await act(() => { + addEntry.parentNode.click(); + }); + + // then + expect(getBusinessObject(serviceTask).get('extensionElements')).to.exist; + expect(getInputParameters(serviceTask)).to.have.length(1); + })); + + + it('should delete input parameter', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const listItems = getInputListItems(group); + const removeEntry = domQuery('.bio-properties-panel-remove-entry', listItems[0]); + + // when + await act(() => { + removeEntry.parentNode.click(); + }); + + // then + expect(getInputParameters(serviceTask)).to.have.length(3); + })); + + + it('should update on external change', + inject(async function(elementRegistry, selection, commandStack) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + const originalParameters = getInputParameters(serviceTask); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const addEntry = domQuery('.bio-properties-panel-add-entry', group); + await act(() => { + addEntry.parentNode.click(); + }); + + // when + await act(() => { + commandStack.undo(); + }); + + const listItems = getInputListItems(group); + + // then + expect(listItems.length).to.eql(originalParameters.length); + }) + ); + + }); + +}); + + +// helper ////////////////// + +function getGroup(container, id) { + return domQuery(`[data-group-id="group-${id}"`, container); +} + +function getListItems(container, type) { + return domQueryAll(`div[data-entry-id*="-${type}-"].bio-properties-panel-collapsible-entry`, container); +} + +function getInputListItems(container) { + return getListItems(container, 'inputParameter'); +} \ No newline at end of file diff --git a/test/spec/provider/camunda-platform/ListProps.bpmn b/test/spec/provider/camunda-platform/ListProps.bpmn new file mode 100644 index 00000000..2923df56 --- /dev/null +++ b/test/spec/provider/camunda-platform/ListProps.bpmn @@ -0,0 +1,33 @@ + + + + + + + + + value1 + value2 + value2 + + + value + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/provider/camunda-platform/ListProps.spec.js b/test/spec/provider/camunda-platform/ListProps.spec.js new file mode 100644 index 00000000..359e4ee4 --- /dev/null +++ b/test/spec/provider/camunda-platform/ListProps.spec.js @@ -0,0 +1,362 @@ +import TestContainer from 'mocha-test-container-support'; +import { act } from '@testing-library/preact'; + +import { + bootstrapPropertiesPanel, + changeInput, + inject +} from 'test/TestHelper'; + +import { + query as domQuery, + queryAll as domQueryAll +} from 'min-dom'; + +import CoreModule from 'bpmn-js/lib/core'; +import SelectionModule from 'diagram-js/lib/features/selection'; +import ModelingModule from 'bpmn-js/lib/features/modeling'; + +import BpmnPropertiesPanel from 'src/render'; + +import BpmnPropertiesProvider from 'src/provider/bpmn'; + +import CamundaPlatformPropertiesProvider from 'src/provider/camunda-platform'; + +import camundaModdleExtensions from 'camunda-bpmn-moddle/resources/camunda.json'; + +import { + getInputParameters +} from 'src/provider/camunda-platform/utils/InputOutputUtil'; + +import diagramXML from './ListProps.bpmn'; + + +describe('provider/camunda-platform - ListProps', function() { + + const testModules = [ + CoreModule, SelectionModule, ModelingModule, + BpmnPropertiesPanel, + BpmnPropertiesProvider, + CamundaPlatformPropertiesProvider + ]; + + const moddleExtensions = { + camunda: camundaModdleExtensions + }; + + let container; + + beforeEach(function() { + container = TestContainer.get(this); + }); + + beforeEach(bootstrapPropertiesPanel(diagramXML, { + modules: testModules, + moddleExtensions, + debounceInput: false + })); + + + describe('bpmn:ServiceTask#inputParameter.list', function() { + + describe('items', function() { + + it('should NOT display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const list = domQuery('div[data-entry-id="ServiceTask_1-inputParameter-1-list"]', group); + + // then + expect(list).to.not.exist; + })); + + + it('should display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const list = domQuery('div[data-entry-id="ServiceTask_1-inputParameter-0-list"]', group); + const listItems = getListItems(list); + + const parameter = getInputParameter(serviceTask, 0); + + // then + expect(listItems.length).to.equal(getItems(parameter).length); + })); + + + it('should add item', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const list = domQuery('div[data-entry-id="ServiceTask_1-inputParameter-0-list"]', group); + const addEntry = domQuery('.bio-properties-panel-add-entry', list); + + // when + await act(() => { + addEntry.click(); + }); + + const parameter = getInputParameter(serviceTask, 0); + + // then + expect(getItems(parameter)).to.have.length(4); + })); + + + it('should remove item', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const list = domQuery('div[data-entry-id="ServiceTask_1-inputParameter-0-list"]', group); + const listItem = getListItems(list)[0]; + const removeEntry = domQuery('.bio-properties-panel-remove-entry', listItem); + + // when + await act(() => { + removeEntry.click(); + }); + + const parameter = getInputParameter(serviceTask, 0); + + // then + expect(getItems(parameter)).to.have.length(2); + })); + + + it('should update on external change', + inject(async function(elementRegistry, selection, commandStack) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + const parameter = getInputParameter(serviceTask, 0); + const originalItems = getItems(parameter); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const list = domQuery('div[data-entry-id="ServiceTask_1-inputParameter-0-list"]', group); + const addEntry = domQuery('.bio-properties-panel-add-entry', list); + await act(() => { + addEntry.click(); + }); + + // when + await act(() => { + commandStack.undo(); + }); + + const listItems = getListItems(list); + + // then + expect(listItems.length).to.eql(originalItems.length); + }) + ); + + }); + + + describe('item.value', function() { + + it('should NOT display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-1-listItem-0-value"]', group); + + // then + expect(input).to.not.exist; + })); + + + it('should display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-0-listItem-0-value"]', group); + + const item = getItem(getInputParameter(serviceTask, 0), 0); + + // then + expect(input.value).to.eql(item.get('value')); + })); + + + it('should display as disabled - script', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-2-listItem-0-value"]', group); + + // then + expect(input.value).to.eql('Script'); + expect(input.disabled).to.be.true; + })); + + + it('should display as disabled - list', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-2-listItem-1-value"]', group); + + // then + expect(input.value).to.eql('List'); + expect(input.disabled).to.be.true; + })); + + + it('should display as disabled - map', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-2-listItem-2-value"]', group); + + // then + expect(input.value).to.eql('Map'); + expect(input.disabled).to.be.true; + })); + + + it('should update', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-0-listItem-0-value"]', group); + + // when + changeInput(input, 'newValue'); + + const item = getItem(getInputParameter(serviceTask, 0), 0); + + // then + expect(item.get('value')).to.eql('newValue'); + })); + + + it('should update on external change', + inject(async function(elementRegistry, selection, commandStack) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + const item = getItem(getInputParameter(serviceTask, 0), 0); + const originalValue = item.get('value'); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-0-listItem-0-value"]', group); + changeInput(input, 'newValue'); + + // when + await act(() => { + commandStack.undo(); + }); + + // then + expect(input.value).to.eql(originalValue); + }) + ); + + }); + + }); + +}); + + +// helper ////////////////// + +function getGroup(container, id) { + return domQuery(`[data-group-id="group-${id}"`, container); +} + +function getInputParameter(element, idx) { + return (getInputParameters(element) || [])[idx]; +} + +function getItems(parameter) { + return parameter.get('definition').get('items'); +} + +function getItem(parameter, idx) { + return (getItems(parameter) || [])[idx]; +} + +function getListItems(container) { + return domQueryAll('.bio-properties-panel-list-entry-item', container); +} \ No newline at end of file diff --git a/test/spec/provider/camunda-platform/MapProps.bpmn b/test/spec/provider/camunda-platform/MapProps.bpmn new file mode 100644 index 00000000..e1ac200f --- /dev/null +++ b/test/spec/provider/camunda-platform/MapProps.bpmn @@ -0,0 +1,39 @@ + + + + + + + + + value1 + value2 + value3 + + + value + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/provider/camunda-platform/MapProps.spec.js b/test/spec/provider/camunda-platform/MapProps.spec.js new file mode 100644 index 00000000..25da8393 --- /dev/null +++ b/test/spec/provider/camunda-platform/MapProps.spec.js @@ -0,0 +1,452 @@ +import TestContainer from 'mocha-test-container-support'; +import { act } from '@testing-library/preact'; + +import { + bootstrapPropertiesPanel, + changeInput, + inject +} from 'test/TestHelper'; + +import { + query as domQuery, + queryAll as domQueryAll +} from 'min-dom'; + +import CoreModule from 'bpmn-js/lib/core'; +import SelectionModule from 'diagram-js/lib/features/selection'; +import ModelingModule from 'bpmn-js/lib/features/modeling'; + +import BpmnPropertiesPanel from 'src/render'; + +import BpmnPropertiesProvider from 'src/provider/bpmn'; + +import CamundaPlatformPropertiesProvider from 'src/provider/camunda-platform'; + +import camundaModdleExtensions from 'camunda-bpmn-moddle/resources/camunda.json'; + +import { + getInputParameters +} from 'src/provider/camunda-platform/utils/InputOutputUtil'; + +import diagramXML from './MapProps.bpmn'; + + +describe('provider/camunda-platform - MapProps', function() { + + const testModules = [ + CoreModule, SelectionModule, ModelingModule, + BpmnPropertiesPanel, + BpmnPropertiesProvider, + CamundaPlatformPropertiesProvider + ]; + + const moddleExtensions = { + camunda: camundaModdleExtensions + }; + + let container; + + beforeEach(function() { + container = TestContainer.get(this); + }); + + beforeEach(bootstrapPropertiesPanel(diagramXML, { + modules: testModules, + moddleExtensions, + debounceInput: false + })); + + + describe('bpmn:ServiceTask#inputParameter.map', function() { + + describe('entries', function() { + + it('should NOT display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const list = domQuery('div[data-entry-id="ServiceTask_1-inputParameter-1-map"]', group); + + // then + expect(list).to.not.exist; + })); + + + it('should display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const list = domQuery('div[data-entry-id="ServiceTask_1-inputParameter-0-map"]', group); + const listItems = getListItems(list); + + const parameter = getInputParameter(serviceTask, 0); + + // then + expect(listItems.length).to.equal(getEntries(parameter).length); + })); + + + it('should add entry', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const list = domQuery('div[data-entry-id="ServiceTask_1-inputParameter-0-map"]', group); + const addEntry = domQuery('.bio-properties-panel-add-entry', list); + + // when + await act(() => { + addEntry.click(); + }); + + const parameter = getInputParameter(serviceTask, 0); + + // then + expect(getEntries(parameter)).to.have.length(4); + })); + + + it('should remove entry', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const list = domQuery('div[data-entry-id="ServiceTask_1-inputParameter-0-map"]', group); + const listItem = getListItems(list)[0]; + const removeEntry = domQuery('.bio-properties-panel-remove-entry', listItem); + + // when + await act(() => { + removeEntry.click(); + }); + + const parameter = getInputParameter(serviceTask, 0); + + // then + expect(getEntries(parameter)).to.have.length(2); + })); + + + it('should update on external change', + inject(async function(elementRegistry, selection, commandStack) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + const parameter = getInputParameter(serviceTask, 0); + const originalItems = getEntries(parameter); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const list = domQuery('div[data-entry-id="ServiceTask_1-inputParameter-0-map"]', group); + const addEntry = domQuery('.bio-properties-panel-add-entry', list); + await act(() => { + addEntry.click(); + }); + + // when + await act(() => { + commandStack.undo(); + }); + + const listItems = getListItems(list); + + // then + expect(listItems.length).to.eql(originalItems.length); + }) + ); + + }); + + + describe('entry.key', function() { + + it('should NOT display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-1-mapEntry-0-key"]', group); + + // then + expect(input).to.not.exist; + })); + + + it('should display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-0-mapEntry-0-key"]', group); + + const entry = getEntry(getInputParameter(serviceTask, 0), 0); + + // then + expect(input.value).to.eql(entry.get('key')); + })); + + + it('should update', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-0-mapEntry-0-key"]', group); + + // when + changeInput(input, 'newValue'); + + const entry = getEntry(getInputParameter(serviceTask, 0), 0); + + // then + expect(entry.get('key')).to.eql('newValue'); + })); + + + it('should update on external change', + inject(async function(elementRegistry, selection, commandStack) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + const entry = getEntry(getInputParameter(serviceTask, 0), 0); + const originalValue = entry.get('key'); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-0-mapEntry-0-key"]', group); + changeInput(input, 'newValue'); + + // when + await act(() => { + commandStack.undo(); + }); + + // then + expect(input.value).to.eql(originalValue); + }) + ); + + }); + + + describe('entry.value', function() { + + it('should NOT display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-1-mapEntry-0-value"]', group); + + // then + expect(input).to.not.exist; + })); + + + it('should display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-0-mapEntry-0-value"]', group); + + const entry = getEntry(getInputParameter(serviceTask, 0), 0); + + // then + expect(input.value).to.eql(entry.get('value')); + })); + + + it('should display as disabled - script', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-2-mapEntry-0-value"]', group); + + // then + expect(input.value).to.eql('Script'); + expect(input.disabled).to.be.true; + })); + + + it('should display as disabled - list', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-2-mapEntry-1-value"]', group); + + // then + expect(input.value).to.eql('List'); + expect(input.disabled).to.be.true; + })); + + + it('should display as disabled - map', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-2-mapEntry-2-value"]', group); + + // then + expect(input.value).to.eql('Map'); + expect(input.disabled).to.be.true; + })); + + + it('should update', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-0-mapEntry-0-value"]', group); + + // when + changeInput(input, 'newValue'); + + const entry = getEntry(getInputParameter(serviceTask, 0), 0); + + // then + expect(entry.get('value')).to.eql('newValue'); + })); + + + it('should update on external change', + inject(async function(elementRegistry, selection, commandStack) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + const entry = getEntry(getInputParameter(serviceTask, 0), 0); + const originalValue = entry.get('value'); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Input'); + const input = domQuery('input[name="ServiceTask_1-inputParameter-0-mapEntry-0-value"]', group); + changeInput(input, 'newValue'); + + // when + await act(() => { + commandStack.undo(); + }); + + // then + expect(input.value).to.eql(originalValue); + }) + ); + + }); + + }); + +}); + + +// helper ////////////////// + +function getGroup(container, id) { + return domQuery(`[data-group-id="group-${id}"`, container); +} + +function getInputParameter(element, idx) { + return (getInputParameters(element) || [])[idx]; +} + +function getEntries(parameter) { + return parameter.get('definition').get('entries'); +} + +function getEntry(parameter, idx) { + return (getEntries(parameter) || [])[idx]; +} + +function getListItems(container) { + return domQueryAll('.bio-properties-panel-list-entry-item', container); +} \ No newline at end of file diff --git a/test/spec/provider/camunda-platform/OutputProps.bpmn b/test/spec/provider/camunda-platform/OutputProps.bpmn new file mode 100644 index 00000000..9f7c194f --- /dev/null +++ b/test/spec/provider/camunda-platform/OutputProps.bpmn @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/provider/camunda-platform/OutputProps.spec.js b/test/spec/provider/camunda-platform/OutputProps.spec.js new file mode 100644 index 00000000..1ef3af74 --- /dev/null +++ b/test/spec/provider/camunda-platform/OutputProps.spec.js @@ -0,0 +1,229 @@ +import TestContainer from 'mocha-test-container-support'; + +import { + act +} from '@testing-library/preact'; + +import { + bootstrapPropertiesPanel, + inject +} from 'test/TestHelper'; + +import { + query as domQuery, + queryAll as domQueryAll +} from 'min-dom'; + +import { + getBusinessObject +} from 'bpmn-js/lib/util/ModelUtil'; + +import CoreModule from 'bpmn-js/lib/core'; +import SelectionModule from 'diagram-js/lib/features/selection'; +import ModelingModule from 'bpmn-js/lib/features/modeling'; + +import BpmnPropertiesPanel from 'src/render'; + +import CamundaPlatformPropertiesProvider from 'src/provider/camunda-platform'; + +import camundaModdleExtensions from 'camunda-bpmn-moddle/resources/camunda.json'; + +import { + getOutputParameters +} from 'src/provider/camunda-platform/utils/InputOutputUtil'; + +import diagramXML from './OutputProps.bpmn'; + + +describe('provider/camunda-platform - OutputProps', function() { + + const testModules = [ + CoreModule, SelectionModule, ModelingModule, + BpmnPropertiesPanel, + CamundaPlatformPropertiesProvider + ]; + + const moddleExtensions = { + camunda: camundaModdleExtensions + }; + + let container; + + beforeEach(function() { + container = TestContainer.get(this); + }); + + beforeEach(bootstrapPropertiesPanel(diagramXML, { + modules: testModules, + moddleExtensions, + debounceInput: false + })); + + + describe('bpmn:ServiceTask#outputParameters', function() { + + it('should display', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + // when + const group = getGroup(container, 'CamundaPlatform__Output'); + const listItems = getOutputListItems(group); + + // then + expect(group).to.exist; + expect(listItems.length).to.equal(getOutputParameters(serviceTask).length); + })); + + + it('should add new output parameter', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Output'); + const addEntry = domQuery('.bio-properties-panel-add-entry', group); + + // when + await act(() => { + addEntry.parentNode.click(); + }); + + // then + expect(getOutputParameters(serviceTask)).to.have.length(5); + })); + + + it('should create non existing extension elements', + inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_empty'); + + await act(() => { + selection.select(serviceTask); + }); + + // assume + expect(getBusinessObject(serviceTask).get('extensionElements')).not.to.exist; + + const group = getGroup(container, 'CamundaPlatform__Output'); + const addEntry = domQuery('.bio-properties-panel-add-entry', group); + + // when + await act(() => { + addEntry.parentNode.click(); + }); + + // then + expect(getBusinessObject(serviceTask).get('extensionElements')).to.exist; + expect(getOutputParameters(serviceTask)).to.have.length(1); + }) + ); + + + it('should re-use existing extensionElements', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_withExtensionElements'); + + await act(() => { + selection.select(serviceTask); + }); + + // assume + expect(getBusinessObject(serviceTask).get('extensionElements')).to.exist; + + const group = getGroup(container, 'CamundaPlatform__Output'); + const addEntry = domQuery('.bio-properties-panel-add-entry', group); + + // when + await act(() => { + addEntry.parentNode.click(); + }); + + // then + expect(getBusinessObject(serviceTask).get('extensionElements')).to.exist; + expect(getOutputParameters(serviceTask)).to.have.length(1); + })); + + + it('should delete output parameter', inject(async function(elementRegistry, selection) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Output'); + const listItems = getOutputListItems(group); + const removeEntry = domQuery('.bio-properties-panel-remove-entry', listItems[0]); + + // when + await act(() => { + removeEntry.parentNode.click(); + }); + + // then + expect(getOutputParameters(serviceTask)).to.have.length(3); + })); + + + it('should update on external change', + inject(async function(elementRegistry, selection, commandStack) { + + // given + const serviceTask = elementRegistry.get('ServiceTask_1'); + const originalParameters = getOutputParameters(serviceTask); + + await act(() => { + selection.select(serviceTask); + }); + + const group = getGroup(container, 'CamundaPlatform__Output'); + const addEntry = domQuery('.bio-properties-panel-add-entry', group); + await act(() => { + addEntry.parentNode.click(); + }); + + // when + await act(() => { + commandStack.undo(); + }); + + const listItems = getOutputListItems(group); + + // then + expect(listItems.length).to.eql(originalParameters.length); + }) + ); + + }); + +}); + + +// helper ////////////////// + +function getGroup(container, id) { + return domQuery(`[data-group-id="group-${id}"`, container); +} + +function getListItems(container, type) { + return domQueryAll(`div[data-entry-id*="-${type}-"].bio-properties-panel-collapsible-entry`, container); +} + +function getOutputListItems(container) { + return getListItems(container, 'outputParameter'); +} \ No newline at end of file