diff --git a/src/api/public/api.ts b/src/api/public/api.ts index 8898c75eca..e1c136e185 100644 --- a/src/api/public/api.ts +++ b/src/api/public/api.ts @@ -20,4 +20,5 @@ export { BpmnVisualization }; export { GlobalOptions, NavigationConfiguration, FitOptions, FitType, LoadOptions, ZoomConfiguration } from '../../component/options'; // Interaction -export { BpmnElementsRegistry, BpmnSemantic, BpmnElement } from '../../component/registry'; +export { BpmnElement, BpmnElementsRegistry, BpmnSemantic } from '../../component/registry'; +export * from '../../model/bpmn/internal/api'; diff --git a/src/component/BpmnVisualization.ts b/src/component/BpmnVisualization.ts index 0ba614cfcc..a3a15c521f 100644 --- a/src/component/BpmnVisualization.ts +++ b/src/component/BpmnVisualization.ts @@ -20,6 +20,7 @@ import { BpmnMxGraph } from './mxgraph/BpmnMxGraph'; import { FitOptions, GlobalOptions, LoadOptions } from './options'; import { BpmnElementsRegistry } from './registry'; import { newBpmnElementsRegistry } from './registry/bpmn-elements-registry'; +import { BpmnModelRegistry } from './registry/bpmn-model-registry'; import { htmlElement } from './helpers/dom-utils'; /** @@ -32,19 +33,22 @@ export default class BpmnVisualization { * @experimental subject to change, feedback welcome */ readonly bpmnElementsRegistry: BpmnElementsRegistry; + private readonly _bpmnModelRegistry: BpmnModelRegistry; constructor(options: GlobalOptions) { // mxgraph configuration const configurator = new MxGraphConfigurator(htmlElement(options?.container)); this.graph = configurator.configure(options); // other configurations - this.bpmnElementsRegistry = newBpmnElementsRegistry(this.graph); + this._bpmnModelRegistry = new BpmnModelRegistry(); + this.bpmnElementsRegistry = newBpmnElementsRegistry(this._bpmnModelRegistry, this.graph); } public load(xml: string, options?: LoadOptions): void { try { const bpmnModel = newBpmnParser().parse(xml); - newMxGraphRenderer(this.graph).render(bpmnModel, options); + const renderedModel = this._bpmnModelRegistry.computeRenderedModel(bpmnModel); + newMxGraphRenderer(this.graph).render(renderedModel, options); } catch (e) { // TODO error handling window.alert(`Cannot load bpmn diagram: ${e.message}`); diff --git a/src/component/mxgraph/MxGraphRenderer.ts b/src/component/mxgraph/MxGraphRenderer.ts index 57293b16db..f6a53d7d43 100644 --- a/src/component/mxgraph/MxGraphRenderer.ts +++ b/src/component/mxgraph/MxGraphRenderer.ts @@ -15,8 +15,7 @@ */ import Shape from '../../model/bpmn/internal/shape/Shape'; import Edge from '../../model/bpmn/internal/edge/Edge'; -import BpmnModel from '../../model/bpmn/internal/BpmnModel'; -import ShapeBpmnElement, { ShapeBpmnSubProcess } from '../../model/bpmn/internal/shape/ShapeBpmnElement'; +import ShapeBpmnElement from '../../model/bpmn/internal/shape/ShapeBpmnElement'; import Waypoint from '../../model/bpmn/internal/edge/Waypoint'; import Bounds from '../../model/bpmn/internal/Bounds'; import ShapeUtil from '../../model/bpmn/internal/shape/ShapeUtil'; @@ -24,35 +23,33 @@ import CoordinatesTranslator from './renderer/CoordinatesTranslator'; import StyleConfigurator from './config/StyleConfigurator'; import { MessageFlow } from '../../model/bpmn/internal/edge/Flow'; import { MessageVisibleKind } from '../../model/bpmn/internal/edge/MessageVisibleKind'; -import { ShapeBpmnMarkerKind } from '../../model/bpmn/internal/shape'; import { BpmnMxGraph } from './BpmnMxGraph'; import { LoadOptions } from '../options'; +import { RenderedModel } from '../registry/bpmn-model-registry'; import { mxgraph } from './initializer'; import { mxCell } from 'mxgraph'; // for types export default class MxGraphRenderer { constructor(readonly graph: BpmnMxGraph, readonly coordinatesTranslator: CoordinatesTranslator, readonly styleConfigurator: StyleConfigurator) {} - public render(bpmnModel: BpmnModel, loadOptions?: LoadOptions): void { - this.insertShapesAndEdges(bpmnModel); + public render(renderedModel: RenderedModel, loadOptions?: LoadOptions): void { + this.insertShapesAndEdges(renderedModel); this.graph.customFit(loadOptions?.fit); } - private insertShapesAndEdges(bpmnModel: BpmnModel): void { - const displayedModel = toDisplayedModel(bpmnModel); - + private insertShapesAndEdges({ pools, lanes, subprocesses, otherFlowNodes, boundaryEvents, edges }: RenderedModel): void { const model = this.graph.getModel(); model.clear(); // ensure to remove manual changes or already loaded graphs model.beginUpdate(); try { - this.insertShapes(displayedModel.pools); - this.insertShapes(displayedModel.lanes); - this.insertShapes(displayedModel.subprocesses); - this.insertShapes(displayedModel.otherFlowNodes); + this.insertShapes(pools); + this.insertShapes(lanes); + this.insertShapes(subprocesses); + this.insertShapes(otherFlowNodes); // last shape as the boundary event parent must be in the model (subprocess or activity) - this.insertShapes(displayedModel.boundaryEvents); + this.insertShapes(boundaryEvents); // at last as edge source and target must be present in the model prior insertion, otherwise they are not rendered - this.insertEdges(displayedModel.edges); + this.insertEdges(edges); } finally { model.endUpdate(); } @@ -163,37 +160,3 @@ export default class MxGraphRenderer { export function newMxGraphRenderer(graph: BpmnMxGraph): MxGraphRenderer { return new MxGraphRenderer(graph, new CoordinatesTranslator(graph), new StyleConfigurator(graph)); } - -function toDisplayedModel(bpmnModel: BpmnModel): DisplayedModel { - const collapsedSubProcessIds: string[] = bpmnModel.flowNodes - .filter(shape => { - const bpmnElement = shape.bpmnElement; - return ShapeUtil.isSubProcess(bpmnElement?.kind) && (bpmnElement as ShapeBpmnSubProcess)?.markers.includes(ShapeBpmnMarkerKind.EXPAND); - }) - .map(shape => shape.bpmnElement?.id); - - const subprocesses: Shape[] = []; - const boundaryEvents: Shape[] = []; - const otherFlowNodes: Shape[] = []; - bpmnModel.flowNodes.forEach(shape => { - const kind = shape.bpmnElement?.kind; - if (ShapeUtil.isSubProcess(kind)) { - subprocesses.push(shape); - } else if (ShapeUtil.isBoundaryEvent(kind)) { - boundaryEvents.push(shape); - } else if (!collapsedSubProcessIds.includes(shape.bpmnElement?.parentId)) { - otherFlowNodes.push(shape); - } - }); - - return { boundaryEvents: boundaryEvents, edges: bpmnModel.edges, lanes: bpmnModel.lanes, otherFlowNodes: otherFlowNodes, pools: bpmnModel.pools, subprocesses: subprocesses }; -} - -interface DisplayedModel { - edges: Edge[]; - boundaryEvents: Shape[]; - otherFlowNodes: Shape[]; - lanes: Shape[]; - pools: Shape[]; - subprocesses: Shape[]; -} diff --git a/src/component/mxgraph/config/StyleConfigurator.ts b/src/component/mxgraph/config/StyleConfigurator.ts index ddb379c79f..edfd20aa49 100644 --- a/src/component/mxgraph/config/StyleConfigurator.ts +++ b/src/component/mxgraph/config/StyleConfigurator.ts @@ -286,7 +286,7 @@ export default class StyleConfigurator { } computeStyle(bpmnCell: Shape | Edge, labelBounds: Bounds): string { - const styles: string[] = [bpmnCell.bpmnElement?.kind as string]; + const styles: string[] = [bpmnCell.bpmnElement.kind as string]; let shapeStyleValues; if (bpmnCell instanceof Shape) { diff --git a/src/component/registry/bpmn-elements-registry.ts b/src/component/registry/bpmn-elements-registry.ts index 0729ea8244..333684e2c5 100644 --- a/src/component/registry/bpmn-elements-registry.ts +++ b/src/component/registry/bpmn-elements-registry.ts @@ -15,20 +15,16 @@ */ import { ensureIsArray } from '../helpers/array-utils'; import { BpmnMxGraph } from '../mxgraph/BpmnMxGraph'; -import { computeBpmnBaseClassName, extractBpmnKindFromStyle } from '../mxgraph/style-helper'; -import { FlowKind } from '../../model/bpmn/internal/edge/FlowKind'; -import { ShapeBpmnElementKind } from '../../model/bpmn/internal/shape'; +import { computeBpmnBaseClassName } from '../mxgraph/style-helper'; import { CssRegistry } from './css-registry'; import MxGraphCellUpdater from '../mxgraph/MxGraphCellUpdater'; import { BpmnQuerySelectors } from './query-selectors'; +import { BpmnElement } from './types'; +import { BpmnModelRegistry } from './bpmn-model-registry'; +import { BpmnElementKind } from '../../model/bpmn/internal/api'; -export function newBpmnElementsRegistry(graph: BpmnMxGraph): BpmnElementsRegistry { - return new BpmnElementsRegistry( - new BpmnModelRegistry(graph), - new HtmlElementRegistry(new BpmnQuerySelectors(graph.container?.id)), - new CssRegistry(), - new MxGraphCellUpdater(graph), - ); +export function newBpmnElementsRegistry(bpmnModelRegistry: BpmnModelRegistry, graph: BpmnMxGraph): BpmnElementsRegistry { + return new BpmnElementsRegistry(bpmnModelRegistry, new HtmlElementRegistry(new BpmnQuerySelectors(graph.container?.id)), new CssRegistry(), new MxGraphCellUpdater(graph)); } /** @@ -183,42 +179,6 @@ export class BpmnElementsRegistry { } } -export type BpmnElementKind = FlowKind | ShapeBpmnElementKind; - -/** - * @category Interaction - */ -export interface BpmnSemantic { - id: string; - name: string; - /** `true` when relates to a BPMN Shape, `false` when relates to a BPMN Edge. */ - isShape: boolean; - // TODO use a more 'type oriented' BpmnElementKind (as part of #929) - kind: string; -} - -/** - * @category Interaction - */ -export interface BpmnElement { - bpmnSemantic: BpmnSemantic; - htmlElement: HTMLElement; -} - -// for now, we don't store the BpmnModel so we can use it, information are only available in the mxgraph model -class BpmnModelRegistry { - constructor(private graph: BpmnMxGraph) {} - - getBpmnSemantic(bpmnElementId: string): BpmnSemantic | undefined { - const mxCell = this.graph.getModel().getCell(bpmnElementId); - if (mxCell == null) { - return undefined; - } - - return { id: bpmnElementId, name: mxCell.value, isShape: mxCell.isVertex(), kind: extractBpmnKindFromStyle(mxCell) }; - } -} - class HtmlElementRegistry { constructor(private selectors: BpmnQuerySelectors) {} diff --git a/src/component/registry/bpmn-model-registry.ts b/src/component/registry/bpmn-model-registry.ts new file mode 100644 index 0000000000..1090e9d0cd --- /dev/null +++ b/src/component/registry/bpmn-model-registry.ts @@ -0,0 +1,92 @@ +/** + * Copyright 2021 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 BpmnModel from '../../model/bpmn/internal/BpmnModel'; +import Shape from '../../model/bpmn/internal/shape/Shape'; +import Edge from '../../model/bpmn/internal/edge/Edge'; +import { BpmnSemantic } from './types'; +import ShapeUtil from '../../model/bpmn/internal/shape/ShapeUtil'; +import ShapeBpmnElement, { ShapeBpmnSubProcess } from '../../model/bpmn/internal/shape/ShapeBpmnElement'; +import { ShapeBpmnMarkerKind } from '../../model/bpmn/internal/shape'; + +export class BpmnModelRegistry { + private searchableModel: SearchableModel; + + computeRenderedModel(bpmnModel: BpmnModel): RenderedModel { + this.searchableModel = new SearchableModel(bpmnModel); + return toRenderedModel(bpmnModel); + } + + getBpmnSemantic(bpmnElementId: string): BpmnSemantic | undefined { + const element = this.searchableModel.elementById(bpmnElementId); + if (!element) { + return undefined; + } + const bpmnElement = element.bpmnElement; + const isShape = bpmnElement instanceof ShapeBpmnElement; + return { id: bpmnElementId, name: bpmnElement.name, isShape: isShape, kind: bpmnElement.kind }; + } +} + +function toRenderedModel(bpmnModel: BpmnModel): RenderedModel { + const collapsedSubProcessIds: string[] = bpmnModel.flowNodes + .filter(shape => { + const bpmnElement = shape.bpmnElement; + return ShapeUtil.isSubProcess(bpmnElement.kind) && (bpmnElement as ShapeBpmnSubProcess).markers.includes(ShapeBpmnMarkerKind.EXPAND); + }) + .map(shape => shape.bpmnElement.id); + + const subprocesses: Shape[] = []; + const boundaryEvents: Shape[] = []; + const otherFlowNodes: Shape[] = []; + bpmnModel.flowNodes.forEach(shape => { + const kind = shape.bpmnElement.kind; + if (ShapeUtil.isSubProcess(kind)) { + subprocesses.push(shape); + } else if (ShapeUtil.isBoundaryEvent(kind)) { + boundaryEvents.push(shape); + } else if (!collapsedSubProcessIds.includes(shape.bpmnElement.parentId)) { + otherFlowNodes.push(shape); + } + }); + + return { boundaryEvents: boundaryEvents, edges: bpmnModel.edges, lanes: bpmnModel.lanes, otherFlowNodes: otherFlowNodes, pools: bpmnModel.pools, subprocesses: subprocesses }; +} + +export interface RenderedModel { + edges: Edge[]; + boundaryEvents: Shape[]; + otherFlowNodes: Shape[]; + lanes: Shape[]; + pools: Shape[]; + subprocesses: Shape[]; +} + +class SearchableModel { + private elements: Map = new Map(); + + constructor(bpmnModel: BpmnModel) { + ([] as Array) + .concat(bpmnModel.pools, bpmnModel.lanes, bpmnModel.flowNodes, bpmnModel.edges) + // use the bpmn element id and not the bpmn shape id + .forEach(e => { + this.elements.set(e.bpmnElement.id, e); + }); + } + + elementById(id: string): Shape | Edge | undefined { + return this.elements.get(id); + } +} diff --git a/src/component/registry/index.ts b/src/component/registry/index.ts index 123c32a006..04478d4452 100644 --- a/src/component/registry/index.ts +++ b/src/component/registry/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ -export { BpmnElementsRegistry, BpmnElement, BpmnSemantic } from './bpmn-elements-registry'; +export { BpmnElementsRegistry } from './bpmn-elements-registry'; +export * from './types'; diff --git a/src/component/registry/types.ts b/src/component/registry/types.ts new file mode 100644 index 0000000000..085452aa66 --- /dev/null +++ b/src/component/registry/types.ts @@ -0,0 +1,36 @@ +/** + * Copyright 2021 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 { BpmnElementKind } from '../../model/bpmn/internal/api'; + +/** + * @category Interaction + */ +export interface BpmnSemantic { + id: string; + name: string; + /** `true` when relates to a BPMN Shape, `false` when relates to a BPMN Edge. */ + isShape: boolean; + kind: BpmnElementKind; +} + +/** + * @category Interaction + */ +export interface BpmnElement { + bpmnSemantic: BpmnSemantic; + htmlElement: HTMLElement; +} diff --git a/src/demo/index.ts b/src/demo/index.ts index 2e7f94760b..91b4d9b139 100644 --- a/src/demo/index.ts +++ b/src/demo/index.ts @@ -17,7 +17,8 @@ import BpmnVisualization from '../component/BpmnVisualization'; import { GlobalOptions, FitOptions, FitType, LoadOptions } from '../component/options'; import { log, logStartup } from './helper'; import { DropFileUserInterface } from './component/DropFileUserInterface'; -import { BpmnElement, BpmnElementKind } from '../component/registry/bpmn-elements-registry'; +import { BpmnElement } from '../component/registry'; +import { BpmnElementKind } from '../model/bpmn/internal/api'; export * from './helper'; diff --git a/src/elements-identification.html b/src/elements-identification.html index 84d0774938..301a7f2005 100644 --- a/src/elements-identification.html +++ b/src/elements-identification.html @@ -2,7 +2,7 @@ - BPMN Visualization Non Regression + BPMN Visualization - Elements Identification @@ -68,6 +87,8 @@ + + diff --git a/src/model/bpmn/internal/api.ts b/src/model/bpmn/internal/api.ts new file mode 100644 index 0000000000..6ce1a7245a --- /dev/null +++ b/src/model/bpmn/internal/api.ts @@ -0,0 +1,22 @@ +/** + * Copyright 2021 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 { FlowKind } from './edge/FlowKind'; +import { ShapeBpmnElementKind } from './shape'; + +export type BpmnElementKind = FlowKind | ShapeBpmnElementKind; +export { FlowKind } from './edge/FlowKind'; +export { ShapeBpmnElementKind } from './shape'; diff --git a/src/model/bpmn/internal/edge/Edge.ts b/src/model/bpmn/internal/edge/Edge.ts index f3100ee351..d31726bd8c 100644 --- a/src/model/bpmn/internal/edge/Edge.ts +++ b/src/model/bpmn/internal/edge/Edge.ts @@ -20,8 +20,8 @@ import { MessageVisibleKind } from './MessageVisibleKind'; export default class Edge { constructor( - readonly id?: string, - readonly bpmnElement?: Flow, + readonly id: string, + readonly bpmnElement: Flow, readonly waypoints?: Waypoint[], readonly label?: Label, readonly messageVisibleKind: MessageVisibleKind = MessageVisibleKind.NONE, diff --git a/src/model/bpmn/internal/shape/Shape.ts b/src/model/bpmn/internal/shape/Shape.ts index c41f593a8d..f9715e25a8 100644 --- a/src/model/bpmn/internal/shape/Shape.ts +++ b/src/model/bpmn/internal/shape/Shape.ts @@ -18,5 +18,5 @@ import Bounds from '../Bounds'; import Label from '../Label'; export default class Shape { - constructor(readonly id?: string, readonly bpmnElement?: ShapeBpmnElement, readonly bounds?: Bounds, readonly label?: Label, readonly isHorizontal?: boolean) {} + constructor(readonly id: string, readonly bpmnElement: ShapeBpmnElement, readonly bounds?: Bounds, readonly label?: Label, readonly isHorizontal?: boolean) {} } diff --git a/src/static/js/elements-identification.js b/src/static/js/elements-identification.js index b272000bc2..4799109460 100644 --- a/src/static/js/elements-identification.js +++ b/src/static/js/elements-identification.js @@ -60,6 +60,8 @@ function getCustomCssClassName(bpmnKind) { return 'detection-event'; } else if (bpmnKind.includes('lane')) { return 'detection-lane'; + } else if (bpmnKind.includes('Flow')) { + return 'detection-flow'; } return 'detection'; } diff --git a/test/integration/dom.interactions.test.ts b/test/integration/dom.interactions.test.ts index d392f525b5..7a3dd421f7 100644 --- a/test/integration/dom.interactions.test.ts +++ b/test/integration/dom.interactions.test.ts @@ -17,6 +17,7 @@ import { readFileSync } from '../helpers/file-helper'; import { BpmnElement, BpmnVisualization, ShapeBpmnElementKind } from '../../src/bpmn-visualization'; import { FlowKind } from '../../src/model/bpmn/internal/edge/FlowKind'; import { expectSvgEvent, expectSvgPool, expectSvgSequenceFlow, expectSvgTask, HtmlElementLookup } from './helpers/html-utils'; +import { ExpectedBaseBpmnElement, expectEndEvent, expectPool, expectSequenceFlow, expectServiceTask, expectStartEvent, expectTask } from '../unit/helpers/bpmn-semantic-utils'; const bpmnContainerId = 'bpmn-visualization-container'; const bpmnVisualization = initializeBpmnVisualization(); @@ -41,60 +42,33 @@ describe('DOM only checks', () => { }); }); -interface ExpectedBaseBpmnElement { - id: string; - name?: string; -} - -function expectShapeBpmnElement(bpmnElement: BpmnElement, expected: ExpectedBaseBpmnElement): void { - const bpmnSemantic = bpmnElement.bpmnSemantic; - expect(bpmnSemantic.id).toEqual(expected.id); - expect(bpmnSemantic.name).toEqual(expected.name); - expect(bpmnSemantic.isShape).toBeTruthy(); -} - function expectStartEventBpmnElement(bpmnElement: BpmnElement, expected: ExpectedBaseBpmnElement): void { - expectShapeBpmnElement(bpmnElement, expected); - expect(bpmnElement.bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.EVENT_START); - + expectStartEvent(bpmnElement.bpmnSemantic, expected); expectSvgEvent(bpmnElement.htmlElement); } function expectEndEventBpmnElement(bpmnElement: BpmnElement, expected: ExpectedBaseBpmnElement): void { - expectShapeBpmnElement(bpmnElement, expected); - expect(bpmnElement.bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.EVENT_END); - + expectEndEvent(bpmnElement.bpmnSemantic, expected); expectSvgEvent(bpmnElement.htmlElement); } function expectSequenceFlowBpmnElement(bpmnElement: BpmnElement, expected: ExpectedBaseBpmnElement): void { - const bpmnSemantic = bpmnElement.bpmnSemantic; - expect(bpmnSemantic.id).toEqual(expected.id); - expect(bpmnSemantic.name).toEqual(expected.name); - expect(bpmnSemantic.isShape).toBeFalsy(); - expect(bpmnSemantic.kind).toEqual(FlowKind.SEQUENCE_FLOW); - + expectSequenceFlow(bpmnElement.bpmnSemantic, expected); expectSvgSequenceFlow(bpmnElement.htmlElement); } function expectTaskBpmnElement(bpmnElement: BpmnElement, expected: ExpectedBaseBpmnElement): void { - expectShapeBpmnElement(bpmnElement, expected); - expect(bpmnElement.bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.TASK); - + expectTask(bpmnElement.bpmnSemantic, expected); expectSvgTask(bpmnElement.htmlElement); } function expectServiceTaskBpmnElement(bpmnElement: BpmnElement, expected: ExpectedBaseBpmnElement): void { - expectShapeBpmnElement(bpmnElement, expected); - expect(bpmnElement.bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.TASK_SERVICE); - + expectServiceTask(bpmnElement.bpmnSemantic, expected); expectSvgTask(bpmnElement.htmlElement); } function expectPoolBpmnElement(bpmnElement: BpmnElement, expected: ExpectedBaseBpmnElement): void { - expectShapeBpmnElement(bpmnElement, expected); - expect(bpmnElement.bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.POOL); - + expectPool(bpmnElement.bpmnSemantic, expected); expectSvgPool(bpmnElement.htmlElement); } diff --git a/test/unit/component/registry/bpmn-model-registry.test.ts b/test/unit/component/registry/bpmn-model-registry.test.ts new file mode 100644 index 0000000000..4ec2975bdd --- /dev/null +++ b/test/unit/component/registry/bpmn-model-registry.test.ts @@ -0,0 +1,82 @@ +/** + * Copyright 2021 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 { BpmnModelRegistry } from '../../../../src/component/registry/bpmn-model-registry'; +import BpmnModel from '../../../../src/model/bpmn/internal/BpmnModel'; +import Edge from '../../../../src/model/bpmn/internal/edge/Edge'; +import { SequenceFlow } from '../../../../src/model/bpmn/internal/edge/Flow'; +import Shape from '../../../../src/model/bpmn/internal/shape/Shape'; +import { ShapeBpmnElementKind, ShapeBpmnEventKind } from '../../../../src/model/bpmn/internal/shape'; +import ShapeBpmnElement, { ShapeBpmnStartEvent } from '../../../../src/model/bpmn/internal/shape/ShapeBpmnElement'; +import { expectLane, expectPool, expectSequenceFlow, expectStartEvent } from '../../helpers/bpmn-semantic-utils'; + +const bpmnModelRegistry = new BpmnModelRegistry(); + +function newBpmnModel(): BpmnModel { + return { + edges: [], + flowNodes: [], + lanes: [], + pools: [], + }; +} + +function sequenceFlowInModel(id: string, name: string): BpmnModel { + const bpmnModel = newBpmnModel(); + bpmnModel.edges.push(new Edge(`Edge_${id}`, new SequenceFlow(id, name))); + return bpmnModel; +} + +function startEventInModel(id: string, name: string): BpmnModel { + const bpmnModel = newBpmnModel(); + bpmnModel.flowNodes.push(new Shape(`Shape_${id}`, new ShapeBpmnStartEvent(id, name, ShapeBpmnEventKind.TIMER, 'parentId'))); + return bpmnModel; +} + +function laneInModel(id: string, name: string): BpmnModel { + const bpmnModel = newBpmnModel(); + bpmnModel.lanes.push(new Shape(`Shape_${id}`, new ShapeBpmnElement(id, name, ShapeBpmnElementKind.LANE))); + return bpmnModel; +} + +function poolInModel(id: string, name: string): BpmnModel { + const bpmnModel = newBpmnModel(); + bpmnModel.pools.push(new Shape(`Shape_${id}`, new ShapeBpmnElement(id, name, ShapeBpmnElementKind.POOL))); + return bpmnModel; +} + +describe('Bpmn Model registry', () => { + it('search edge', () => { + bpmnModelRegistry.computeRenderedModel(sequenceFlowInModel('seq flow id', 'seq flow name')); + const bpmnSemantic = bpmnModelRegistry.getBpmnSemantic('seq flow id'); + expectSequenceFlow(bpmnSemantic, { id: 'seq flow id', name: 'seq flow name' }); + }); + it('search flownode', () => { + bpmnModelRegistry.computeRenderedModel(startEventInModel('start event id', 'start event name')); + const bpmnSemantic = bpmnModelRegistry.getBpmnSemantic('start event id'); + expectStartEvent(bpmnSemantic, { id: 'start event id', name: 'start event name' }); + }); + it('search lane', () => { + bpmnModelRegistry.computeRenderedModel(laneInModel('lane id', 'lane name')); + const bpmnSemantic = bpmnModelRegistry.getBpmnSemantic('lane id'); + expectLane(bpmnSemantic, { id: 'lane id', name: 'lane name' }); + }); + it('search pool', () => { + bpmnModelRegistry.computeRenderedModel(poolInModel('pool id', 'pool name')); + const bpmnSemantic = bpmnModelRegistry.getBpmnSemantic('pool id'); + expectPool(bpmnSemantic, { id: 'pool id', name: 'pool name' }); + }); +}); diff --git a/test/unit/helpers/bpmn-semantic-utils.ts b/test/unit/helpers/bpmn-semantic-utils.ts new file mode 100644 index 0000000000..3212e5142e --- /dev/null +++ b/test/unit/helpers/bpmn-semantic-utils.ts @@ -0,0 +1,67 @@ +/** + * Copyright 2021 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 { BpmnSemantic } from '../../../src/component/registry'; +import { FlowKind } from '../../../src/model/bpmn/internal/edge/FlowKind'; +import { ShapeBpmnElementKind } from '../../../src/model/bpmn/internal/shape'; + +export interface ExpectedBaseBpmnElement { + id: string; + name?: string; +} + +export function expectSequenceFlow(bpmnSemantic: BpmnSemantic, expected: ExpectedBaseBpmnElement): void { + expect(bpmnSemantic.id).toEqual(expected.id); + expect(bpmnSemantic.name).toEqual(expected.name); + expect(bpmnSemantic.isShape).toBeFalsy(); + expect(bpmnSemantic.kind).toEqual(FlowKind.SEQUENCE_FLOW); +} + +function expectShape(bpmnSemantic: BpmnSemantic, expected: ExpectedBaseBpmnElement): void { + expect(bpmnSemantic.id).toEqual(expected.id); + expect(bpmnSemantic.name).toEqual(expected.name); + expect(bpmnSemantic.isShape).toBeTruthy(); +} + +export function expectStartEvent(bpmnSemantic: BpmnSemantic, expected: ExpectedBaseBpmnElement): void { + expectShape(bpmnSemantic, expected); + expect(bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.EVENT_START); +} + +export function expectEndEvent(bpmnSemantic: BpmnSemantic, expected: ExpectedBaseBpmnElement): void { + expectShape(bpmnSemantic, expected); + expect(bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.EVENT_END); +} + +export function expectLane(bpmnSemantic: BpmnSemantic, expected: ExpectedBaseBpmnElement): void { + expectShape(bpmnSemantic, expected); + expect(bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.LANE); +} + +export function expectPool(bpmnSemantic: BpmnSemantic, expected: ExpectedBaseBpmnElement): void { + expectShape(bpmnSemantic, expected); + expect(bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.POOL); +} + +export function expectTask(bpmnSemantic: BpmnSemantic, expected: ExpectedBaseBpmnElement): void { + expectShape(bpmnSemantic, expected); + expect(bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.TASK); +} + +export function expectServiceTask(bpmnSemantic: BpmnSemantic, expected: ExpectedBaseBpmnElement): void { + expectShape(bpmnSemantic, expected); + expect(bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.TASK_SERVICE); +}