fix : autonumber positioning for all types of arrows and central connections

This commit is contained in:
omkarht
2025-11-10 16:14:23 +05:30
parent c02cf92656
commit e7c78dec87
3 changed files with 992 additions and 44 deletions

View File

@@ -776,5 +776,834 @@ describe('Sequence Diagram Special Cases', () => {
);
});
});
describe('Sequence Diagram Rendering with Autonumber and All Arrow Types', () => {
describe('Autonumber with All Arrow Types', () => {
it('should render all arrow types with autonumbering', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
Alice->>Bob: Solid arrow (->>)
Bob-->>Alice: Dotted arrow (-->>)
Alice->Charlie: Solid open arrow (->)
Charlie-->Dave: Dotted open arrow (-->)
Alice-xBob: Solid cross (-x)
Bob--xAlice: Dotted cross (--x)
Alice-)Charlie: Solid point async (-)
Charlie--)Dave: Dotted point async (--)
Alice<<->>Bob: Bidirectional solid (<<->>)
Charlie<<-->>Dave: Bidirectional dotted (<<-->>)
Alice-|\\Bob: Solid top half (-|\\)
Bob-|/Alice: Solid bottom half (-|/)
Alice-\\\\Charlie: Stick top half (-\\\\)
Charlie-//Dave: Stick bottom half (-//)
Dave/|-Charlie: Solid top reverse (/|-)
Charlie\\|-Bob: Solid bottom reverse (\\|-)
Bob//-Alice: Stick top reverse (//-)
Alice\\\\-Bob: Stick bottom reverse (\\\\-)
Alice--|\\Bob: Dotted solid top (--|\\)
Bob--|/Alice: Dotted solid bottom (--|/)
Alice--\\\\Charlie: Dotted stick top (--\\\\)
Charlie--//Dave: Dotted stick bottom (--//)
Dave/|--Charlie: Dotted solid top reverse (/|--)
Charlie\\|--Bob: Dotted solid bottom reverse (\\|--)
Bob//--Alice: Dotted stick top reverse (//--)
Alice\\\\--Bob: Dotted stick bottom reverse (\\\\--)
Alice->>()Bob: Solid with central connection
Bob()-->>Alice: Dotted with reverse central
Alice()->>()Charlie: Dual central connections`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render all arrow types with autonumbering - left to right only', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
Alice->>Bob: Solid arrow (->>)
Alice-->>Bob: Dotted arrow (-->>)
Alice->Bob: Solid open arrow (->)
Alice-->Bob: Dotted open arrow (-->)
Alice-xBob: Solid cross (-x)
Alice--xBob: Dotted cross (--x)
Alice-)Bob: Solid point async (-)
Alice--)Bob: Dotted point async (--)
Alice<<->>Bob: Bidirectional solid (<<->>)
Alice<<-->>Bob: Bidirectional dotted (<<-->>)
Alice-|\\Bob: Solid top half (-|\\)
Alice-|/Bob: Solid bottom half (-|/)
Alice-\\\\Bob: Stick top half (-\\\\)
Alice-//Bob: Stick bottom half (-//)
Bob/|-Alice: Solid top reverse (/|-)
Bob\\|-Alice: Solid bottom reverse (\\|-)
Bob//-Alice: Stick top reverse (//-)
Bob\\\\-Alice: Stick bottom reverse (\\\\-)
Alice--|\\Bob: Dotted solid top (--|\\)
Alice--|/Bob: Dotted solid bottom (--|/)
Alice--\\\\Bob: Dotted stick top (--\\\\)
Alice--//Bob: Dotted stick bottom (--//)
Bob/|--Alice: Dotted solid top reverse (/|--)
Bob\\|--Alice: Dotted solid bottom reverse (\\|--)
Bob//--Alice: Dotted stick top reverse (//--)
Bob\\\\--Alice: Dotted stick bottom reverse (\\\\--)
Alice->>()Bob: Solid with central connection
Alice()-->>Bob: Dotted with reverse central
Alice()->>()Bob: Dual central connections`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render central connections with autonumbering', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice->>()Bob: Central connection at destination
Bob()->>Alice: Reverse central at source
Alice()->>()Bob: Dual central connections
Bob->>()Charlie: Another central connection
Charlie()-->>Alice: Reverse central dotted
Alice()<<-->>()Bob: Dual central bidirectional`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render bidirectional arrows with autonumbering', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice<<->>Bob: Bidirectional solid left to right
Bob<<->>Alice: Bidirectional solid right to left
Alice<<-->>Charlie: Bidirectional dotted left to right
Charlie<<-->>Alice: Bidirectional dotted right to left
Bob<<->>Charlie: Bidirectional solid
Charlie<<-->>Bob: Bidirectional dotted`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render reverse arrows with autonumbering', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Bob/|-Alice: Solid top reverse left to right
Alice/|-Bob: Solid top reverse right to left
Bob\\|-Alice: Solid bottom reverse left to right
Alice\\|-Bob: Solid bottom reverse right to left
Bob//-Alice: Stick top reverse left to right
Alice//-Bob: Stick top reverse right to left
Bob\\\\-Alice: Stick bottom reverse left to right
Alice\\\\-Bob: Stick bottom reverse right to left
Bob/|--Alice: Dotted solid top reverse
Alice\\|--Bob: Dotted solid bottom reverse
Bob//--Alice: Dotted stick top reverse
Alice\\\\--Bob: Dotted stick bottom reverse`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
});
describe('Central Connections with Autonumber - Comprehensive Coverage', () => {
it('should render CENTRAL_CONNECTION with normal arrows - left to right', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice->>()Bob: Solid arrow with circle at destination
Alice-->>()Bob: Dotted arrow with circle at destination
Alice->()Bob: Open arrow with circle at destination
Alice--x()Bob: Cross arrow with circle at destination
Alice--)()Bob: Close arrow with circle at destination`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render CENTRAL_CONNECTION with normal arrows - right to left', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Bob->>()Alice: Solid arrow with circle at destination (RTL)
Charlie-->>()Bob: Dotted arrow with circle at destination (RTL)
Bob->()Alice: Open arrow with circle at destination (RTL)
Charlie--x()Alice: Cross arrow with circle at destination (RTL)
Bob--)()Alice: Close arrow with circle at destination (RTL)`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render CENTRAL_CONNECTION with reverse arrows - left to right', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Bob/|-()Alice: Solid top reverse with circle (LTR)
Bob\\|-()Alice: Solid bottom reverse with circle (LTR)
Bob//-()Alice: Stick top reverse with circle (LTR)
Bob\\\\-()Alice: Stick bottom reverse with circle (LTR)
Bob/|--()Alice: Dotted solid top reverse with circle (LTR)
Bob\\|--()Alice: Dotted solid bottom reverse with circle (LTR)
Bob//--()Alice: Dotted stick top reverse with circle (LTR)
Bob\\\\--()Alice: Dotted stick bottom reverse with circle (LTR)`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render CENTRAL_CONNECTION with reverse arrows - right to left', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice/|-()Bob: Solid top reverse with circle (RTL)
Alice\\|-()Bob: Solid bottom reverse with circle (RTL)
Alice//-()Bob: Stick top reverse with circle (RTL)
Alice\\\\-()Bob: Stick bottom reverse with circle (RTL)
Alice/|--()Bob: Dotted solid top reverse with circle (RTL)
Alice\\|--()Bob: Dotted solid bottom reverse with circle (RTL)
Alice//--()Bob: Dotted stick top reverse with circle (RTL)
Alice\\\\--()Bob: Dotted stick bottom reverse with circle (RTL)`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render Central_Connection_REVERSE ()->> normal LTR', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice()->>Bob: Circle at source with solid arrow
Alice()-->>Bob: Circle at source with dotted arrow
Alice()->Bob: Circle at source with open arrow
Alice()--xBob: Circle at source with cross arrow
Alice()--)Bob: Circle at source with close arrow`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render Central_Connection_REVERSE ()->> normal RTL', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Bob()->>Alice: Circle at source with solid arrow (RTL)
Charlie()-->>Bob: Circle at source with dotted arrow (RTL)
Bob()->Alice: Circle at source with open arrow (RTL)
Charlie()--xAlice: Circle at source with cross arrow (RTL)
Bob()--)Alice: Circle at source with close arrow (RTL)`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render Central_Connection_REVERSE ()->> reverse LTR', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Bob()/|-Alice: Circle at source with solid top reverse (LTR)
Bob()\\|-Alice: Circle at source with solid bottom reverse (LTR)
Bob()//-Alice: Circle at source with stick top reverse (LTR)
Bob()\\\\-Alice: Circle at source with stick bottom reverse (LTR)
Bob()/|--Alice: Circle at source with dotted solid top reverse (LTR)
Bob()\\|--Alice: Circle at source with dotted solid bottom reverse (LTR)
Bob()//--Alice: Circle at source with dotted stick top reverse (LTR)
Bob()\\\\--Alice: Circle at source with dotted stick bottom reverse (LTR)`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render Central_Connection_REVERSE ()->> reverse RTL', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice()/|-Bob: Circle at source with solid top reverse (RTL)
Alice()\\|-Bob: Circle at source with solid bottom reverse (RTL)
Alice()//-Bob: Circle at source with stick top reverse (RTL)
Alice()\\\\-Bob: Circle at source with stick bottom reverse (RTL)
Alice()/|--Bob: Circle at source with dotted solid top reverse (RTL)
Alice()\\|--Bob: Circle at source with dotted solid bottom reverse (RTL)
Alice()//--Bob: Circle at source with dotted stick top reverse (RTL)
Alice()\\\\--Bob: Circle at source with dotted stick bottom reverse (RTL)`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render Central_Connection_DUAL ()->>() normal LTR', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice()->>()Bob: Circles at both ends with solid arrow
Alice()-->>()Bob: Circles at both ends with dotted arrow
Alice()->()Bob: Circles at both ends with open arrow
Alice()--x()Bob: Circles at both ends with cross arrow
Alice()--)()Bob: Circles at both ends with close arrow`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render Central_Connection_DUAL ()->>() normal RTL', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Bob()->>()Alice: Circles at both ends with solid arrow (RTL)
Charlie()-->>()Bob: Circles at both ends with dotted arrow (RTL)
Bob()->()Alice: Circles at both ends with open arrow (RTL)
Charlie()--x()Alice: Circles at both ends with cross arrow (RTL)
Bob()--)()Alice: Circles at both ends with close arrow (RTL)`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render Central_Connection_DUAL ()->>() reverse LTR', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Bob()/|-()Alice: Circles at both ends with solid top reverse (LTR)
Bob()\\|-()Alice: Circles at both ends with solid bottom reverse (LTR)
Bob()//-()Alice: Circles at both ends with stick top reverse (LTR)
Bob()\\\\-()Alice: Circles at both ends with stick bottom reverse (LTR)
Bob()/|--()Alice: Circles at both ends with dotted solid top reverse (LTR)
Bob()\\|--()Alice: Circles at both ends with dotted solid bottom reverse (LTR)
Bob()//--()Alice: Circles at both ends with dotted stick top reverse (LTR)
Bob()\\\\--()Alice: Circles at both ends with dotted stick bottom reverse (LTR)`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render Central_Connection_DUAL ()->>() reverse RTL', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice()/|-()Bob: Circles at both ends with solid top reverse (RTL)
Alice()\\|-()Bob: Circles at both ends with solid bottom reverse (RTL)
Alice()//-()Bob: Circles at both ends with stick top reverse (RTL)
Alice()\\\\-()Bob: Circles at both ends with stick bottom reverse (RTL)
Alice()/|--()Bob: Circles at both ends with dotted solid top reverse (RTL)
Alice()\\|--()Bob: Circles at both ends with dotted solid bottom reverse (RTL)
Alice()//--()Bob: Circles at both ends with dotted stick top reverse (RTL)
Alice()\\\\--()Bob: Circles at both ends with dotted stick bottom reverse (RTL)`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render mixed central connections with autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
participant David
Note over Alice,David: Normal arrows with central connections
Alice->>()Bob: CENTRAL_CONNECTION LTR
Bob->>()Alice: CENTRAL_CONNECTION RTL
Alice()->>Bob: CENTRAL_CONNECTION_REVERSE LTR
Bob()->>Alice: CENTRAL_CONNECTION_REVERSE RTL
Alice()->>()Bob: CENTRAL_CONNECTION_DUAL LTR
Bob()->>()Alice: CENTRAL_CONNECTION_DUAL RTL
Note over Alice,David: Reverse arrows with central connections
Bob/|-()Alice: Reverse with CENTRAL_CONNECTION LTR
Alice/|-()Bob: Reverse with CENTRAL_CONNECTION RTL
Bob()/|-Alice: Reverse with CENTRAL_CONNECTION_REVERSE LTR
Alice()/|-Bob: Reverse with CENTRAL_CONNECTION_REVERSE RTL
Bob()/|-()Alice: Reverse with CENTRAL_CONNECTION_DUAL LTR
Alice()/|-()Bob: Reverse with CENTRAL_CONNECTION_DUAL RTL
Note over Alice,David: Mixed with different participants
Alice->>()Charlie: Skip participant
Charlie()->>Alice: Back skip
Bob()->>()David: Another skip
David()->>()Bob: Return skip`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render central connections with bidirectional arrows and autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice()<<->>()Bob: Dual central with bidirectional solid LTR
Bob()<<->>()Alice: Dual central with bidirectional solid RTL
Alice()<<-->>()Bob: Dual central with bidirectional dotted LTR
Bob()<<-->>()Alice: Dual central with bidirectional dotted RTL
Alice<<->>()Bob: Central at end with bidirectional LTR
Bob()<<->>Alice: Central at start with bidirectional RTL`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
});
describe('Self-Reference Arrows - Comprehensive Coverage', () => {
it('should render self-reference with normal arrows - without autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice->>Alice: Solid arrow self-reference
Bob-->>Bob: Dotted arrow self-reference
Charlie->Charlie: Open arrow self-reference
Alice-->Alice: Dotted open arrow self-reference
Bob-xBob: Cross arrow self-reference
Charlie--xCharlie: Dotted cross self-reference`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with normal arrows - with autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice->>Alice: Solid arrow self-reference
Bob-->>Bob: Dotted arrow self-reference
Charlie->Charlie: Open arrow self-reference
Alice-->Alice: Dotted open arrow self-reference
Bob-xBob: Cross arrow self-reference
Charlie--xCharlie: Dotted cross self-reference`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with reverse arrows - without autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice/|-Alice: Solid top reverse self-reference
Bob\\|-Bob: Solid bottom reverse self-reference
Charlie//-Charlie: Stick top reverse self-reference
Alice\\\\-Alice: Stick bottom reverse self-reference
Bob/|--Bob: Dotted solid top reverse self-reference
Charlie\\|--Charlie: Dotted solid bottom reverse self-reference
Alice//--Alice: Dotted stick top reverse self-reference
Bob\\\\--Bob: Dotted stick bottom reverse self-reference`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with reverse arrows - with autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice/|-Alice: Solid top reverse self-reference
Bob\\|-Bob: Solid bottom reverse self-reference
Charlie//-Charlie: Stick top reverse self-reference
Alice\\\\-Alice: Stick bottom reverse self-reference
Bob/|--Bob: Dotted solid top reverse self-reference
Charlie\\|--Charlie: Dotted solid bottom reverse self-reference
Alice//--Alice: Dotted stick top reverse self-reference
Bob\\\\--Bob: Dotted stick bottom reverse self-reference`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with bidirectional arrows - without autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice<<->>Alice: Bidirectional solid self-reference
Bob<<-->>Bob: Bidirectional dotted self-reference
Charlie<<->>Charlie: Another bidirectional solid
Alice<<-->>Alice: Another bidirectional dotted`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with bidirectional arrows - with autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice<<->>Alice: Bidirectional solid self-reference
Bob<<-->>Bob: Bidirectional dotted self-reference
Charlie<<->>Charlie: Another bidirectional solid
Alice<<-->>Alice: Another bidirectional dotted`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with CENTRAL_CONNECTION - without autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice->>()Alice: Solid arrow with circle at destination
Bob-->>()Bob: Dotted arrow with circle at destination
Charlie->()Charlie: Open arrow with circle at destination
Alice--x()Alice: Cross arrow with circle at destination
Bob--)()Bob: Close arrow with circle at destination`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with CENTRAL_CONNECTION - with autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice->>()Alice: Solid arrow with circle at destination
Bob-->>()Bob: Dotted arrow with circle at destination
Charlie->()Charlie: Open arrow with circle at destination
Alice--x()Alice: Cross arrow with circle at destination
Bob--)()Bob: Close arrow with circle at destination`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with CENTRAL_CONNECTION_REVERSE - without autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice()->>Alice: Circle at source with solid arrow
Bob()-->>Bob: Circle at source with dotted arrow
Charlie()->Charlie: Circle at source with open arrow
Alice()--xAlice: Circle at source with cross arrow
Bob()--)Bob: Circle at source with close arrow`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with CENTRAL_CONNECTION_REVERSE - with autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice()->>Alice: Circle at source with solid arrow
Bob()-->>Bob: Circle at source with dotted arrow
Charlie()->Charlie: Circle at source with open arrow
Alice()--xAlice: Circle at source with cross arrow
Bob()--)Bob: Circle at source with close arrow`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with CENTRAL_CONNECTION_DUAL - without autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice()->>()Alice: Circles at both ends with solid arrow
Bob()-->>()Bob: Circles at both ends with dotted arrow
Charlie()->()Charlie: Circles at both ends with open arrow
Alice()--x()Alice: Circles at both ends with cross arrow
Bob()--)()Bob: Circles at both ends with close arrow`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with CENTRAL_CONNECTION_DUAL - with autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice()->>()Alice: Circles at both ends with solid arrow
Bob()-->>()Bob: Circles at both ends with dotted arrow
Charlie()->()Charlie: Circles at both ends with open arrow
Alice()--x()Alice: Circles at both ends with cross arrow
Bob()--)()Bob: Circles at both ends with close arrow`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with reverse arrows and CENTRAL_CONNECTION', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice/|-()Alice: Solid top reverse with circle at destination
Bob\\|-()Bob: Solid bottom reverse with circle at destination
Charlie//-()Charlie: Stick top reverse with circle at destination
Alice\\\\-()Alice: Stick bottom reverse with circle at destination
Bob/|--()Bob: Dotted solid top reverse with circle at destination
Charlie\\|--()Charlie: Dotted solid bottom reverse with circle at destination
Alice//--()Alice: Dotted stick top reverse with circle at destination
Bob\\\\--()Bob: Dotted stick bottom reverse with circle at destination`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with reverse arrows and CENTRAL_CONNECTION - with autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice/|-()Alice: Solid top reverse with circle at destination
Bob\\|-()Bob: Solid bottom reverse with circle at destination
Charlie//-()Charlie: Stick top reverse with circle at destination
Alice\\\\-()Alice: Stick bottom reverse with circle at destination
Bob/|--()Bob: Dotted solid top reverse with circle at destination
Charlie\\|--()Charlie: Dotted solid bottom reverse with circle at destination
Alice//--()Alice: Dotted stick top reverse with circle at destination
Bob\\\\--()Bob: Dotted stick bottom reverse with circle at destination`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-ref reverse Central_Connection_REVERSE no-autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice()/|-Alice: Circle at source with solid top reverse
Bob()\\|-Bob: Circle at source with solid bottom reverse
Charlie()//-Charlie: Circle at source with stick top reverse
Alice()\\\\-Alice: Circle at source with stick bottom reverse
Bob()/|--Bob: Circle at source with dotted solid top reverse
Charlie()\\|--Charlie: Circle at source with dotted solid bottom reverse
Alice()//--Alice: Circle at source with dotted stick top reverse
Bob()\\\\--Bob: Circle at source with dotted stick bottom reverse`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-ref reverse Central_Connection_REVERSE autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice()/|-Alice: Circle at source with solid top reverse
Bob()\\|-Bob: Circle at source with solid bottom reverse
Charlie()//-Charlie: Circle at source with stick top reverse
Alice()\\\\-Alice: Circle at source with stick bottom reverse
Bob()/|--Bob: Circle at source with dotted solid top reverse
Charlie()\\|--Charlie: Circle at source with dotted solid bottom reverse
Alice()//--Alice: Circle at source with dotted stick top reverse
Bob()\\\\--Bob: Circle at source with dotted stick bottom reverse`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-ref reverse Central_Connection_DUAL no-autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice()/|-()Alice: Circles at both ends with solid top reverse
Bob()\\|-()Bob: Circles at both ends with solid bottom reverse
Charlie()//-()Charlie: Circles at both ends with stick top reverse
Alice()\\\\-()Alice: Circles at both ends with stick bottom reverse
Bob()/|--()Bob: Circles at both ends with dotted solid top reverse
Charlie()\\|--()Charlie: Circles at both ends with dotted solid bottom reverse
Alice()//--()Alice: Circles at both ends with dotted stick top reverse
Bob()\\\\--()Bob: Circles at both ends with dotted stick bottom reverse`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-references, reverse arrows & dual central connection (autonumber).', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice()/|-()Alice: Circles at both ends with solid top reverse
Bob()\\|-()Bob: Circles at both ends with solid bottom reverse
Charlie()//-()Charlie: Circles at both ends with stick top reverse
Alice()\\\\-()Alice: Circles at both ends with stick bottom reverse
Bob()/|--()Bob: Circles at both ends with dotted solid top reverse
Charlie()\\|--()Charlie: Circles at both ends with dotted solid bottom reverse
Alice()//--()Alice: Circles at both ends with dotted stick top reverse
Bob()\\\\--()Bob: Circles at both ends with dotted stick bottom reverse`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('Render self-references with bidirectional central connections (no autonumber).', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice()<<->>()Alice: Dual central with bidirectional solid
Bob()<<-->>()Bob: Dual central with bidirectional dotted
Charlie<<->>()Alice: Central at end with bidirectional
Bob()<<->>Bob: Central at start with bidirectional`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference with bidirectional and central connections - with autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice()<<->>()Alice: Dual central with bidirectional solid
Bob()<<-->>()Bob: Dual central with bidirectional dotted
Charlie<<->>()Charlie: Central at end with bidirectional
Bob()<<->>Bob: Central at start with bidirectional`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render comprehensive self-reference scenario - all arrow types mixed', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Note over Alice,Charlie: Normal arrows
Alice->>Alice: Normal solid
Bob-->>Bob: Normal dotted
Charlie->Charlie: Normal open
Note over Alice,Charlie: Reverse arrows
Alice/|-Alice: Reverse solid top
Bob\\|-Bob: Reverse solid bottom
Note over Alice,Charlie: Bidirectional arrows
Charlie<<->>Charlie: Bidirectional solid
Alice<<-->>Alice: Bidirectional dotted
Note over Alice,Charlie: Central connections
Bob->>()Bob: Central at destination
Charlie()->>Charlie: Central at source
Alice()->>()Alice: Dual central
Note over Alice,Charlie: Reverse with central
Bob()/|-()Bob: Reverse with dual central
Charlie/|-()Charlie: Reverse with central at destination
Note over Alice,Charlie: Bidirectional with central
Alice()<<->>()Alice: Bidirectional with dual central`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render self-reference mixed with regular messages and autonumber', () => {
imgSnapshotTest(
`%%{init: {'theme':'base'}}%%
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Charlie
Alice->>Bob: Regular message
Bob->>Bob: Self-reference solid
Bob-->>Charlie: Regular dotted
Charlie()->>()Charlie: Self-ref dual central
Charlie->>Alice: Regular back
Alice<<->>Alice: Self-ref bidirectional
Alice()->>Bob: Regular with central
Bob()/|-()Bob: Self-ref reverse dual central
Bob-->>Alice: Regular dotted back`,
{ sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
});
});
});
});

View File

@@ -294,11 +294,25 @@ const drawCentralConnection = function (
const actors = diagObj.db.getActors();
const fromActor = actors.get(msg.from);
const toActor = actors.get(msg.to);
const fromCenter = fromActor.x + fromActor.width / 2;
const toCenter = toActor.x + toActor.width / 2;
const isAutoNumberOn = msgModel.sequenceVisible;
let fromCenter = fromActor.x + fromActor.width / 2;
let toCenter = toActor.x + toActor.width / 2;
// Determine arrow direction: left-to-right or right-to-left
const isLeftToRight = fromCenter <= toCenter;
const isReverse = isReverseArrowType(msg, diagObj);
const g = elem.append('g');
const CENTRAL_CONNECTION_CIRCLE_OFFSET = 16.5;
const getCircleOffset = (isLeftToRight: boolean, isReverse: boolean) => {
const baseOffset = isLeftToRight
? CENTRAL_CONNECTION_CIRCLE_OFFSET
: -CENTRAL_CONNECTION_CIRCLE_OFFSET;
return isReverse ? -baseOffset : baseOffset;
};
const drawCircle = (cx: number) => {
g.append('circle')
.attr('cx', cx)
@@ -311,6 +325,37 @@ const drawCentralConnection = function (
const { CENTRAL_CONNECTION, CENTRAL_CONNECTION_REVERSE, CENTRAL_CONNECTION_DUAL } =
diagObj.db.LINETYPE;
// Calculate circle position adjustments when autonumber is enabled
if (isAutoNumberOn) {
switch (msg.centralConnection) {
case CENTRAL_CONNECTION:
// Pattern: actor ->>() actor - circle at destination
if (isReverse) {
toCenter += getCircleOffset(isLeftToRight, true);
}
// No adjustment for normal arrows
break;
case CENTRAL_CONNECTION_REVERSE:
// Pattern: actor ()->> actor - circle at source
if (!isReverse) {
fromCenter += getCircleOffset(isLeftToRight, false);
}
// No adjustment for reverse arrows
break;
case CENTRAL_CONNECTION_DUAL:
// Pattern: actor ()->>() actor - circles at both ends
if (isReverse) {
toCenter += getCircleOffset(isLeftToRight, true);
} else {
fromCenter += getCircleOffset(isLeftToRight, false);
}
break;
}
}
// Draw circles based on central connection type
switch (msg.centralConnection) {
case CENTRAL_CONNECTION:
drawCircle(toCenter);
@@ -438,12 +483,17 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
let line;
if (startx === stopx) {
const isAutoNumberOn = sequenceVisible || conf.showSequenceNumbers;
const isReverse = isReverseArrowType(msg, diagObj);
const isBidirectional = isBidirectionalArrowType(msg, diagObj);
const lineStartX = startx + (isAutoNumberOn && (isReverse || isBidirectional) ? 10 : 0);
if (conf.rightAngles) {
line = diagram
.append('path')
.attr(
'd',
`M ${startx},${lineStartY} H ${
`M ${lineStartX},${lineStartY} H ${
startx + common.getMax(conf.width / 2, textWidth / 2)
} V ${lineStartY + 25} H ${startx}`
);
@@ -453,11 +503,11 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
.attr(
'd',
'M ' +
startx +
lineStartX +
',' +
lineStartY +
' C ' +
(startx + 60) +
(lineStartX + 60) +
',' +
(lineStartY - 10) +
' ' +
@@ -590,38 +640,70 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE ||
type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED;
let x = 0;
if (isBidirectional || isReverseArrowType) {
const SEQUENCE_NUMBER_RADIUS = 6;
const SEQUENCE_NUMBER_RADIUS = 6;
const hasCentralConn = hasCentralConnection(msg, diagObj);
let lineStartX = startx;
let lineStopX = stopx;
if (isBidirectional) {
// For bidirectional arrows, adjust the start position
if (startx < stopx) {
line.attr('x1', startx + 2 * SEQUENCE_NUMBER_RADIUS);
lineStartX = startx + SEQUENCE_NUMBER_RADIUS + (hasCentralConn ? 5 : 0);
} else {
line.attr('x1', startx + SEQUENCE_NUMBER_RADIUS);
lineStartX = startx - SEQUENCE_NUMBER_RADIUS + (hasCentralConn ? -5 : 0);
}
x = 3.5;
line.attr('x1', lineStartX);
} else if (isReverseArrowType) {
// For reverse arrows, adjust the stop position (where the arrowhead is)
if (stopx > startx) {
lineStopX = stopx - 2 * SEQUENCE_NUMBER_RADIUS;
} else {
lineStopX = stopx - SEQUENCE_NUMBER_RADIUS;
lineStartX +=
msg?.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_DUAL ||
msg?.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_REVERSE
? -7.5
: 0;
}
lineStopX += hasCentralConn ? 15 : 0;
line.attr('x2', lineStopX);
line.attr('x1', lineStartX);
} else {
line.attr('x1', startx + SEQUENCE_NUMBER_RADIUS);
}
// Calculate autonumber X position
let autonumberX = 0;
const isSelfMessage = startx === stopx;
const isLeftToRight = startx <= stopx;
if (isSelfMessage) {
autonumberX = msgModel.fromBounds + 1;
} else if (isReverseArrowType) {
autonumberX = isLeftToRight ? msgModel.toBounds - 1 : msgModel.fromBounds + 1;
} else {
autonumberX = isLeftToRight ? msgModel.fromBounds + 1 : msgModel.toBounds - 1;
}
diagram
.append('line')
.attr('x1', startx)
.attr('x1', autonumberX)
.attr('y1', lineStartY)
.attr('x2', startx)
.attr('x2', autonumberX)
.attr('y2', lineStartY)
.attr('stroke-width', 0)
.attr('marker-start', 'url(' + url + '#sequencenumber)')
.attr('transform', `translate(-${x}, 0)`);
.attr('marker-start', 'url(' + url + '#sequencenumber)');
diagram
.append('text')
.attr('x', startx)
.attr('x', autonumberX)
.attr('y', lineStartY + 4)
.attr('font-family', 'sans-serif')
.attr('font-size', '12px')
.attr('text-anchor', 'middle')
.attr('class', 'sequenceNumber')
.text(sequenceIndex)
.attr('transform', `translate(-${x}, 0)`);
.text(sequenceIndex);
}
};
@@ -1657,6 +1739,47 @@ const calculateCentralConnectionOffset = function (msg, diagObj, isArrowToRight)
return offset;
};
/**
* Check if a message is a reverse arrow type
* @param msg - The message object
* @param diagObj - The diagram object containing LINETYPE constants
* @returns True if the message is a reverse arrow type
*/
const isReverseArrowType = function (msg, diagObj) {
const {
SOLID_ARROW_TOP_REVERSE,
SOLID_ARROW_TOP_REVERSE_DOTTED,
SOLID_ARROW_BOTTOM_REVERSE,
SOLID_ARROW_BOTTOM_REVERSE_DOTTED,
STICK_ARROW_TOP_REVERSE,
STICK_ARROW_TOP_REVERSE_DOTTED,
STICK_ARROW_BOTTOM_REVERSE,
STICK_ARROW_BOTTOM_REVERSE_DOTTED,
} = diagObj.db.LINETYPE;
return [
SOLID_ARROW_TOP_REVERSE,
SOLID_ARROW_TOP_REVERSE_DOTTED,
SOLID_ARROW_BOTTOM_REVERSE,
SOLID_ARROW_BOTTOM_REVERSE_DOTTED,
STICK_ARROW_TOP_REVERSE,
STICK_ARROW_TOP_REVERSE_DOTTED,
STICK_ARROW_BOTTOM_REVERSE,
STICK_ARROW_BOTTOM_REVERSE_DOTTED,
].includes(msg.type);
};
/**
* Check if a message is a bidirectional arrow type
* @param msg - The message object
* @param diagObj - The diagram object containing LINETYPE constants
* @returns True if the message is a bidirectional arrow type
*/
const isBidirectionalArrowType = function (msg, diagObj) {
const { BIDIRECTIONAL_SOLID, BIDIRECTIONAL_DOTTED } = diagObj.db.LINETYPE;
return [BIDIRECTIONAL_SOLID, BIDIRECTIONAL_DOTTED].includes(msg.type);
};
const buildMessageModel = function (msg, actors, diagObj) {
if (
![

View File

@@ -681,8 +681,8 @@ const drawActorTypeControl = function (elem, actor, conf, isFooter) {
rect.class = 'actor';
const cx = actor.x + actor.width / 2;
const cy = actorY + 30;
const r = 18;
const cy = actorY + 32;
const r = 22;
actElem
.append('defs')
@@ -719,7 +719,7 @@ const drawActorTypeControl = function (elem, actor, conf, isFooter) {
actor.description,
actElem,
rect.x,
rect.y + r + (isFooter ? 5 : 10),
rect.y + r + (!isFooter ? 12 : 5),
rect.width,
rect.height,
{ class: `actor ${ACTOR_MAN_FIGURE_CLASS}` },
@@ -737,7 +737,7 @@ const drawActorTypeEntity = function (elem, actor, conf, isFooter) {
const line = elem.append('g').lower();
const actElem = elem.append('g');
let cssClass = ACTOR_MAN_FIGURE_CLASS;
let cssClass = 'actor';
if (isFooter) {
cssClass += ` ${BOTTOM_ACTOR_CLASS}`;
} else {
@@ -756,7 +756,7 @@ const drawActorTypeEntity = function (elem, actor, conf, isFooter) {
const cx = actor.x + actor.width / 2;
const cy = actorY + (!isFooter ? 25 : 10);
const r = 18;
const r = 22;
actElem
.append('circle')
@@ -772,7 +772,6 @@ const drawActorTypeEntity = function (elem, actor, conf, isFooter) {
.attr('x2', cx + r)
.attr('y1', cy + r)
.attr('y2', cy + r)
.attr('stroke', '#333')
.attr('stroke-width', 2);
const bounds = actElem.node().getBBox();
@@ -799,7 +798,7 @@ const drawActorTypeEntity = function (elem, actor, conf, isFooter) {
actor.description,
actElem,
rect.x,
rect.y + (!isFooter ? (cy + r - actorY) / 2 : (cy - actorY + r - 5) / 2),
rect.y + (!isFooter ? 30 : 15),
rect.width,
rect.height,
{ class: `actor ${ACTOR_MAN_FIGURE_CLASS}` },
@@ -807,9 +806,9 @@ const drawActorTypeEntity = function (elem, actor, conf, isFooter) {
);
if (!isFooter) {
actElem.attr('transform', `translate(${0}, ${r / 2})`);
actElem.attr('transform', `translate(${0}, ${r / 2 - 5})`);
} else {
actElem.attr('transform', `translate(${0}, ${r / 2})`);
actElem.attr('transform', `translate(${0}, ${r})`);
}
return actor.height;
@@ -872,8 +871,8 @@ const drawActorTypeDatabase = function (elem, actor, conf, isFooter) {
// Cylinder dimensions
rect.x = actor.x;
rect.y = actorY;
const w = rect.width / 4;
const h = rect.width / 4;
const w = rect.width / 3;
const h = rect.width / 3;
const rx = w / 2;
const ry = rx / (2.5 + w / 50);
@@ -897,17 +896,14 @@ const drawActorTypeDatabase = function (elem, actor, conf, isFooter) {
.attr('stroke-width', 1)
.attr('class', cssclass);
if (!isFooter) {
cylinderGroup.attr('transform', `translate(${w * 1.5}, ${(rect.height + ry) / 4})`);
} else {
cylinderGroup.attr('transform', `translate(${w * 1.5}, ${rect.height / 4 - 2 * ry})`);
}
cylinderGroup.attr('transform', `translate(${w}, ${ry})`);
actor.rectData = rect;
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
actor.description,
g,
rect.x,
rect.y + (!isFooter ? (rect.height + ry) / 2 : (rect.height + h) / 4),
rect.y + 35,
rect.width,
rect.height,
{ class: `actor ${ACTOR_BOX_CLASS}` },
@@ -927,7 +923,7 @@ const drawActorTypeBoundary = function (elem, actor, conf, isFooter) {
const actorY = isFooter ? actor.stopy : actor.starty;
const center = actor.x + actor.width / 2;
const centerY = actorY + 80;
const radius = 30;
const radius = 22;
const line = elem.append('g').lower();
if (!isFooter) {
@@ -968,22 +964,22 @@ const drawActorTypeBoundary = function (elem, actor, conf, isFooter) {
.append('line')
.attr('id', 'actor-man-torso' + actorCnt)
.attr('x1', actor.x + actor.width / 2 - radius * 2.5)
.attr('y1', actorY + 10)
.attr('y1', actorY + 12)
.attr('x2', actor.x + actor.width / 2 - 15)
.attr('y2', actorY + 10);
.attr('y2', actorY + 12);
actElem
.append('line')
.attr('id', 'actor-man-arms' + actorCnt)
.attr('x1', actor.x + actor.width / 2 - radius * 2.5)
.attr('y1', actorY + 0) // starting Y
.attr('y1', actorY + 2) // starting Y
.attr('x2', actor.x + actor.width / 2 - radius * 2.5)
.attr('y2', actorY + 20); // ending Y (26px long, adjust as needed)
.attr('y2', actorY + 22); // ending Y (26px long, adjust as needed)
actElem
.append('circle')
.attr('cx', actor.x + actor.width / 2)
.attr('cy', actorY + 10)
.attr('cy', actorY + 12)
.attr('r', radius);
const bounds = actElem.node().getBBox();
@@ -993,7 +989,7 @@ const drawActorTypeBoundary = function (elem, actor, conf, isFooter) {
actor.description,
actElem,
rect.x,
rect.y + (!isFooter ? radius / 2 + 3 : radius / 2 - 4),
rect.y + 15,
rect.width,
rect.height,
{ class: `actor ${ACTOR_MAN_FIGURE_CLASS}` },
@@ -1001,9 +997,9 @@ const drawActorTypeBoundary = function (elem, actor, conf, isFooter) {
);
if (!isFooter) {
actElem.attr('transform', `translate(0,${radius / 2 + 7})`);
actElem.attr('transform', `translate(0,${radius / 2 + 10})`);
} else {
actElem.attr('transform', `translate(0,${radius / 2 + 7})`);
actElem.attr('transform', `translate(0,${radius / 2 + 10})`);
}
return actor.height;