diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 412f9b2..624c317 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -16,24 +16,23 @@ on: jobs: build-and-test: runs-on: ubuntu-latest - steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v6 with: - dotnet-version: 9.0.x + dotnet-version: | + 8.0.x + 9.0.x + 10.0.x - name: Check formatter rules run: dotnet format --verify-no-changes --verbosity diagnostic - - name: Restore dependencies - run: dotnet restore - - name: Build - run: dotnet build --no-restore + run: dotnet build - name: Test run: dotnet test --no-build --verbosity normal diff --git a/src/Mermaid.Flowcharts/Flowchart.cs b/src/Mermaid.Flowcharts/Flowchart.cs index 6859daa..7f654eb 100644 --- a/src/Mermaid.Flowcharts/Flowchart.cs +++ b/src/Mermaid.Flowcharts/Flowchart.cs @@ -42,12 +42,18 @@ public Flowchart AddNode(INode node) public Flowchart AddLink(Link link) { - if (AllNodeChildren.Any(link.Source.Equals) && AllNodeChildren.Any(link.Destination.Equals)) + if (!AllNodeChildren.Any(link.Source.Equals)) { - _links.Add(link); - return this; + throw new InvalidOperationException("Cannot add link to flowchart: the source node should be present within the flowchart."); + } + + if (!AllNodeChildren.Any(link.Destination.Equals)) + { + throw new InvalidOperationException("Cannot add link to flowchart: the destination node should be present within the flowchart."); } - throw new InvalidOperationException("Cannot add link to flowchart: the source and the destination nodes should both be present within the flowchart."); + + _links.Add(link); + return this; } public override string ToString() diff --git a/src/Mermaid.Flowcharts/Subgraphs/Subgraph.cs b/src/Mermaid.Flowcharts/Subgraphs/Subgraph.cs index 61036b8..114a45c 100644 --- a/src/Mermaid.Flowcharts/Subgraphs/Subgraph.cs +++ b/src/Mermaid.Flowcharts/Subgraphs/Subgraph.cs @@ -16,7 +16,7 @@ public record Subgraph : INode public IEnumerable Nodes => _nodes.OfType(); public IEnumerable Subgraphs => _nodes.OfType(); public IEnumerable Links => _links.AsReadOnly(); - public IEnumerable AllNodeChildren => _nodes.Concat(Subgraphs.SelectMany(subgraph => subgraph._nodes)); + public IEnumerable AllNodeChildren => _nodes.Concat(Subgraphs.SelectMany(subgraph => subgraph.AllNodeChildren)); public IEnumerable AllNodes => Nodes.Concat(Subgraphs.SelectMany(subgraph => subgraph.AllNodes)); public IEnumerable AllLinks => Links.Concat(Subgraphs.SelectMany(subgraph => subgraph.AllLinks)); diff --git a/tests/Mermaid.Flowcharts.Tests/FlowchartTests.cs b/tests/Mermaid.Flowcharts.Tests/FlowchartTests.cs index dc9c9d7..025c292 100644 --- a/tests/Mermaid.Flowcharts.Tests/FlowchartTests.cs +++ b/tests/Mermaid.Flowcharts.Tests/FlowchartTests.cs @@ -123,7 +123,7 @@ public void Flowchart_WhenLinkSourceNotPresent_AddLinkShouldThrow() InvalidOperationException exception = Assert.Throws( () => flowchart.AddLink(link) ); - Assert.Contains("Cannot add link to flowchart: the source and the destination nodes should both be present within the flowchart.", exception.Message); + Assert.Contains("Cannot add link to flowchart: the source node should be present within the flowchart.", exception.Message); } [Fact] @@ -140,7 +140,7 @@ public void Flowchart_WhenLinkDestinationNotPresent_AddLinkShouldThrow() InvalidOperationException exception = Assert.Throws( () => flowchart.AddLink(link) ); - Assert.Contains("Cannot add link to flowchart: the source and the destination nodes should both be present within the flowchart.", exception.Message); + Assert.Contains("Cannot add link to flowchart: the destination node should be present within the flowchart.", exception.Message); } [Fact] @@ -805,4 +805,80 @@ subgraph ssg ["Subsubgraph"] // Assert Assert.Equal(expected, actual); } + + [Fact] + public void Flowchart_ShouldLinkToNodeThreeSubgraphLevelsDeep() + { + Flowchart flowchart = new(); + Node top = Node.Create("top", "Top"); + Subgraph outer = Subgraph.Create("outer", "Outer"); + Subgraph middle = Subgraph.Create("middle", "Middle"); + Subgraph inner = Subgraph.Create("inner", "Inner"); + Node deep = Node.Create("deep", "Deep"); + + inner.AddNode(deep); + middle.AddNode(inner); + outer.AddNode(middle); + flowchart.AddNode(top).AddNode(outer); + flowchart.AddLink(Link.Create(top, deep)); + + string expected = + """ + flowchart TD + top["Top"] + + subgraph outer ["Outer"] + + subgraph middle ["Middle"] + + subgraph inner ["Inner"] + deep["Deep"] + end + end + end + + top ---> deep + + """; + + Assert.Equal(expected, flowchart.ToMermaidString(0, " ")); + } + + [Fact] + public void Subgraph_ShouldLinkToNodeNestedInsideInnerSubgraph() + { + Flowchart flowchart = new(); + Node a = Node.Create("a", "A"); + Subgraph outer = Subgraph.Create("outer", "Outer"); + Subgraph middle = Subgraph.Create("middle", "Middle"); + Subgraph inner = Subgraph.Create("inner", "Inner"); + Node deep = Node.Create("deep", "Deep"); + + inner.AddNode(deep); + middle.AddNode(inner); + outer.AddNode(a).AddNode(middle); + flowchart.AddNode(outer); + outer.AddLink(Link.Create(a, deep)); + + string expected = + """ + flowchart TD + + subgraph outer ["Outer"] + a["A"] + + subgraph middle ["Middle"] + + subgraph inner ["Inner"] + deep["Deep"] + end + end + + a ---> deep + end + + """; + + Assert.Equal(expected, flowchart.ToMermaidString(0, " ")); + } }