From 85c8b206ee7b929a44e2085cbcd37f2fc6f5f472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Souchet=20C=C3=A9line?= <4921914+csouchet@users.noreply.github.com> Date: Fri, 13 Oct 2023 08:23:11 +0200 Subject: [PATCH] feat: add more data in the internal model for the link events (#2924) Add `sourceIds` & `targetId` properties in the internal model for the link events. In the BPMN specification, the link event definition can refer to another link event definition as either a source or a target. Since the user of the library doesn't have this information, we internally calculate the ID of the event associated with this other link event definition to set as either source or target in `ShapeBpmnSemantic`. --------- Co-authored-by: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> --- .../images/architecture/internal-model.drawio | 744 +++++++++++++++++- .../images/architecture/internal-model.svg | 4 +- .../parser/json/converter/ProcessConverter.ts | 65 +- .../bpmn/internal/shape/ShapeBpmnElement.ts | 22 + src/model/bpmn/internal/shape/utils.ts | 12 + ...rser.event.link.with.source.target.test.ts | 143 ++++ test/unit/helpers/bpmn-model-expect.ts | 30 +- 7 files changed, 1005 insertions(+), 15 deletions(-) create mode 100644 test/unit/component/parser/json/BpmnJsonParser.event.link.with.source.target.test.ts diff --git a/docs/users/architecture/images/architecture/internal-model.drawio b/docs/users/architecture/images/architecture/internal-model.drawio index e8cd23bce7..ea4797d26f 100644 --- a/docs/users/architecture/images/architecture/internal-model.drawio +++ b/docs/users/architecture/images/architecture/internal-model.drawio @@ -1 +1,743 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/users/architecture/images/architecture/internal-model.svg b/docs/users/architecture/images/architecture/internal-model.svg index 01e4013cf7..ccd2505b61 100644 --- a/docs/users/architecture/images/architecture/internal-model.svg +++ b/docs/users/architecture/images/architecture/internal-model.svg @@ -1,4 +1,4 @@ - + -
0..1
0..1
0..1
0..1
0..1
0..1
2..*
2..*
1
1
1
1
1
1
1
1
1..*
1..*
1
1
0
0
0..*
0..*
Extends
Extends
1
1
0..1
0..1
EdgeShape- id: string- bpmnElement: ShapeBpmnElement- bounds: Bounds- label: Label- isHorizontal?: boolean- isMarkerVisible?: boolean- isMessageVisible?: boolean- extensions: ShapeExtensionsShapes- flowNodes: Shape[]- lanes: Shape[]- pools: Shape[]
Extends
Extends
Extends
Extends
1
1
Extends
Extends
1
1
0
0
Extends
Extends
Extends
Extends
Participant- name?: string- id: string- processRef?: stringShapeBpmnElement- id?: string- name?: string- kind: ShapeBpmnElementKind- parentId?: string- instantiate?: boolean = false- incomingIds?: string[] = []- outgoingIds?: string[] = []Definitions- collaboration?: Collaboration- process?: Process- bpmnModel: BpmnModel+ bpmnModel(): BpmnModelCollaborationProcessBpmnModel- edges: Edge[]Label- font?: Font- bounds?: Bounds- extensions: LabelExtensionsFont- name?: string- size?: number- isBold?: boolean- isItalic?: boolean- isUnderline?: boolean- isStrikeThrough?: booleanBounds- x: number- y: number- width: number- height: numberFlow- id: string- name: string- sourceRefId: string- targetRefId: string- kind: FlowKindSequenceFlow- sequenceFlowKind: SequenceFlowKindSequenceFlowKindMessageVisibleKindWaypoint- x: number- y: number
1
1
1
1
FlowKindAssociationFlow
Extends
Extends
MessageFlow
1
1
AssociationDirectionKind
0..3
0..3
ShapeBpmnEvent- eventDefinitionKind: ShapeBpmnEventDefinitionKindShapeBpmnEventDefinitionKindShapeBpmnMarkerKindShapeBpmnSubProcess- subProcessKind: ShapeBpmnSubProcessKind
Extends
Extends
ShapeBpmnActivity- markers?: ShapeBpmnMarkerKind[] = []ShapeBpmnCallActivity- callActivityKind: ShapeBpmnCallActivityKind- globalTaskKind?: GlobalTaskKindShapeBpmnElementKindShapeBpmnCallActivityKind
1
1
ShapeBpmnBoundaryEvent- isInterrupting?: boolean = trueShapeBpmnStartEvent- isInterrupting?: boolean
Extends
Extends
ShapeBpmnEventBasedGateway- gatewayKind?: ShapeBpmnEventBasedGatewayKindShapeBpmnEventBasedGatewayKind
1
1
Extends
Extends
Extends
Extends
ShapeBpmnSubProcessKind- extensions: EdgeExtensions- id: string- bpmnElement: SequenceFlow- waypoints: Waypoint[]- label: Label- messageVisibleKind: MessageVisibleKind = MessageVisibleKind.NONE
Text is not SVG - cannot display
\ No newline at end of file +
0..1
0..1
0..1
0..1
0..1
0..1
2..*
2..*
1
1
1
1
1
1
1
1
1..*
1..*
1
1
0
0
0..*
0..*
Extends
Extends
1
1
0..1
0..1
EdgeShape- id: string- bpmnElement: ShapeBpmnElement- bounds: Bounds- label: Label- isHorizontal?: boolean- isMarkerVisible?: boolean- isMessageVisible?: boolean- extensions: ShapeExtensionsShapes- flowNodes: Shape[]- lanes: Shape[]- pools: Shape[]
Extends
Extends
Extends
Extends
1
1
Extends
Extends
1
1
0
0
Extends
Extends
Extends
Extends
Participant- name?: string- id: string- processRef?: stringShapeBpmnElement- id?: string- name?: string- kind: ShapeBpmnElementKind- parentId?: string- instantiate?: boolean = false- incomingIds?: string[] = []- outgoingIds?: string[] = []Definitions- collaboration?: Collaboration- process?: Process- bpmnModel: BpmnModel+ bpmnModel(): BpmnModelCollaborationProcessBpmnModel- edges: Edge[]Label- font?: Font- bounds?: Bounds- extensions: LabelExtensionsFont- name?: string- size?: number- isBold?: boolean- isItalic?: boolean- isUnderline?: boolean- isStrikeThrough?: booleanBounds- x: number- y: number- width: number- height: numberFlow- id: string- name: string- sourceRefId: string- targetRefId: string- kind: FlowKindSequenceFlow- sequenceFlowKind: SequenceFlowKindSequenceFlowKindMessageVisibleKindWaypoint- x: number- y: number
1
1
1
1
FlowKindAssociationFlow
Extends
Extends
MessageFlow
1
1
AssociationDirectionKind
0..3
0..3
ShapeBpmnEvent- eventDefinitionKind: ShapeBpmnEventDefinitionKindShapeBpmnEventDefinitionKindShapeBpmnMarkerKindShapeBpmnSubProcess- subProcessKind: ShapeBpmnSubProcessKind
Extends
Extends
ShapeBpmnActivity- markers?: ShapeBpmnMarkerKind[] = []ShapeBpmnCallActivity- callActivityKind: ShapeBpmnCallActivityKind- globalTaskKind?: GlobalTaskKindShapeBpmnElementKindShapeBpmnCallActivityKind
1
1
ShapeBpmnBoundaryEvent- isInterrupting?: boolean = trueShapeBpmnStartEvent- isInterrupting?: boolean
Extends
Extends
ShapeBpmnEventBasedGateway- gatewayKind?: ShapeBpmnEventBasedGatewayKindShapeBpmnEventBasedGatewayKind
1
1
Extends
Extends
Extends
Extends
ShapeBpmnSubProcessKind- extensions: EdgeExtensions- id: string- bpmnElement: SequenceFlow- waypoints: Waypoint[]- label: Label- messageVisibleKind: MessageVisibleKind = MessageVisibleKind.NONEShapeBpmnIntermediateCatchEvent- sourceIds?: string[] = []ShapeBpmnIntermediateThrowEvent- targetId?: string
Text is not SVG - cannot display
\ No newline at end of file diff --git a/src/component/parser/json/converter/ProcessConverter.ts b/src/component/parser/json/converter/ProcessConverter.ts index b08c523fe1..b6d85bb1ff 100644 --- a/src/component/parser/json/converter/ProcessConverter.ts +++ b/src/component/parser/json/converter/ProcessConverter.ts @@ -38,6 +38,8 @@ import { } from '../../../../model/bpmn/internal'; import { AssociationFlow, SequenceFlow } from '../../../../model/bpmn/internal/edge/flows'; import ShapeBpmnElement, { + ShapeBpmnIntermediateThrowEvent, + ShapeBpmnIntermediateCatchEvent, ShapeBpmnActivity, ShapeBpmnBoundaryEvent, ShapeBpmnCallActivity, @@ -90,6 +92,7 @@ export default class ProcessConverter { private defaultSequenceFlowIds: string[] = []; private elementsWithoutParentByProcessId = new Map(); private callActivitiesCallingProcess = new Map(); + private eventsByLinkEventDefinition = new Map(); constructor( private convertedElements: ConvertedElements, @@ -103,6 +106,7 @@ export default class ProcessConverter { for (const process of ensureIsArray(processes)) this.assignParentOfProcessElementsCalledByCallActivity(process.id); this.assignIncomingAndOutgoingIdsFromFlows(); + this.assignSourceAndTargetIdsToLinkEvents(); } private assignParentOfProcessElementsCalledByCallActivity(processId: string): void { @@ -139,6 +143,23 @@ export default class ProcessConverter { } } + private assignSourceAndTargetIdsToLinkEvents(): void { + const linkEventDefinitions = [...this.eventsByLinkEventDefinition.entries()].filter(([targetEventDefinition]) => targetEventDefinition.id); + + for (const [eventDefinition, bpmnEvent] of this.eventsByLinkEventDefinition) { + if (bpmnEvent instanceof ShapeBpmnIntermediateThrowEvent) { + const target = linkEventDefinitions.find(([targetEventDefinition]) => eventDefinition.target === targetEventDefinition.id); + bpmnEvent.targetId = target?.[1]?.id; + } else if (bpmnEvent instanceof ShapeBpmnIntermediateCatchEvent) { + bpmnEvent.sourceIds = linkEventDefinitions + .filter(([sourceEventDefinition]) => + Array.isArray(eventDefinition.source) ? eventDefinition.source.includes(sourceEventDefinition.id) : eventDefinition.source === sourceEventDefinition.id, + ) + .map(([, sourceEvent]) => sourceEvent.id); + } + } + } + private parseProcess(process: TProcess): void { const processReference = process.id; const pool = this.convertedElements.findPoolByProcessRef(processReference); @@ -251,15 +272,45 @@ export default class ProcessConverter { } if (numberOfEventDefinitions == 1) { - const eventDefinitionKind = [...eventDefinitionsByKind.keys()][0]; - if (ShapeUtil.isBoundaryEvent(elementKind)) { - return this.buildShapeBpmnBoundaryEvent(bpmnElement as TBoundaryEvent, eventDefinitionKind); - } - if (ShapeUtil.isStartEvent(elementKind)) { - return new ShapeBpmnStartEvent(bpmnElement.id, bpmnElement.name, eventDefinitionKind, parentId, bpmnElement.isInterrupting); + const [eventDefinitionKind, eventDefinitions] = [...eventDefinitionsByKind.entries()][0]; + + const bpmnEvent = ShapeUtil.isCatchEvent(elementKind) + ? this.buildShapeBpmnCatchEvent(bpmnElement as TCatchEvent, elementKind, eventDefinitionKind, parentId) + : this.buildShapeBpmnThrowEvent(bpmnElement as TThrowEvent, elementKind, eventDefinitionKind, parentId); + + if (eventDefinitionKind === ShapeBpmnEventDefinitionKind.LINK && (eventDefinitions[0].id || eventDefinitions[0].target || eventDefinitions[0].source)) { + this.eventsByLinkEventDefinition.set(eventDefinitions[0], bpmnEvent); } - return new ShapeBpmnEvent(bpmnElement.id, bpmnElement.name, elementKind, eventDefinitionKind, parentId); + + return bpmnEvent; + } + } + + private buildShapeBpmnCatchEvent( + bpmnElement: TCatchEvent, + elementKind: BpmnEventKind, + eventDefinitionKind: ShapeBpmnEventDefinitionKind, + parentId: string, + ): ShapeBpmnIntermediateCatchEvent | ShapeBpmnStartEvent | ShapeBpmnBoundaryEvent { + if (ShapeUtil.isBoundaryEvent(elementKind)) { + return this.buildShapeBpmnBoundaryEvent(bpmnElement as TBoundaryEvent, eventDefinitionKind); + } + if (ShapeUtil.isStartEvent(elementKind)) { + return new ShapeBpmnStartEvent(bpmnElement.id, bpmnElement.name, eventDefinitionKind, parentId, bpmnElement.isInterrupting); + } + return new ShapeBpmnIntermediateCatchEvent(bpmnElement.id, bpmnElement.name, eventDefinitionKind, parentId); + } + + private buildShapeBpmnThrowEvent( + bpmnElement: TThrowEvent, + elementKind: BpmnEventKind, + eventDefinitionKind: ShapeBpmnEventDefinitionKind, + parentId: string, + ): ShapeBpmnIntermediateThrowEvent | ShapeBpmnEvent { + if (ShapeUtil.isIntermediateThrowEvent(elementKind)) { + return new ShapeBpmnIntermediateThrowEvent(bpmnElement.id, bpmnElement.name, eventDefinitionKind, parentId); } + return new ShapeBpmnEvent(bpmnElement.id, bpmnElement.name, elementKind, eventDefinitionKind, parentId); } private buildShapeBpmnBoundaryEvent(bpmnElement: TBoundaryEvent, eventDefinitionKind: ShapeBpmnEventDefinitionKind): ShapeBpmnBoundaryEvent { diff --git a/src/model/bpmn/internal/shape/ShapeBpmnElement.ts b/src/model/bpmn/internal/shape/ShapeBpmnElement.ts index 5174d1a097..94a0a2b6a4 100644 --- a/src/model/bpmn/internal/shape/ShapeBpmnElement.ts +++ b/src/model/bpmn/internal/shape/ShapeBpmnElement.ts @@ -97,6 +97,28 @@ export class ShapeBpmnEvent extends ShapeBpmnElement { } } +/** + * @internal + */ +export class ShapeBpmnIntermediateCatchEvent extends ShapeBpmnEvent { + sourceIds?: string[] = []; + + constructor(id: string, name: string, eventDefinitionKind: ShapeBpmnEventDefinitionKind, parentId: string) { + super(id, name, ShapeBpmnElementKind.EVENT_INTERMEDIATE_CATCH, eventDefinitionKind, parentId); + } +} + +/** + * @internal + */ +export class ShapeBpmnIntermediateThrowEvent extends ShapeBpmnEvent { + targetId?: string; + + constructor(id: string, name: string, eventDefinitionKind: ShapeBpmnEventDefinitionKind, parentId: string) { + super(id, name, ShapeBpmnElementKind.EVENT_INTERMEDIATE_THROW, eventDefinitionKind, parentId); + } +} + /** * @internal */ diff --git a/src/model/bpmn/internal/shape/utils.ts b/src/model/bpmn/internal/shape/utils.ts index 673bbc63ef..2d782df457 100644 --- a/src/model/bpmn/internal/shape/utils.ts +++ b/src/model/bpmn/internal/shape/utils.ts @@ -45,6 +45,18 @@ export class ShapeUtil { return ShapeBpmnElementKind.EVENT_START === kind; } + static isCatchEvent(kind: ShapeBpmnElementKind): boolean { + return ShapeBpmnElementKind.EVENT_INTERMEDIATE_CATCH === kind || ShapeBpmnElementKind.EVENT_BOUNDARY === kind || ShapeBpmnElementKind.EVENT_START === kind; + } + + static isIntermediateCatchEvent(kind: ShapeBpmnElementKind): boolean { + return ShapeBpmnElementKind.EVENT_INTERMEDIATE_CATCH === kind; + } + + static isIntermediateThrowEvent(kind: ShapeBpmnElementKind): boolean { + return ShapeBpmnElementKind.EVENT_INTERMEDIATE_THROW === kind; + } + static isCallActivity(kind: ShapeBpmnElementKind): boolean { return ShapeBpmnElementKind.CALL_ACTIVITY === kind; } diff --git a/test/unit/component/parser/json/BpmnJsonParser.event.link.with.source.target.test.ts b/test/unit/component/parser/json/BpmnJsonParser.event.link.with.source.target.test.ts new file mode 100644 index 0000000000..27903c0201 --- /dev/null +++ b/test/unit/component/parser/json/BpmnJsonParser.event.link.with.source.target.test.ts @@ -0,0 +1,143 @@ +/* +Copyright 2023 Bonitasoft S.A. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { verifyShape } from '../../../helpers/bpmn-model-expect'; +import { buildDefinitions, EventDefinitionOn } from '../../../helpers/JsonBuilder'; +import { parseJsonAndExpectOnlyFlowNodes } from '../../../helpers/JsonTestUtils'; +import { expectedBounds } from '../../../helpers/TestUtils.BpmnJsonParser.event'; + +import { ShapeBpmnElementKind, ShapeBpmnEventDefinitionKind } from '@lib/model/bpmn/internal'; + +describe('parse bpmn as json for link events', () => { + describe.each([ + ['event with eventDefinitionRef attribute', EventDefinitionOn.DEFINITIONS], + ['event with eventDefinition object', EventDefinitionOn.EVENT], + ] as [string, EventDefinitionOn][])(`%s`, (titleSuffix: string, eventDefinitionOn: EventDefinitionOn) => { + it(`should convert with one source and one target, ${titleSuffix}`, () => { + const json = buildDefinitions({ + process: { + event: [ + { + id: 'source_id', + bpmnKind: 'intermediateThrowEvent', + eventDefinitionParameter: { + eventDefinitionKind: 'link', + eventDefinitionOn, + target: 'link_target_id', // event definition id of 'target_id' + }, + }, + { + id: 'target_id', + bpmnKind: 'intermediateCatchEvent', + eventDefinitionParameter: { + eventDefinitionKind: 'link', + eventDefinitionOn, + source: 'link_source_id', // event definition id of 'source_id' + }, + }, + ], + }, + }); + + const model = parseJsonAndExpectOnlyFlowNodes(json, 2); + + verifyShape(model.flowNodes[0], { + shapeId: `shape_source_id`, + bpmnElementId: `source_id`, + bounds: expectedBounds, + bpmnElementKind: ShapeBpmnElementKind.EVENT_INTERMEDIATE_THROW, + bpmnElementName: undefined, + eventDefinitionKind: ShapeBpmnEventDefinitionKind.LINK, + targetId: 'target_id', + }); + verifyShape(model.flowNodes[1], { + shapeId: `shape_target_id`, + bpmnElementId: `target_id`, + bounds: expectedBounds, + bpmnElementKind: ShapeBpmnElementKind.EVENT_INTERMEDIATE_CATCH, + bpmnElementName: undefined, + eventDefinitionKind: ShapeBpmnEventDefinitionKind.LINK, + sourceIds: ['source_id'], + }); + }); + + it(`should convert with several sources, ${titleSuffix}`, () => { + const json = buildDefinitions({ + process: { + event: [ + { + id: 'source_1_id', + bpmnKind: 'intermediateThrowEvent', + eventDefinitionParameter: { + eventDefinitionKind: 'link', + eventDefinitionOn, + target: 'link_target_id', // event definition id of 'target_id' + }, + }, + { + id: 'source_2_id', + bpmnKind: 'intermediateThrowEvent', + eventDefinitionParameter: { + eventDefinitionKind: 'link', + eventDefinitionOn, + target: 'link_target_id', // event definition id of 'target_id' + }, + }, + { + id: 'target_id', + bpmnKind: 'intermediateCatchEvent', + eventDefinitionParameter: { + eventDefinitionKind: 'link', + eventDefinitionOn, + source: ['link_source_1_id', 'link_source_2_id'], // event definition id of 'source_1_id' & 'source_2_id' + }, + }, + ], + }, + }); + + const model = parseJsonAndExpectOnlyFlowNodes(json, 3); + + verifyShape(model.flowNodes[0], { + shapeId: `shape_source_1_id`, + bpmnElementId: `source_1_id`, + bounds: expectedBounds, + bpmnElementKind: ShapeBpmnElementKind.EVENT_INTERMEDIATE_THROW, + bpmnElementName: undefined, + eventDefinitionKind: ShapeBpmnEventDefinitionKind.LINK, + targetId: 'target_id', + }); + verifyShape(model.flowNodes[1], { + shapeId: `shape_source_2_id`, + bpmnElementId: `source_2_id`, + bounds: expectedBounds, + bpmnElementKind: ShapeBpmnElementKind.EVENT_INTERMEDIATE_THROW, + bpmnElementName: undefined, + eventDefinitionKind: ShapeBpmnEventDefinitionKind.LINK, + targetId: 'target_id', + }); + verifyShape(model.flowNodes[2], { + shapeId: `shape_target_id`, + bpmnElementId: `target_id`, + bounds: expectedBounds, + bpmnElementKind: ShapeBpmnElementKind.EVENT_INTERMEDIATE_CATCH, + bpmnElementName: undefined, + eventDefinitionKind: ShapeBpmnEventDefinitionKind.LINK, + sourceIds: ['source_1_id', 'source_2_id'], + }); + }); + }); +}); diff --git a/test/unit/helpers/bpmn-model-expect.ts b/test/unit/helpers/bpmn-model-expect.ts index 684a313286..4cc4f842c4 100644 --- a/test/unit/helpers/bpmn-model-expect.ts +++ b/test/unit/helpers/bpmn-model-expect.ts @@ -14,14 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import type { GlobalTaskKind, ShapeBpmnCallActivityKind, ShapeBpmnElementKind, ShapeBpmnEventDefinitionKind } from '@lib/model/bpmn/internal'; +import type { GlobalTaskKind, ShapeBpmnCallActivityKind, ShapeBpmnElementKind } from '@lib/model/bpmn/internal'; import type BpmnModel from '@lib/model/bpmn/internal/BpmnModel'; import type { Edge, Waypoint } from '@lib/model/bpmn/internal/edge/edge'; import type Shape from '@lib/model/bpmn/internal/shape/Shape'; +import type { ShapeBpmnIntermediateCatchEvent, ShapeBpmnIntermediateThrowEvent } from '@lib/model/bpmn/internal/shape/ShapeBpmnElement'; import type { EdgeExtensions, LabelExtensions, ShapeExtensions } from '@lib/model/bpmn/internal/types'; import type { TProcess } from '@lib/model/bpmn/json/baseElement/rootElement/rootElement'; -import { FlowKind, MessageVisibleKind, SequenceFlowKind, ShapeBpmnMarkerKind, ShapeBpmnSubProcessKind } from '@lib/model/bpmn/internal'; +import { FlowKind, MessageVisibleKind, SequenceFlowKind, ShapeBpmnMarkerKind, ShapeBpmnSubProcessKind, ShapeBpmnEventDefinitionKind, ShapeUtil } from '@lib/model/bpmn/internal'; import { SequenceFlow } from '@lib/model/bpmn/internal/edge/flows'; import { ShapeBpmnActivity, ShapeBpmnBoundaryEvent, ShapeBpmnCallActivity, ShapeBpmnEvent } from '@lib/model/bpmn/internal/shape/ShapeBpmnElement'; @@ -51,7 +52,15 @@ export interface ExpectedEventShape extends ExpectedShape { eventDefinitionKind: ShapeBpmnEventDefinitionKind; } -export interface ExpectedBoundaryEventShape extends ExpectedEventShape { +export interface ExpectedCatchEventShape extends ExpectedEventShape { + sourceIds?: string[]; +} + +export interface ExpectedThrowEventShape extends ExpectedEventShape { + targetId?: string; +} + +export interface ExpectedBoundaryEventShape extends ExpectedCatchEventShape { isInterrupting?: boolean; } @@ -94,7 +103,7 @@ export interface ExpectedBounds { export const verifyShape = ( shape: Shape, - expectedShape: ExpectedShape | ExpectedActivityShape | ExpectedCallActivityShape | ExpectedEventShape | ExpectedBoundaryEventShape, + expectedShape: ExpectedShape | ExpectedActivityShape | ExpectedCallActivityShape | ExpectedCatchEventShape | ExpectedThrowEventShape | ExpectedBoundaryEventShape, ): void => { expect(shape.id).toEqual(expectedShape.shapeId); expect(shape.isHorizontal).toEqual(expectedShape.isHorizontal); @@ -123,7 +132,18 @@ export const verifyShape = ( if ('eventDefinitionKind' in expectedShape) { expect(bpmnElement instanceof ShapeBpmnEvent).toBeTruthy(); - expect((bpmnElement as ShapeBpmnEvent).eventDefinitionKind).toEqual((expectedShape as ExpectedEventShape).eventDefinitionKind); + + const expectedEvent = expectedShape as ExpectedEventShape; + const event = bpmnElement as ShapeBpmnEvent; + expect(event.eventDefinitionKind).toEqual(expectedEvent.eventDefinitionKind); + + if (expectedEvent.eventDefinitionKind === ShapeBpmnEventDefinitionKind.LINK) { + if (ShapeUtil.isIntermediateCatchEvent(expectedShape.bpmnElementKind)) { + expect((event as ShapeBpmnIntermediateCatchEvent).sourceIds).toEqual((expectedEvent as ExpectedCatchEventShape).sourceIds ?? []); + } else { + expect((event as ShapeBpmnIntermediateThrowEvent).targetId).toEqual((expectedEvent as ExpectedThrowEventShape).targetId); + } + } } if ('isInterrupting' in expectedShape) {