XOR-Gateways mit sichtbarem X ausgeben (#15)
- build_bpmn(): Die BPMNShape jedes exklusiven Gateways bekommt isMarkerVisible="true". BPMN lässt das X optional; ohne das Attribut zeichnet bpmn-js eine leere Raute. OR-, AND- und ereignisbasierte Gateways haben ihr Symbol immer und bleiben unverändert. - Alle Beispiele neu erzeugt: je XOR-Gateway nur das Attribut ergänzt (beispiel_lanes 1, beispiel_prozess 2, Referenzmodell 14), sonst byte-gleich. - Neuer Test test_nur_xor_gateways_zeigen_ihr_x. - Tests: python -m pytest -q -> 93 bestanden. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -89,7 +89,7 @@
|
||||
<bpmndi:BPMNShape id="Task_S2_di" bpmnElement="Task_S2">
|
||||
<dc:Bounds x="300" y="320" width="100" height="80"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S3_di" bpmnElement="Gateway_S3">
|
||||
<bpmndi:BPMNShape id="Gateway_S3_di" bpmnElement="Gateway_S3" isMarkerVisible="true">
|
||||
<dc:Bounds x="490" y="335" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="SendTask_S4_di" bpmnElement="SendTask_S4">
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<bpmndi:BPMNShape id="Task_S2_di" bpmnElement="Task_S2">
|
||||
<dc:Bounds x="165" y="250" width="100" height="80"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S3_di" bpmnElement="Gateway_S3">
|
||||
<bpmndi:BPMNShape id="Gateway_S3_di" bpmnElement="Gateway_S3" isMarkerVisible="true">
|
||||
<dc:Bounds x="165" y="420" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="SendTask_S4_di" bpmnElement="SendTask_S4">
|
||||
@@ -73,7 +73,7 @@
|
||||
<bpmndi:BPMNShape id="Task_S5_di" bpmnElement="Task_S5">
|
||||
<dc:Bounds x="250" y="590" width="100" height="80"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S6_di" bpmnElement="Gateway_S6">
|
||||
<bpmndi:BPMNShape id="Gateway_S6_di" bpmnElement="Gateway_S6" isMarkerVisible="true">
|
||||
<dc:Bounds x="165" y="760" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_S7_di" bpmnElement="Task_S7">
|
||||
|
||||
@@ -305,7 +305,7 @@
|
||||
<bpmndi:BPMNShape id="Task_S5_di" bpmnElement="Task_S5">
|
||||
<dc:Bounds x="870" y="130" width="100" height="80"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S6_di" bpmnElement="Gateway_S6">
|
||||
<bpmndi:BPMNShape id="Gateway_S6_di" bpmnElement="Gateway_S6" isMarkerVisible="true">
|
||||
<dc:Bounds x="1060" y="145" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_S7_di" bpmnElement="Task_S7">
|
||||
@@ -317,31 +317,31 @@
|
||||
<bpmndi:BPMNShape id="Task_S9_di" bpmnElement="Task_S9">
|
||||
<dc:Bounds x="1630" y="130" width="100" height="80"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S10_di" bpmnElement="Gateway_S10">
|
||||
<bpmndi:BPMNShape id="Gateway_S10_di" bpmnElement="Gateway_S10" isMarkerVisible="true">
|
||||
<dc:Bounds x="1820" y="145" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_S11_di" bpmnElement="Task_S11">
|
||||
<dc:Bounds x="2010" y="130" width="100" height="80"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S12_di" bpmnElement="Gateway_S12">
|
||||
<bpmndi:BPMNShape id="Gateway_S12_di" bpmnElement="Gateway_S12" isMarkerVisible="true">
|
||||
<dc:Bounds x="2200" y="145" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S13_di" bpmnElement="Gateway_S13">
|
||||
<bpmndi:BPMNShape id="Gateway_S13_di" bpmnElement="Gateway_S13" isMarkerVisible="true">
|
||||
<dc:Bounds x="2390" y="60" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_S14_di" bpmnElement="Task_S14">
|
||||
<dc:Bounds x="2580" y="45" width="100" height="80"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S15_di" bpmnElement="Gateway_S15">
|
||||
<bpmndi:BPMNShape id="Gateway_S15_di" bpmnElement="Gateway_S15" isMarkerVisible="true">
|
||||
<dc:Bounds x="2770" y="60" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_S16_di" bpmnElement="Task_S16">
|
||||
<dc:Bounds x="2960" y="45" width="100" height="80"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S17_di" bpmnElement="Gateway_S17">
|
||||
<bpmndi:BPMNShape id="Gateway_S17_di" bpmnElement="Gateway_S17" isMarkerVisible="true">
|
||||
<dc:Bounds x="3150" y="60" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S18_di" bpmnElement="Gateway_S18">
|
||||
<bpmndi:BPMNShape id="Gateway_S18_di" bpmnElement="Gateway_S18" isMarkerVisible="true">
|
||||
<dc:Bounds x="3340" y="60" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_S19_di" bpmnElement="Task_S19">
|
||||
@@ -353,10 +353,10 @@
|
||||
<bpmndi:BPMNShape id="Task_S21_di" bpmnElement="Task_S21">
|
||||
<dc:Bounds x="3530" y="765" width="100" height="80"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S22_di" bpmnElement="Gateway_S22">
|
||||
<bpmndi:BPMNShape id="Gateway_S22_di" bpmnElement="Gateway_S22" isMarkerVisible="true">
|
||||
<dc:Bounds x="3910" y="145" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S23_di" bpmnElement="Gateway_S23">
|
||||
<bpmndi:BPMNShape id="Gateway_S23_di" bpmnElement="Gateway_S23" isMarkerVisible="true">
|
||||
<dc:Bounds x="3340" y="230" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_S24_di" bpmnElement="Task_S24">
|
||||
@@ -365,10 +365,10 @@
|
||||
<bpmndi:BPMNShape id="Task_S25_di" bpmnElement="Task_S25">
|
||||
<dc:Bounds x="3530" y="130" width="100" height="80"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S26_di" bpmnElement="Gateway_S26">
|
||||
<bpmndi:BPMNShape id="Gateway_S26_di" bpmnElement="Gateway_S26" isMarkerVisible="true">
|
||||
<dc:Bounds x="3720" y="230" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S27_di" bpmnElement="Gateway_S27">
|
||||
<bpmndi:BPMNShape id="Gateway_S27_di" bpmnElement="Gateway_S27" isMarkerVisible="true">
|
||||
<dc:Bounds x="4100" y="145" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_S40_di" bpmnElement="Task_S40">
|
||||
@@ -377,16 +377,16 @@
|
||||
<bpmndi:BPMNShape id="Task_S41_di" bpmnElement="Task_S41">
|
||||
<dc:Bounds x="2580" y="215" width="100" height="80"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S42_di" bpmnElement="Gateway_S42">
|
||||
<bpmndi:BPMNShape id="Gateway_S42_di" bpmnElement="Gateway_S42" isMarkerVisible="true">
|
||||
<dc:Bounds x="2770" y="230" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_S43_di" bpmnElement="Task_S43">
|
||||
<dc:Bounds x="2960" y="215" width="100" height="80"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S44_di" bpmnElement="Gateway_S44">
|
||||
<bpmndi:BPMNShape id="Gateway_S44_di" bpmnElement="Gateway_S44" isMarkerVisible="true">
|
||||
<dc:Bounds x="3150" y="230" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_S50_di" bpmnElement="Gateway_S50">
|
||||
<bpmndi:BPMNShape id="Gateway_S50_di" bpmnElement="Gateway_S50" isMarkerVisible="true">
|
||||
<dc:Bounds x="4290" y="145" width="50" height="50"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_S51_di" bpmnElement="Task_S51">
|
||||
|
||||
@@ -884,9 +884,12 @@ def build_bpmn(meta_md, steps, order, externals):
|
||||
}).append(_bounds_el(0, ex_y, pool_width, 100))
|
||||
|
||||
for sid in order:
|
||||
shape = ET.SubElement(plane, q('bpmndi', 'BPMNShape'), {
|
||||
'id': f'{node_ids[sid]}_di', 'bpmnElement': node_ids[sid],
|
||||
})
|
||||
attrs = {'id': f'{node_ids[sid]}_di', 'bpmnElement': node_ids[sid]}
|
||||
if steps[sid].type == 'XorGateway':
|
||||
# BPMN lässt das "X" im exklusiven Gateway optional; ohne dieses
|
||||
# Attribut zeichnen Werkzeuge wie bpmn-js eine leere Raute (#15).
|
||||
attrs['isMarkerVisible'] = 'true'
|
||||
shape = ET.SubElement(plane, q('bpmndi', 'BPMNShape'), attrs)
|
||||
x, y, w, h = bounds_of(sid)
|
||||
shape.append(_bounds_el(x, y, w, h))
|
||||
|
||||
|
||||
@@ -398,6 +398,32 @@ def test_kante_zwischen_gateways_zum_westpunkt_eines_linken_ziels_umgeht_die_rau
|
||||
assert all(a[0] == b[0] or a[1] == b[1] for a, b in zip(punkte, punkte[1:])), "nicht rechtwinklig"
|
||||
|
||||
|
||||
def test_nur_xor_gateways_zeigen_ihr_x():
|
||||
"""Prüft, dass die Diagrammform jedes exklusiven Gateways isMarkerVisible="true" trägt, damit das „X“ erscheint,
|
||||
und dass kein anderes Element das Attribut bekommt (Issue #15)."""
|
||||
text = """
|
||||
S1 Start: Beginn
|
||||
-> S2
|
||||
S2 Gateway XOR: Vollständig?
|
||||
Ja -> S3
|
||||
Nein -> S4
|
||||
S3 Gateway AND: Aufteilen
|
||||
-> S5
|
||||
S4 Gateway OR: Welche?
|
||||
-> S5
|
||||
S5 Task: Bearbeiten
|
||||
-> S6
|
||||
S6 Ende: Fertig
|
||||
"""
|
||||
meta, steps, order, externals = m.parse_markdown(text)
|
||||
defs = m.build_bpmn(meta, steps, order, externals)
|
||||
xor_ids = {g.get('id') for g in defs.iter(f"{{{m.NS['bpmn']}}}exclusiveGateway")}
|
||||
assert xor_ids, "Testmodell ohne XOR-Gateway"
|
||||
for shape in defs.iter(f"{{{m.NS['bpmndi']}}}BPMNShape"):
|
||||
erwartet = 'true' if shape.get('bpmnElement') in xor_ids else None
|
||||
assert shape.get('isMarkerVisible') == erwartet, shape.get('bpmnElement')
|
||||
|
||||
|
||||
def test_ausgabe_ist_wohlgeformtes_xml(tmp_path):
|
||||
"""Prüft, dass die erzeugte .bpmn-Datei sich als valides XML parsen laesst."""
|
||||
src = tmp_path / 'prozess.md'
|
||||
|
||||
Reference in New Issue
Block a user