diff --git a/README.md b/README.md index 612220a..2b4a5ae 100644 --- a/README.md +++ b/README.md @@ -87,8 +87,7 @@ dotnet run --project src/ProjGraph.Mcp projgraph visualize ./MySolution.sln # Mermaid format for documentation -projgraph visualize ./MySolution.slnx --format mermaid > docs/dependencies.mmd -``` +projgraph visualize ./MySolution.slnx --format mermaid > docs/dependencies.mmd``` ### Generate Database Diagrams diff --git a/samples/visualize/simple-dependencies/README.md b/samples/visualize/simple-dependencies/README.md index f82fe38..ce99a20 100644 --- a/samples/visualize/simple-dependencies/README.md +++ b/samples/visualize/simple-dependencies/README.md @@ -14,7 +14,7 @@ projgraph visualize A/A.csproj **Output:** -``` +```bash Projects ├── 📦 A │ └── → B diff --git a/specs/001-cli-graph-rendering/spec.md b/specs/001-cli-graph-rendering/spec.md index 496b521..db049fe 100644 --- a/specs/001-cli-graph-rendering/spec.md +++ b/specs/001-cli-graph-rendering/spec.md @@ -78,6 +78,7 @@ As a modern .NET developer, I want the tool to support the new XML-based `.slnx` - **Description**: Analyzes a .NET solution or project file and returns the dependency graph as a set of nodes and edges. - **Parameters**: - `path`: (string) The absolute path to the .sln, .slnx, or .csproj file. + - `show_title`: (boolean, optional) Whether to include the title in the diagram (default: true). - `includePackages`: (boolean) (Out of scope for initial version) Whether to include NuGet package dependencies in the graph. ## Success Criteria *(mandatory)* diff --git a/specs/002-dbcontext-erd/spec.md b/specs/002-dbcontext-erd/spec.md index ffb803b..e52e67c 100644 --- a/specs/002-dbcontext-erd/spec.md +++ b/specs/002-dbcontext-erd/spec.md @@ -82,6 +82,7 @@ As an AI Assistant, I want to retrieve a Mermaid ERD of the project's data model - **Parameters**: - `path`: (string) Absolute path to the DbContext .cs file. - `contextName`: (string, optional) Specific DbContext class name to use if multiple are present. + - `show_title`: (boolean, optional) Whether to include the title in the diagram (default: true). ## Success Criteria *(mandatory)* diff --git a/specs/003-mermaid-class-diagram/spec.md b/specs/003-mermaid-class-diagram/spec.md index 5d8242c..3619e13 100644 --- a/specs/003-mermaid-class-diagram/spec.md +++ b/specs/003-mermaid-class-diagram/spec.md @@ -96,3 +96,4 @@ As a developer, I want to control the depth of the discovery so the diagram does - `includeInheritance`: (boolean) Whether to search the workspace for base classes and interfaces. - `includeDependencies`: (boolean) Whether to search for and include classes used by the target types. - `depth`: (integer) How many levels of relationships to follow (default: 1). + - `show_title`: (boolean, optional) Whether to include the title in the diagram (default: true). diff --git a/src/ProjGraph.Cli/Commands/ClassDiagramCommand.cs b/src/ProjGraph.Cli/Commands/ClassDiagramCommand.cs index 3a8b926..0061c7e 100644 --- a/src/ProjGraph.Cli/Commands/ClassDiagramCommand.cs +++ b/src/ProjGraph.Cli/Commands/ClassDiagramCommand.cs @@ -59,6 +59,14 @@ public sealed class Settings : CommandSettings [DefaultValue(1)] public int Depth { get; init; } + /// + /// Gets or sets a value indicating whether to include the title in the rendered output. + /// + [CommandOption("--show-title ")] + [Description("Include the diagram title (default true)")] + [DefaultValue(true)] + public bool ShowTitle { get; init; } = true; + /// /// Validates the settings provided by the user. /// Ensures the file path is valid, exists, and points to a .cs file. @@ -108,8 +116,8 @@ public override async Task ExecuteAsync( settings.IncludeDependencies, settings.Depth); - var mermaid = mermaidRenderer.Render(model); - console.WriteLine(mermaid); + var mermaidOutput = mermaidRenderer.Render(model, new DiagramOptions(settings.ShowTitle)); + console.WriteLine(mermaidOutput); return 0; } diff --git a/src/ProjGraph.Cli/Commands/ErdCommand.cs b/src/ProjGraph.Cli/Commands/ErdCommand.cs index 29871df..5f73bd0 100644 --- a/src/ProjGraph.Cli/Commands/ErdCommand.cs +++ b/src/ProjGraph.Cli/Commands/ErdCommand.cs @@ -49,6 +49,14 @@ public sealed class Settings : CommandSettings [Description("The name of the DbContext or ModelSnapshot to analyze (optional)")] public string? ContextName { get; init; } + /// + /// Gets or sets a value indicating whether to include the title in the rendered output. + /// + [CommandOption("--show-title ")] + [Description("Include the diagram title (default true)")] + [DefaultValue(true)] + public bool ShowTitle { get; init; } = true; + /// /// Validates the settings provided for the command. /// Ensures that the specified path exists, is a .cs file, or is left empty to search the current directory. @@ -107,8 +115,8 @@ public override async Task ExecuteAsync( var model = await AnalyzeModelAsync(targetPath, settings.ContextName, cancellationToken); - var mermaid = mermaidRenderer.Render(model); - console.WriteLine(mermaid); + var mermaidOutput = mermaidRenderer.Render(model, new DiagramOptions(settings.ShowTitle)); + console.WriteLine(mermaidOutput); return 0; } diff --git a/src/ProjGraph.Cli/Commands/VisualizeCommand.cs b/src/ProjGraph.Cli/Commands/VisualizeCommand.cs index 1e33342..5a4e0b1 100644 --- a/src/ProjGraph.Cli/Commands/VisualizeCommand.cs +++ b/src/ProjGraph.Cli/Commands/VisualizeCommand.cs @@ -24,6 +24,10 @@ public sealed class VisualizeCommand( IOutputConsole console) : AsyncCommand { + private const string FormatMermaid = "mermaid"; + private const string FormatTree = "tree"; + private const string FormatFlat = "flat"; + /// /// Represents the settings for the `VisualizeCommand`. /// @@ -49,6 +53,14 @@ public sealed class Settings : CommandSettings [DefaultValue("mermaid")] public string Format { get; private set; } = "mermaid"; + /// + /// Gets or sets a value indicating whether to include the title in the rendered output. + /// + [CommandOption("--show-title ")] + [Description("Include the diagram title (default true)")] + [DefaultValue(true)] + public bool ShowTitle { get; init; } = true; + /// /// Validates the settings provided for the command. /// Ensures that the specified path exists, is valid, and that the format is "flat", "tree" or "mermaid". @@ -69,7 +81,7 @@ public override ValidationResult Validate() } Format = Format.ToLowerInvariant(); - if (Format != "flat" && Format != "tree" && Format != "mermaid") + if (Format != FormatFlat && Format != FormatTree && Format != FormatMermaid) { return ValidationResult.Error("Format must be 'flat', 'tree' or 'mermaid'"); } @@ -101,13 +113,13 @@ public override async Task ExecuteAsync( { try { - if (settings.Format.Equals("mermaid", StringComparison.OrdinalIgnoreCase)) + if (settings.Format.Equals(FormatMermaid, StringComparison.OrdinalIgnoreCase)) { // For mermaid, we want clean stdout, so all status goes to stderr console.WriteInfo($"Analyzing {settings.Path}..."); var graph = await Task.Run(() => graphService.BuildGraph(settings.Path), cancellationToken); - console.WriteLine(GetRenderer(settings.Format).Render(graph)); + console.WriteLine(GetRenderer(settings.Format).Render(graph, new DiagramOptions(settings.ShowTitle))); } else { @@ -126,7 +138,7 @@ await AnsiConsole.Status() return 0; } - console.WriteLine(GetRenderer(settings.Format).Render(graph)); + console.WriteLine(GetRenderer(settings.Format).Render(graph, new DiagramOptions(settings.ShowTitle))); } return 0; @@ -152,9 +164,9 @@ private IDiagramRenderer GetRenderer(string format) { return format.ToLowerInvariant() switch { - "mermaid" => renderers.OfType().First(), - "tree" => renderers.OfType().First(), - "flat" => renderers.OfType().First(), + FormatMermaid => renderers.OfType().First(), + FormatTree => renderers.OfType().First(), + FormatFlat => renderers.OfType().First(), _ => throw new ArgumentException($"Unsupported format: {format}") }; } diff --git a/src/ProjGraph.Cli/README.md b/src/ProjGraph.Cli/README.md index 5d1ae93..4dd94ba 100644 --- a/src/ProjGraph.Cli/README.md +++ b/src/ProjGraph.Cli/README.md @@ -21,8 +21,17 @@ projgraph visualize ./MySolution.sln # Mermaid diagram projgraph visualize ./MySolution.slnx --format mermaid > graph.mmd + +# Mermaid diagram without title header +projgraph visualize ./MySolution.slnx --format mermaid --show-title false ``` +**Settings**: + +- ``: Path to `.sln`, `.slnx`, or `.csproj` file. +- `-f|--format`: Output format (`flat`, `tree`, `mermaid`). Default: `mermaid`. +- `--show-title `: Include diagram title. Default: `true`. + **Supports**: `.sln`, `.slnx`, `.csproj` **Example output**: @@ -46,8 +55,17 @@ projgraph erd ./Migrations/MyDbContextModelSnapshot.cs # Save to file projgraph erd ./Data/MyDbContext.cs > database-schema.md + +# Generate without title header +projgraph erd ./Data/MyDbContext.cs --show-title false ``` +**Settings**: + +- `[path]`: Optional path to `.cs` file. Searches current directory if not specified. +- `-c|--context `: Optional context/snapshot name. +- `--show-title `: Include diagram title. Default: `true`. + **Features**: - Detects entities, properties, and relationships from source or snapshots @@ -83,10 +101,21 @@ Generate Mermaid Class Diagram for a specific class and its hierarchy. # Analyze a specific class and discover its base types and dependencies projgraph classdiagram ./Models/Admin.cs -# Specify depth of discovery (default: 3) +# Specify depth of discovery (default: 1) projgraph classdiagram ./Models/Admin.cs --depth 5 + +# Generate without title header +projgraph classdiagram ./Models/Admin.cs --show-title false ``` +**Settings**: + +- ``: Path to the `.cs` file. +- `-i|--inheritance`: Include base classes/interfaces. Default: `false`. +- `-d|--dependencies`: Include dependent types. Default: `false`. +- `--depth `: Max discovery depth. Default: `1`. +- `--show-title `: Include diagram title. Default: `true`. + **Features**: - Detects properties, fields, and inheritance (`<|--`) diff --git a/src/ProjGraph.Lib.ClassDiagram/Rendering/MermaidClassDiagramRenderer.cs b/src/ProjGraph.Lib.ClassDiagram/Rendering/MermaidClassDiagramRenderer.cs index 7dbbed4..d6cb5b0 100644 --- a/src/ProjGraph.Lib.ClassDiagram/Rendering/MermaidClassDiagramRenderer.cs +++ b/src/ProjGraph.Lib.ClassDiagram/Rendering/MermaidClassDiagramRenderer.cs @@ -13,13 +13,14 @@ public sealed class MermaidClassDiagramRenderer : IDiagramRenderer /// Renders a ClassModel as a Mermaid class diagram. /// /// The ClassModel containing the types, relationships, and an optional title to be rendered. + /// The options for rendering the diagram. /// A string representation of the Mermaid class diagram. - public string Render(ClassModel model) + public string Render(ClassModel model, DiagramOptions? options = null) { var sb = new StringBuilder(); sb.AppendLine("```mermaid"); - if (!string.IsNullOrWhiteSpace(model.Title)) + if ((options?.ShowTitle ?? true) && !string.IsNullOrWhiteSpace(model.Title)) { sb.AppendLine("---"); sb.AppendLine($"title: {model.Title}"); diff --git a/src/ProjGraph.Lib.Core/Abstractions/DiagramOptions.cs b/src/ProjGraph.Lib.Core/Abstractions/DiagramOptions.cs new file mode 100644 index 0000000..7464a64 --- /dev/null +++ b/src/ProjGraph.Lib.Core/Abstractions/DiagramOptions.cs @@ -0,0 +1,7 @@ +namespace ProjGraph.Lib.Core.Abstractions; + +/// +/// Represents options for rendering a diagram. +/// +/// Whether to include the title in the rendered output. +public record DiagramOptions(bool ShowTitle = true); \ No newline at end of file diff --git a/src/ProjGraph.Lib.Core/Abstractions/IDiagramRenderer.cs b/src/ProjGraph.Lib.Core/Abstractions/IDiagramRenderer.cs index 8451b15..4f7b400 100644 --- a/src/ProjGraph.Lib.Core/Abstractions/IDiagramRenderer.cs +++ b/src/ProjGraph.Lib.Core/Abstractions/IDiagramRenderer.cs @@ -10,6 +10,7 @@ public interface IDiagramRenderer /// Renders the specified model into a string representation. /// /// The model to render. + /// The options for rendering the diagram. /// A string representation of the diagram. - string Render(TModel model); + string Render(TModel model, DiagramOptions? options = null); } \ No newline at end of file diff --git a/src/ProjGraph.Lib.EntityFramework/Rendering/MermaidErdRenderer.cs b/src/ProjGraph.Lib.EntityFramework/Rendering/MermaidErdRenderer.cs index b4de1f8..91af577 100644 --- a/src/ProjGraph.Lib.EntityFramework/Rendering/MermaidErdRenderer.cs +++ b/src/ProjGraph.Lib.EntityFramework/Rendering/MermaidErdRenderer.cs @@ -14,15 +14,16 @@ public sealed class MermaidErdRenderer : IDiagramRenderer /// Renders a Mermaid ERD diagram from the given Entity Framework model. /// /// The EF model containing entities and relationships to be rendered. + /// The options for rendering the diagram. /// /// A string representing the ERD in Mermaid syntax, which can be used to visualize the model. /// - public string Render(EfModel model) + public string Render(EfModel model, DiagramOptions? options = null) { var sb = new StringBuilder(); sb.AppendLine("```mermaid"); - if (!string.IsNullOrWhiteSpace(model.ContextName)) + if ((options?.ShowTitle ?? true) && !string.IsNullOrWhiteSpace(model.ContextName)) { sb.AppendLine("---"); sb.AppendLine($"title: {model.ContextName}"); diff --git a/src/ProjGraph.Lib.ProjectGraph/Rendering/FlatGraphRenderer.cs b/src/ProjGraph.Lib.ProjectGraph/Rendering/FlatGraphRenderer.cs index 08b6bcb..9cee7c7 100644 --- a/src/ProjGraph.Lib.ProjectGraph/Rendering/FlatGraphRenderer.cs +++ b/src/ProjGraph.Lib.ProjectGraph/Rendering/FlatGraphRenderer.cs @@ -1,4 +1,5 @@ using ProjGraph.Core.Models; +using ProjGraph.Lib.Core.Abstractions; using Spectre.Console; namespace ProjGraph.Lib.ProjectGraph.Rendering; @@ -12,17 +13,18 @@ public sealed class FlatGraphRenderer : SolutionGraphRendererBase /// Renders a as a flat list of projects and their direct dependencies. /// /// The to render. + /// The options for rendering the diagram. /// A string representation of the solution graph rendered as a flat list. /// /// This method renders each project in sorted order (by type and name) followed by its direct dependencies. /// Projects involved in cyclic dependencies are highlighted in red. Cycle detection is performed using the /// method. /// - public override string Render(SolutionGraph graph) + public override string Render(SolutionGraph graph, DiagramOptions? options = null) { _writer.GetStringBuilder().Clear(); - RenderHeader(graph); + RenderHeader(graph, options); var cyclicProjectIds = GetCyclicProjectIds(graph); diff --git a/src/ProjGraph.Lib.ProjectGraph/Rendering/MermaidGraphRenderer.cs b/src/ProjGraph.Lib.ProjectGraph/Rendering/MermaidGraphRenderer.cs index e7f449e..54b9296 100644 --- a/src/ProjGraph.Lib.ProjectGraph/Rendering/MermaidGraphRenderer.cs +++ b/src/ProjGraph.Lib.ProjectGraph/Rendering/MermaidGraphRenderer.cs @@ -13,13 +13,14 @@ public sealed class MermaidGraphRenderer : IDiagramRenderer /// Renders a solution graph into a Mermaid.js graph definition. /// /// The solution graph to render. + /// The options for rendering the diagram. /// A string containing the Mermaid.js graph definition. - public string Render(SolutionGraph graph) + public string Render(SolutionGraph graph, DiagramOptions? options = null) { var sb = new StringBuilder(); sb.AppendLine("```mermaid"); - if (!string.IsNullOrWhiteSpace(graph.Name)) + if ((options?.ShowTitle ?? true) && !string.IsNullOrWhiteSpace(graph.Name)) { sb.AppendLine("---"); sb.AppendLine($"title: {graph.Name}"); diff --git a/src/ProjGraph.Lib.ProjectGraph/Rendering/SolutionGraphRendererBase.cs b/src/ProjGraph.Lib.ProjectGraph/Rendering/SolutionGraphRendererBase.cs index 179c687..a94dd9b 100644 --- a/src/ProjGraph.Lib.ProjectGraph/Rendering/SolutionGraphRendererBase.cs +++ b/src/ProjGraph.Lib.ProjectGraph/Rendering/SolutionGraphRendererBase.cs @@ -36,17 +36,23 @@ protected SolutionGraphRendererBase() /// Renders the specified to a string representation. /// /// The solution graph to render. + /// The options for rendering the diagram. /// A string representation of the rendered graph. - public abstract string Render(SolutionGraph graph); + public abstract string Render(SolutionGraph graph, DiagramOptions? options = null); /// /// Renders the header section of the solution graph visualization. /// /// The solution graph to render the header for. - protected void RenderHeader(SolutionGraph graph) + /// The options for rendering the diagram. + protected void RenderHeader(SolutionGraph graph, DiagramOptions? options = null) { - var graphName = Markup.Escape(graph.Name.Trim()); - _console.Write(new Rule($"[yellow]Dependency Graph: {graphName}[/]") { Justification = Justify.Left }); + if (options?.ShowTitle ?? true) + { + var graphName = Markup.Escape(graph.Name.Trim()); + _console.Write(new Rule($"[yellow]Dependency Graph: {graphName}[/]") { Justification = Justify.Left }); + } + _console.MarkupLine("[bold blue]Projects[/]"); } @@ -86,9 +92,11 @@ protected void RenderCycleWarning(HashSet cyclicProjectIds) protected static HashSet GetCyclicProjectIds(SolutionGraph graph) { var cycles = TarjanSccAlgorithm.FindStronglyConnectedComponents(graph); - return cycles + return + [ + .. cycles .Where(c => c.Count > 1) .SelectMany(c => c) - .ToHashSet(); + ]; } } \ No newline at end of file diff --git a/src/ProjGraph.Lib.ProjectGraph/Rendering/TreeGraphRenderer.cs b/src/ProjGraph.Lib.ProjectGraph/Rendering/TreeGraphRenderer.cs index 666b9f8..ef4b13f 100644 --- a/src/ProjGraph.Lib.ProjectGraph/Rendering/TreeGraphRenderer.cs +++ b/src/ProjGraph.Lib.ProjectGraph/Rendering/TreeGraphRenderer.cs @@ -1,4 +1,5 @@ using ProjGraph.Core.Models; +using ProjGraph.Lib.Core.Abstractions; using Spectre.Console; namespace ProjGraph.Lib.ProjectGraph.Rendering; @@ -12,17 +13,18 @@ public sealed class TreeGraphRenderer : SolutionGraphRendererBase /// Renders a as a tree structure, displaying project dependencies hierarchically. /// /// The to render. + /// The options for rendering the diagram. /// A string representation of the solution graph rendered as a tree. /// /// This method identifies root projects (those with no incoming dependencies) and renders each as a separate tree branch. /// Projects involved in cyclic dependencies are highlighted in red, and cycle detection is performed using the /// method. /// - public override string Render(SolutionGraph graph) + public override string Render(SolutionGraph graph, DiagramOptions? options = null) { _writer.GetStringBuilder().Clear(); - RenderHeader(graph); + RenderHeader(graph, options); // Identify incoming dependency counts to find roots var incomingCounts = graph.Projects.ToDictionary(p => p.Id, _ => 0); diff --git a/src/ProjGraph.Mcp/Program.cs b/src/ProjGraph.Mcp/Program.cs index 0f8beb0..e2f6b0b 100644 --- a/src/ProjGraph.Mcp/Program.cs +++ b/src/ProjGraph.Mcp/Program.cs @@ -57,12 +57,15 @@ public async Task GetClassDiagram( [Description("Whether to search for and include other classes used as properties or fields.")] bool includeDependencies = false, [Description("How many levels of relationships to follow (default: 1).")] - int depth = 1) + int depth = 1, + [Description("Whether to include the title in the diagram (default: true).")] + bool showTitle = true) { try { var model = await classService.AnalyzeFileAsync(filePath, includeInheritance, includeDependencies, depth); - return classRenderer.Render(model); + + return classRenderer.Render(model, new DiagramOptions(showTitle)); } catch (Exception ex) { @@ -74,12 +77,15 @@ public async Task GetClassDiagram( [Description("Analyzes a .NET solution or project file and returns the dependency graph as a Mermaid diagram.")] public string GetProjectGraph( [Description("Absolute path to the project or solution file.")] - string path) + string path, + [Description("Whether to include the title in the diagram (default: true).")] + bool showTitle = true) { try { var graph = graphService.BuildGraph(path); - return graphRenderer.Render(graph); + + return graphRenderer.Render(graph, new DiagramOptions(showTitle)); } catch (Exception ex) { @@ -96,12 +102,15 @@ public async Task GetErd( [Description("Absolute path to the DbContext .cs file.")] string path, [Description("Specific DbContext class name to use if multiple are present.")] - string? contextName = null) + string? contextName = null, + [Description("Whether to include the title in the diagram (default: true).")] + bool showTitle = true) { try { var model = await efService.AnalyzeContextAsync(path, contextName); - return erdRenderer.Render(model); + + return erdRenderer.Render(model, new DiagramOptions(showTitle)); } catch (Exception ex) { diff --git a/src/ProjGraph.Mcp/README.md b/src/ProjGraph.Mcp/README.md index 6d22b58..b583faf 100644 --- a/src/ProjGraph.Mcp/README.md +++ b/src/ProjGraph.Mcp/README.md @@ -16,6 +16,7 @@ Analyzes a solution or project file and returns the dependency graph as a Mermai **Parameters:** - `path` (string): Absolute path to `.sln`, `.slnx`, or `.csproj` file +- `show_title` (boolean, optional): Whether to include the title in the diagram (default: true) **Returns:** Mermaid graph diagram code @@ -35,6 +36,7 @@ Generates a Mermaid Entity Relationship Diagram from an EF Core `DbContext` or ` - `path` (string): Absolute path to `.cs` file containing a `DbContext` or `ModelSnapshot` - `contextName` (string, optional): Specific class name if multiple exist in the file +- `show_title` (boolean, optional): Whether to include the title in the diagram (default: true) **Returns:** Mermaid ERD diagram code @@ -68,6 +70,7 @@ related types in the workspace. - `includeDependencies` (boolean, optional): Whether to search for and include other classes used as properties or fields (default: false) - `depth` (number, optional): How many levels of relationships to follow (default: 1) +- `show_title` (boolean, optional): Whether to include the title in the diagram (default: true) **Returns:** Mermaid class diagram code diff --git a/tests/ProjGraph.Tests.Contract/McpClassDiagramTests.cs b/tests/ProjGraph.Tests.Contract/McpClassDiagramTests.cs index 4750413..556446d 100644 --- a/tests/ProjGraph.Tests.Contract/McpClassDiagramTests.cs +++ b/tests/ProjGraph.Tests.Contract/McpClassDiagramTests.cs @@ -28,7 +28,7 @@ public void GetClassDiagram_ShouldHave_CorrectSignature() // Assert method has Description attribute var descAttr = method.GetCustomAttribute(); descAttr.Should().NotBeNull(); - descAttr!.Description.Should().Contain("class diagram"); + descAttr.Description.Should().Contain("class diagram"); } [Fact] @@ -44,8 +44,24 @@ public void GetClassDiagram_ShouldHave_RequiredParameters() pathParam.GetCustomAttribute().Should().NotBeNull(); // Check optional flags - parameters.Should().ContainSingle(p => p.Name == "includeInheritance"); - parameters.Should().ContainSingle(p => p.Name == "includeDependencies"); - parameters.Should().ContainSingle(p => p.Name == "depth"); + var inheritanceParam = parameters.Should().ContainSingle(p => p.Name == "includeInheritance").Which; + inheritanceParam.ParameterType.Should().Be(); + inheritanceParam.IsOptional.Should().BeTrue(); + inheritanceParam.DefaultValue.Should().Be(false); + + var dependenciesParam = parameters.Should().ContainSingle(p => p.Name == "includeDependencies").Which; + dependenciesParam.ParameterType.Should().Be(); + dependenciesParam.IsOptional.Should().BeTrue(); + dependenciesParam.DefaultValue.Should().Be(false); + + var depthParam = parameters.Should().ContainSingle(p => p.Name == "depth").Which; + depthParam.ParameterType.Should().Be(); + depthParam.IsOptional.Should().BeTrue(); + depthParam.DefaultValue.Should().Be(1); + + var titleParam = parameters.Should().ContainSingle(p => p.Name == "showTitle").Which; + titleParam.ParameterType.Should().Be(); + titleParam.IsOptional.Should().BeTrue(); + titleParam.DefaultValue.Should().Be(true); } } \ No newline at end of file diff --git a/tests/ProjGraph.Tests.Contract/McpContractTests.cs b/tests/ProjGraph.Tests.Contract/McpContractTests.cs index 22c9231..837aa97 100644 --- a/tests/ProjGraph.Tests.Contract/McpContractTests.cs +++ b/tests/ProjGraph.Tests.Contract/McpContractTests.cs @@ -63,15 +63,23 @@ public void GetProjectGraph_ShouldHave_PathParameter() } [Fact] - public void GetProjectGraph_ShouldHave_OnlyRequiredParameters() + public void GetProjectGraph_ShouldHave_Parameters() { // Arrange var type = typeof(ProjGraphTools); var method = type.GetMethod("GetProjectGraph"); var parameters = method!.GetParameters(); - // Assert only required parameters exist (path is the only required parameter per spec) - parameters.Should().HaveCount(1, "GetProjectGraph should only have the 'path' parameter"); - parameters[0].Name.Should().Be("path"); + // Assert parameters exist + parameters.Should().HaveCount(2, "GetProjectGraph should have 'path' and 'show_title' parameters"); + + var pathParam = parameters.Should().ContainSingle(p => p.Name == "path").Which; + pathParam.ParameterType.Should().Be(); + pathParam.IsOptional.Should().BeFalse(); + + var titleParam = parameters.Should().ContainSingle(p => p.Name == "showTitle").Which; + titleParam.ParameterType.Should().Be(); + titleParam.IsOptional.Should().BeTrue(); + titleParam.DefaultValue.Should().Be(true); } } \ No newline at end of file diff --git a/tests/ProjGraph.Tests.Contract/McpErdContractTests.cs b/tests/ProjGraph.Tests.Contract/McpErdContractTests.cs index c6537e9..2ccfd05 100644 --- a/tests/ProjGraph.Tests.Contract/McpErdContractTests.cs +++ b/tests/ProjGraph.Tests.Contract/McpErdContractTests.cs @@ -86,16 +86,28 @@ public void GetErd_ShouldHave_OptionalContextNameParameter() } [Fact] - public void GetErd_ShouldHave_ExactlyTwoParameters() + public void GetErd_ShouldHave_Parameters() { // Arrange var type = typeof(ProjGraphTools); var method = type.GetMethod("GetErd"); var parameters = method!.GetParameters(); - // Assert exactly two parameters exist (path required, contextName optional) - parameters.Should().HaveCount(2, "GetErd should have exactly 2 parameters: path and contextName"); - parameters[0].Name.Should().Be("path"); - parameters[1].Name.Should().Be("contextName"); + // Assert parameters exist + parameters.Should().HaveCount(3, "GetErd should have 3 parameters: path, contextName, and show_title"); + + var pathParam = parameters.Should().ContainSingle(p => p.Name == "path").Which; + pathParam.ParameterType.Should().Be(); + pathParam.IsOptional.Should().BeFalse(); + + var contextParam = parameters.Should().ContainSingle(p => p.Name == "contextName").Which; + contextParam.ParameterType.Should().Be(); + contextParam.IsOptional.Should().BeTrue(); + contextParam.DefaultValue.Should().BeNull(); + + var titleParam = parameters.Should().ContainSingle(p => p.Name == "showTitle").Which; + titleParam.ParameterType.Should().Be(); + titleParam.IsOptional.Should().BeTrue(); + titleParam.DefaultValue.Should().Be(true); } } \ No newline at end of file diff --git a/tests/ProjGraph.Tests.Unit.ClassDiagram/MermaidClassDiagramRendererTests.cs b/tests/ProjGraph.Tests.Unit.ClassDiagram/MermaidClassDiagramRendererTests.cs index 672fc52..7ec2342 100644 --- a/tests/ProjGraph.Tests.Unit.ClassDiagram/MermaidClassDiagramRendererTests.cs +++ b/tests/ProjGraph.Tests.Unit.ClassDiagram/MermaidClassDiagramRendererTests.cs @@ -1,5 +1,6 @@ using ProjGraph.Core.Models; using ProjGraph.Lib.ClassDiagram.Rendering; +using ProjGraph.Lib.Core.Abstractions; namespace ProjGraph.Tests.Unit.ClassDiagram; @@ -46,6 +47,26 @@ public void Render_WithClassAndMembers_ReturnsCorrectMermaid() result.Should().Contain("+Dictionary~string, string~ Metadata"); } + [Fact] + public void Render_WithShowTitle_ShouldShowTitle() + { + var model = new ClassModel("MyTitle", [], []); + var result = _renderer.Render(model); + + result.Should().Contain("---"); + result.Should().Contain("title: MyTitle"); + } + + [Fact] + public void Render_WithShowTitleFalse_ShouldNotShowTitle() + { + var model = new ClassModel("MyTitle", [], []); + var result = _renderer.Render(model, new DiagramOptions(false)); + + result.Should().NotContain("---"); + result.Should().NotContain("title: MyTitle"); + } + [Fact] public void Render_WithInheritance_ReturnsCorrectMermaid() { diff --git a/tests/ProjGraph.Tests.Unit.EntityFramework/MermaidErdRendererTests.cs b/tests/ProjGraph.Tests.Unit.EntityFramework/MermaidErdRendererTests.cs index 2711955..c04e92d 100644 --- a/tests/ProjGraph.Tests.Unit.EntityFramework/MermaidErdRendererTests.cs +++ b/tests/ProjGraph.Tests.Unit.EntityFramework/MermaidErdRendererTests.cs @@ -1,4 +1,5 @@ using ProjGraph.Core.Models; +using ProjGraph.Lib.Core.Abstractions; using ProjGraph.Lib.EntityFramework.Rendering; namespace ProjGraph.Tests.Unit.EntityFramework; @@ -51,6 +52,34 @@ public void Render_ShouldHandleEmptyModel() result.Should().Contain("```"); } + [Fact] + public void Render_ShouldShowTitle() + { + // Arrange + var model = new EfModel { ContextName = "MyContext", Entities = [] }; + + // Act + var result = _renderer.Render(model); + + // Assert + result.Should().Contain("---"); + result.Should().Contain("title: MyContext"); + } + + [Fact] + public void Render_ShouldNotShowTitle_WhenShowTitleIsFalse() + { + // Arrange + var model = new EfModel { ContextName = "MyContext", Entities = [] }; + + // Act + var result = _renderer.Render(model, new DiagramOptions(false)); + + // Assert + result.Should().NotContain("---"); + result.Should().NotContain("title: MyContext"); + } + [Fact] public void Render_ShouldMarkPrimaryKeys() { diff --git a/tests/ProjGraph.Tests.Unit.ProjectGraph/MermaidGraphRendererTests.cs b/tests/ProjGraph.Tests.Unit.ProjectGraph/MermaidGraphRendererTests.cs index 77d2b24..bdbc523 100644 --- a/tests/ProjGraph.Tests.Unit.ProjectGraph/MermaidGraphRendererTests.cs +++ b/tests/ProjGraph.Tests.Unit.ProjectGraph/MermaidGraphRendererTests.cs @@ -1,4 +1,5 @@ using ProjGraph.Core.Models; +using ProjGraph.Lib.Core.Abstractions; using ProjGraph.Lib.ProjectGraph.Rendering; namespace ProjGraph.Tests.Unit.ProjectGraph; @@ -99,7 +100,7 @@ public void Render_ShouldNotLabelLibraryProjects() } [Fact] - public void Render_ShouldIncludeTitleFromSolutionName() + public void Render_ShouldShowTitleFromSolutionName() { // Arrange var graph = new SolutionGraph("MySolution", "MySolution.sln", [], []); @@ -112,6 +113,20 @@ public void Render_ShouldIncludeTitleFromSolutionName() result.Should().Contain("title: MySolution"); } + [Fact] + public void Render_ShouldNotShowTitle_WhenShowTitleIsFalse() + { + // Arrange + var graph = new SolutionGraph("MySolution", "MySolution.sln", [], []); + + // Act + var result = _renderer.Render(graph, new DiagramOptions(false)); + + // Assert + result.Should().NotContain("---"); + result.Should().NotContain("title: MySolution"); + } + [Fact] public void Render_ShouldHandleEmptyGraph() { @@ -305,7 +320,7 @@ public void Render_ShouldOrderProjectsAndDependenciesByName() var lines = result.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); // Projects should be ordered A, B, C - var projectLines = lines.Where(l => l.Contains("[")).ToList(); + var projectLines = lines.Where(l => l.Contains('[')).ToList(); projectLines[0].Should().Contain("ProjectA"); projectLines[1].Should().Contain("ProjectB"); projectLines[2].Should().Contain("ProjectC");