From f7b6858cd1d2453d3f96d428fd7403d327e9e16d Mon Sep 17 00:00:00 2001 From: Yuriy Durov Date: Thu, 4 Jan 2024 18:44:34 +0400 Subject: [PATCH] Initial upload --- .github/workflows/Publish.yml | 37 ++++++ .github/workflows/Tests.yml | 25 ++++ BitzArt.Console.sln | 57 +++++++++ README.md | 11 +- .../BitzArt.Console.TestApp.csproj | 15 +++ .../BitzArt.Console.TestApp/Menus/MainMenu.cs | 8 ++ .../BitzArt.Console.TestApp/Menus/SubMenu1.cs | 6 + .../BitzArt.Console.TestApp/Menus/SubMenu2.cs | 6 + sample/BitzArt.Console.TestApp/Program.cs | 15 +++ src/BitzArt.Console/App/ConsoleApp.cs | 14 +++ src/BitzArt.Console/App/ConsoleAppBuilder.cs | 17 +++ src/BitzArt.Console/BitzArt.Console.csproj | 35 ++++++ .../Extensions/AddConsoleMenuExtensions.cs | 45 ++++++++ .../Extensions/RunConsoleMenuExtension.cs | 23 ++++ .../IConsoleAppNavigationManager.cs | 7 ++ .../Interfaces/IConsoleMenu.cs | 6 + src/BitzArt.Console/Menus/ConsoleMenuBase.cs | 32 +++++ .../Menus/ConsolePanelMenuBase.cs | 8 ++ .../Menus/ConsoleSelectionMenuBase.cs | 12 ++ .../Menus/ConsoleSelectionMenuItem.cs | 7 ++ .../Services/ConsoleAppNavigationManager.cs | 16 +++ .../Utility/AnsiConsoleMenu.cs | 20 ++++ .../BitzArt.Console.Tests.csproj | 34 ++++++ .../BitzArt.Console.Tests/Menus/TestMenu1.cs | 14 +++ .../BitzArt.Console.Tests/Menus/TestMenu2.cs | 14 +++ .../Tests/ServiceInjectionTests.cs | 109 ++++++++++++++++++ 26 files changed, 591 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/Publish.yml create mode 100644 .github/workflows/Tests.yml create mode 100644 BitzArt.Console.sln create mode 100644 sample/BitzArt.Console.TestApp/BitzArt.Console.TestApp.csproj create mode 100644 sample/BitzArt.Console.TestApp/Menus/MainMenu.cs create mode 100644 sample/BitzArt.Console.TestApp/Menus/SubMenu1.cs create mode 100644 sample/BitzArt.Console.TestApp/Menus/SubMenu2.cs create mode 100644 sample/BitzArt.Console.TestApp/Program.cs create mode 100644 src/BitzArt.Console/App/ConsoleApp.cs create mode 100644 src/BitzArt.Console/App/ConsoleAppBuilder.cs create mode 100644 src/BitzArt.Console/BitzArt.Console.csproj create mode 100644 src/BitzArt.Console/Extensions/AddConsoleMenuExtensions.cs create mode 100644 src/BitzArt.Console/Extensions/RunConsoleMenuExtension.cs create mode 100644 src/BitzArt.Console/Interfaces/IConsoleAppNavigationManager.cs create mode 100644 src/BitzArt.Console/Interfaces/IConsoleMenu.cs create mode 100644 src/BitzArt.Console/Menus/ConsoleMenuBase.cs create mode 100644 src/BitzArt.Console/Menus/ConsolePanelMenuBase.cs create mode 100644 src/BitzArt.Console/Menus/ConsoleSelectionMenuBase.cs create mode 100644 src/BitzArt.Console/Menus/ConsoleSelectionMenuItem.cs create mode 100644 src/BitzArt.Console/Services/ConsoleAppNavigationManager.cs create mode 100644 src/BitzArt.Console/Utility/AnsiConsoleMenu.cs create mode 100644 tests/BitzArt.Console.Tests/BitzArt.Console.Tests.csproj create mode 100644 tests/BitzArt.Console.Tests/Menus/TestMenu1.cs create mode 100644 tests/BitzArt.Console.Tests/Menus/TestMenu2.cs create mode 100644 tests/BitzArt.Console.Tests/Tests/ServiceInjectionTests.cs diff --git a/.github/workflows/Publish.yml b/.github/workflows/Publish.yml new file mode 100644 index 0000000..34aa995 --- /dev/null +++ b/.github/workflows/Publish.yml @@ -0,0 +1,37 @@ +name: Release BitzArt.Console + +on: + repository_dispatch: + push: + tags: + - "BitzArt.Console-v[0-9]+.[0-9]+.[0-9]+*" + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NUGET_APIKEY: ${{ secrets.NUGET_APIKEY}} + +jobs: + + BitzArt-Console: + name: BitzArt.Console + runs-on: ubuntu-latest + steps: + + - name: Checkout + uses: actions/checkout@v2 + + - name: Verify commit + run: | + git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/* + git branch --remote --contains | grep origin/main + + - name: Set version + run: echo "VERSION=${GITHUB_REF/refs\/tags\/BitzArt.Console-v/}" >> $GITHUB_ENV + + - name: Build + run: | + dotnet build src/BitzArt.Console/BitzArt.Console.csproj --configuration Release /p:Version=${VERSION} + dotnet pack src/BitzArt.Console/BitzArt.Console.csproj --configuration Release /p:Version=${VERSION} --no-build --output . + + - name: Push + run: dotnet nuget push BitzArt.Console.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_APIKEY} \ No newline at end of file diff --git a/.github/workflows/Tests.yml b/.github/workflows/Tests.yml new file mode 100644 index 0000000..292d28c --- /dev/null +++ b/.github/workflows/Tests.yml @@ -0,0 +1,25 @@ +name: Tests + +on: + push: + branches: + - main + tags-ignore: + - '*' + +jobs: + tests: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Install dependencies + run: dotnet restore + + - name: Build + run: dotnet build --configuration Release --no-restore + + - name: Test + run: dotnet test --no-restore --verbosity normal \ No newline at end of file diff --git a/BitzArt.Console.sln b/BitzArt.Console.sln new file mode 100644 index 0000000..a9ac604 --- /dev/null +++ b/BitzArt.Console.sln @@ -0,0 +1,57 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34330.188 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E8566731-C3CC-4CB9-85E0-72357C6BFDCA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{FF001A93-A89D-483A-B0F7-1D85F419CC86}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{9410E858-3224-4A08-9D28-658D5ADA7147}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BitzArt.Console.TestApp", "sample\BitzArt.Console.TestApp\BitzArt.Console.TestApp.csproj", "{B936B06A-384C-43CD-98AA-95233A4042B8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BitzArt.Console", "src\BitzArt.Console\BitzArt.Console.csproj", "{50E9BDF9-C9D5-455F-BEFD-546F40BA4269}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BitzArt.Console.Tests", "tests\BitzArt.Console.Tests\BitzArt.Console.Tests.csproj", "{7AC4B407-8B8F-4547-A0A9-A45A80B9C4A8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{E4EB97B3-DECB-4C34-B7F4-55EDD0A680AA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{59D0F46E-803A-4616-A5AF-CA2FAFB2E508}" + ProjectSection(SolutionItems) = preProject + .github\workflows\Publish.yml = .github\workflows\Publish.yml + .github\workflows\Tests.yml = .github\workflows\Tests.yml + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B936B06A-384C-43CD-98AA-95233A4042B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B936B06A-384C-43CD-98AA-95233A4042B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B936B06A-384C-43CD-98AA-95233A4042B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B936B06A-384C-43CD-98AA-95233A4042B8}.Release|Any CPU.Build.0 = Release|Any CPU + {50E9BDF9-C9D5-455F-BEFD-546F40BA4269}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50E9BDF9-C9D5-455F-BEFD-546F40BA4269}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50E9BDF9-C9D5-455F-BEFD-546F40BA4269}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50E9BDF9-C9D5-455F-BEFD-546F40BA4269}.Release|Any CPU.Build.0 = Release|Any CPU + {7AC4B407-8B8F-4547-A0A9-A45A80B9C4A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7AC4B407-8B8F-4547-A0A9-A45A80B9C4A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AC4B407-8B8F-4547-A0A9-A45A80B9C4A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7AC4B407-8B8F-4547-A0A9-A45A80B9C4A8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B936B06A-384C-43CD-98AA-95233A4042B8} = {FF001A93-A89D-483A-B0F7-1D85F419CC86} + {50E9BDF9-C9D5-455F-BEFD-546F40BA4269} = {E8566731-C3CC-4CB9-85E0-72357C6BFDCA} + {7AC4B407-8B8F-4547-A0A9-A45A80B9C4A8} = {9410E858-3224-4A08-9D28-658D5ADA7147} + {59D0F46E-803A-4616-A5AF-CA2FAFB2E508} = {E4EB97B3-DECB-4C34-B7F4-55EDD0A680AA} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7B835038-140D-4D0B-8B20-E8931CFA0DE0} + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md index 5bc82d5..b25d60b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ -# ConsoleApp -A framework for building console apps +@@ -1,10 +0,0 @@ +# BitzArt.Console + +[![NuGet](https://img.shields.io/nuget/v/BitzArt.Console.svg)](https://www.nuget.org/packages/BitzArt.Console/) +[![NuGet](https://img.shields.io/nuget/dt/BitzArt.Console.svg)](https://www.nuget.org/packages/BitzArt.Console/) + +A modern .Net console application framework + +> **Note:** This project is in development and not ready for production use. \ No newline at end of file diff --git a/sample/BitzArt.Console.TestApp/BitzArt.Console.TestApp.csproj b/sample/BitzArt.Console.TestApp/BitzArt.Console.TestApp.csproj new file mode 100644 index 0000000..29a813b --- /dev/null +++ b/sample/BitzArt.Console.TestApp/BitzArt.Console.TestApp.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + enable + enable + BitzArt.Console + + + + + + + diff --git a/sample/BitzArt.Console.TestApp/Menus/MainMenu.cs b/sample/BitzArt.Console.TestApp/Menus/MainMenu.cs new file mode 100644 index 0000000..dc5391a --- /dev/null +++ b/sample/BitzArt.Console.TestApp/Menus/MainMenu.cs @@ -0,0 +1,8 @@ +namespace BitzArt.Console; + +internal class MainMenu(IConsoleAppNavigationManager navigation) : ConsoleMenuBase +{ + public override string Title => "Main Menu"; + + public IConsoleAppNavigationManager Navigation { get; } = navigation; +} diff --git a/sample/BitzArt.Console.TestApp/Menus/SubMenu1.cs b/sample/BitzArt.Console.TestApp/Menus/SubMenu1.cs new file mode 100644 index 0000000..56d5322 --- /dev/null +++ b/sample/BitzArt.Console.TestApp/Menus/SubMenu1.cs @@ -0,0 +1,6 @@ +namespace BitzArt.Console; + +internal class SubMenu1 : ConsoleMenuBase +{ + public override string Title => "Submenu 1"; +} diff --git a/sample/BitzArt.Console.TestApp/Menus/SubMenu2.cs b/sample/BitzArt.Console.TestApp/Menus/SubMenu2.cs new file mode 100644 index 0000000..b040b21 --- /dev/null +++ b/sample/BitzArt.Console.TestApp/Menus/SubMenu2.cs @@ -0,0 +1,6 @@ +namespace BitzArt.Console; + +internal class SubMenu2 : ConsoleMenuBase +{ + public override string Title => "Submenu 2"; +} diff --git a/sample/BitzArt.Console.TestApp/Program.cs b/sample/BitzArt.Console.TestApp/Program.cs new file mode 100644 index 0000000..9eda102 --- /dev/null +++ b/sample/BitzArt.Console.TestApp/Program.cs @@ -0,0 +1,15 @@ +namespace BitzArt.Console; + +internal class Program +{ + static void Main() + { + var builder = new ConsoleAppBuilder(); + + builder.Services.AddConsoleMenusFromAssemblyContaining(); + + var app = builder.Build(); + + app.Run(); + } +} diff --git a/src/BitzArt.Console/App/ConsoleApp.cs b/src/BitzArt.Console/App/ConsoleApp.cs new file mode 100644 index 0000000..b8f8bfc --- /dev/null +++ b/src/BitzArt.Console/App/ConsoleApp.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace BitzArt.Console; + +public class ConsoleApp(ConsoleAppBuilder builder) +{ + public IServiceProvider Services { get; private set; } = builder.Services.BuildServiceProvider(); + + public void Run(CancellationToken cancellationToken = default) where TConsoleMenu : IConsoleMenu + { + var consoleMenu = Services.GetRequiredService(); + consoleMenu.RunAsync().Wait(cancellationToken); + } +} \ No newline at end of file diff --git a/src/BitzArt.Console/App/ConsoleAppBuilder.cs b/src/BitzArt.Console/App/ConsoleAppBuilder.cs new file mode 100644 index 0000000..63ad27a --- /dev/null +++ b/src/BitzArt.Console/App/ConsoleAppBuilder.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace BitzArt.Console; + +public class ConsoleAppBuilder +{ + public IServiceCollection Services { get; private set; } + + public ConsoleAppBuilder() + { + Services = new ServiceCollection(); + + Services.AddSingleton(); + } + + public ConsoleApp Build() => new(this); +} diff --git a/src/BitzArt.Console/BitzArt.Console.csproj b/src/BitzArt.Console/BitzArt.Console.csproj new file mode 100644 index 0000000..fa4b14c --- /dev/null +++ b/src/BitzArt.Console/BitzArt.Console.csproj @@ -0,0 +1,35 @@ + + + + net8.0 + enable + enable + BitzArt.Console + + BitzArt.Console + BitzArt + + MIT + git + https://github.com/BitzArt/Console + https://github.com/BitzArt/Console + README.md + + + + + + + + + + + + + + + + <_Parameter1>BitzArt.Console.Tests + + + diff --git a/src/BitzArt.Console/Extensions/AddConsoleMenuExtensions.cs b/src/BitzArt.Console/Extensions/AddConsoleMenuExtensions.cs new file mode 100644 index 0000000..099759e --- /dev/null +++ b/src/BitzArt.Console/Extensions/AddConsoleMenuExtensions.cs @@ -0,0 +1,45 @@ +using Microsoft.Extensions.DependencyInjection; +using System.Reflection; + +namespace BitzArt.Console; + +public static class AddConsoleMenuExtensions +{ + public static IServiceCollection AddConsoleMenusFromAssemblyContaining(this IServiceCollection services) + => services.AddConsoleMenusFromAssemblyContaining(typeof(TAssemblyPointer)); + + public static IServiceCollection AddConsoleMenusFromAssemblyContaining(this IServiceCollection services, Type type) + => services.AddConsoleMenusFromAssembly(type.Assembly); + + public static IServiceCollection AddConsoleMenusFromAssembly(this IServiceCollection services, Assembly assembly) + { + var tools = assembly + .DefinedTypes + .Where(x => x.IsAbstract == false) + .Where(x => x.GetInterfaces().Contains(typeof(IConsoleMenu))); + + foreach (var tool in tools) services.AddConsoleMenu(tool); + + return services; + } + + public static IServiceCollection AddConsoleMenu(this IServiceCollection services) + where TConsoleMenu : class, IConsoleMenu + { + services.AddTransient(); + services.AddTransient(); + + return services; + } + + public static IServiceCollection AddConsoleMenu(this IServiceCollection services, Type type) + { + if (type is null) throw new ArgumentException($"{nameof(type)} must not be null"); + if (type.IsAssignableTo(typeof(IConsoleMenu)) == false) throw new ArgumentException($"{type.Name} is not assignable to IConsoleMenu"); + + services.AddTransient(type); + services.AddTransient(x => (IConsoleMenu)x.GetRequiredService(type)); + + return services; + } +} diff --git a/src/BitzArt.Console/Extensions/RunConsoleMenuExtension.cs b/src/BitzArt.Console/Extensions/RunConsoleMenuExtension.cs new file mode 100644 index 0000000..5925328 --- /dev/null +++ b/src/BitzArt.Console/Extensions/RunConsoleMenuExtension.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace BitzArt.Console; + +public static class RunConsoleMenuExtension +{ + public static async Task RunConsoleMenuAsync(this IServiceProvider serviceProvider) + where TConsoleMenu : IConsoleMenu + { + var tool = serviceProvider.GetRequiredService(); + + await tool.RunAsync(); + } + + public static async Task RunConsoleMenuAsync(this IServiceProvider serviceProvider, Type menuType) + { + if (!menuType.IsAssignableTo(typeof(IConsoleMenu))) throw new ArgumentException($"Menu Type must implement {nameof(IConsoleMenu)}", nameof(menuType)); + + var tool = (IConsoleMenu)serviceProvider.GetRequiredService(menuType); + + await tool.RunAsync(); + } +} diff --git a/src/BitzArt.Console/Interfaces/IConsoleAppNavigationManager.cs b/src/BitzArt.Console/Interfaces/IConsoleAppNavigationManager.cs new file mode 100644 index 0000000..a3874c7 --- /dev/null +++ b/src/BitzArt.Console/Interfaces/IConsoleAppNavigationManager.cs @@ -0,0 +1,7 @@ +namespace BitzArt.Console; + +public interface IConsoleAppNavigationManager +{ + Task NavigateAsync(Type menuType); + Task NavigateAsync() where T : IConsoleMenu; +} \ No newline at end of file diff --git a/src/BitzArt.Console/Interfaces/IConsoleMenu.cs b/src/BitzArt.Console/Interfaces/IConsoleMenu.cs new file mode 100644 index 0000000..e3a9465 --- /dev/null +++ b/src/BitzArt.Console/Interfaces/IConsoleMenu.cs @@ -0,0 +1,6 @@ +namespace BitzArt.Console; + +public interface IConsoleMenu +{ + public Task RunAsync(); +} diff --git a/src/BitzArt.Console/Menus/ConsoleMenuBase.cs b/src/BitzArt.Console/Menus/ConsoleMenuBase.cs new file mode 100644 index 0000000..b576d6c --- /dev/null +++ b/src/BitzArt.Console/Menus/ConsoleMenuBase.cs @@ -0,0 +1,32 @@ +using Spectre.Console; + +namespace BitzArt.Console; + +public abstract class ConsoleMenuBase : IConsoleMenu +{ + public virtual string Title => "Menu"; + + public async Task RunAsync() + { + Render(); + + OnRendered(); + await OnRenderedAsync(); + } + + public virtual void Render() + { + AnsiConsole.Clear(); + AnsiConsoleMenu.WriteTitle(Title); + } + + public virtual void OnRendered() + { + + } + + public virtual Task OnRenderedAsync() + { + return Task.CompletedTask; + } +} diff --git a/src/BitzArt.Console/Menus/ConsolePanelMenuBase.cs b/src/BitzArt.Console/Menus/ConsolePanelMenuBase.cs new file mode 100644 index 0000000..df4030f --- /dev/null +++ b/src/BitzArt.Console/Menus/ConsolePanelMenuBase.cs @@ -0,0 +1,8 @@ +using Spectre.Console; + +namespace BitzArt.Console; + +public abstract class ConsolePanelMenuBase(Panel panel) : ConsoleMenuBase +{ + public Panel Panel { get; set; } = panel; +} diff --git a/src/BitzArt.Console/Menus/ConsoleSelectionMenuBase.cs b/src/BitzArt.Console/Menus/ConsoleSelectionMenuBase.cs new file mode 100644 index 0000000..88670cc --- /dev/null +++ b/src/BitzArt.Console/Menus/ConsoleSelectionMenuBase.cs @@ -0,0 +1,12 @@ +using Spectre.Console; + +namespace BitzArt.Console; + +public abstract class ConsoleSelectionMenuBase(Panel panel) : ConsolePanelMenuBase(panel) +{ + protected virtual bool IsMain => false; + + public override string Title => "Selection Menu"; + + protected virtual List Items { get; set; } = []; +} diff --git a/src/BitzArt.Console/Menus/ConsoleSelectionMenuItem.cs b/src/BitzArt.Console/Menus/ConsoleSelectionMenuItem.cs new file mode 100644 index 0000000..3300afa --- /dev/null +++ b/src/BitzArt.Console/Menus/ConsoleSelectionMenuItem.cs @@ -0,0 +1,7 @@ +namespace BitzArt.Console; + +public class ConsoleSelectionMenuItem(string title, Action action) +{ + public string Title { get; set; } = title; + public Action Action { get; set; } = action; +} diff --git a/src/BitzArt.Console/Services/ConsoleAppNavigationManager.cs b/src/BitzArt.Console/Services/ConsoleAppNavigationManager.cs new file mode 100644 index 0000000..8a0a06d --- /dev/null +++ b/src/BitzArt.Console/Services/ConsoleAppNavigationManager.cs @@ -0,0 +1,16 @@ +namespace BitzArt.Console; + +internal class ConsoleAppNavigationManager(IServiceProvider serviceProvider) : IConsoleAppNavigationManager +{ + private readonly IServiceProvider _serviceProvider = serviceProvider; + + public async Task NavigateAsync() where T : IConsoleMenu + { + await _serviceProvider.RunConsoleMenuAsync(); + } + + public async Task NavigateAsync(Type menuType) + { + await _serviceProvider.RunConsoleMenuAsync(menuType); + } +} diff --git a/src/BitzArt.Console/Utility/AnsiConsoleMenu.cs b/src/BitzArt.Console/Utility/AnsiConsoleMenu.cs new file mode 100644 index 0000000..b6ea2d4 --- /dev/null +++ b/src/BitzArt.Console/Utility/AnsiConsoleMenu.cs @@ -0,0 +1,20 @@ +using Spectre.Console; + +namespace BitzArt.Console; + +public static class AnsiConsoleMenu +{ + public static void WriteTitle(string value) + => WriteLine($"\n====== {value.ToUpper()} ======\n", "yellow"); + + public static void WriteMenuItems(string value) + => WriteLine($"\n---------------------------\n{value}\n---------------------------\n", "cyan"); + + public static void WriteAction(string value) + => WriteLine(value, "cyan"); + + private static void WriteLine(string value, string color) + { + AnsiConsole.MarkupLine($"[{color}]{value}[/]"); + } +} diff --git a/tests/BitzArt.Console.Tests/BitzArt.Console.Tests.csproj b/tests/BitzArt.Console.Tests/BitzArt.Console.Tests.csproj new file mode 100644 index 0000000..066ae6c --- /dev/null +++ b/tests/BitzArt.Console.Tests/BitzArt.Console.Tests.csproj @@ -0,0 +1,34 @@ + + + + net8.0 + enable + enable + BitzArt.Console + + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + diff --git a/tests/BitzArt.Console.Tests/Menus/TestMenu1.cs b/tests/BitzArt.Console.Tests/Menus/TestMenu1.cs new file mode 100644 index 0000000..e154497 --- /dev/null +++ b/tests/BitzArt.Console.Tests/Menus/TestMenu1.cs @@ -0,0 +1,14 @@ +using BitzArt.Console; +using Spectre.Console; + +namespace BitzArt.Console; + +public class TestMenu1 : IConsoleMenu +{ + public Task RunAsync() + { + AnsiConsole.WriteLine("Test Menu 1"); + + return Task.CompletedTask; + } +} diff --git a/tests/BitzArt.Console.Tests/Menus/TestMenu2.cs b/tests/BitzArt.Console.Tests/Menus/TestMenu2.cs new file mode 100644 index 0000000..9df7897 --- /dev/null +++ b/tests/BitzArt.Console.Tests/Menus/TestMenu2.cs @@ -0,0 +1,14 @@ +using BitzArt.Console; +using Spectre.Console; + +namespace BitzArt.Console; + +public class TestMenu2 : IConsoleMenu +{ + public Task RunAsync() + { + AnsiConsole.WriteLine("Test Menu 2"); + + return Task.CompletedTask; + } +} diff --git a/tests/BitzArt.Console.Tests/Tests/ServiceInjectionTests.cs b/tests/BitzArt.Console.Tests/Tests/ServiceInjectionTests.cs new file mode 100644 index 0000000..2478049 --- /dev/null +++ b/tests/BitzArt.Console.Tests/Tests/ServiceInjectionTests.cs @@ -0,0 +1,109 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace BitzArt.Console.Tests; + +public class ServiceInjectionTests +{ + [Fact] + public void AddConsoleTool_TestTool1_Adds() + { + var services = new ServiceCollection(); + + services.AddConsoleMenu(); + + var provider = services.BuildServiceProvider(); + + var tools = provider.GetServices(); + Assert.Single(tools); + + var toolByInterface = provider.GetService(); + Assert.NotNull(toolByInterface); + Assert.True(toolByInterface is TestMenu1); + + var toolByType = provider.GetService(); + Assert.NotNull(toolByType); + Assert.True(toolByType is IConsoleMenu); + } + + [Fact] + public void AddConsoleTool_TwoGenericCalls_AddsBoth() + { + var services = new ServiceCollection(); + + services.AddConsoleMenu(); + services.AddConsoleMenu(); + + var provider = services.BuildServiceProvider(); + + var tools = provider.GetServices(); + + Assert.Equal(2, tools.Count()); + Assert.Contains(tools, x => x.GetType() == typeof(TestMenu1)); + Assert.Contains(tools, x => x.GetType() == typeof(TestMenu2)); + } + + [Fact] + public void AddConsoleTool_TwoReflectionCalls_AddsBoth() + { + var services = new ServiceCollection(); + + services.AddConsoleMenu(typeof(TestMenu1)); + services.AddConsoleMenu(typeof(TestMenu2)); + + var provider = services.BuildServiceProvider(); + + var tools = provider.GetServices(); + + Assert.Equal(2, tools.Count()); + Assert.Contains(tools, x => x.GetType() == typeof(TestMenu1)); + Assert.Contains(tools, x => x.GetType() == typeof(TestMenu2)); + } + + [Fact] + public void AddConsoleToolsFromAssembly_ThisAssembly_AddsBoth() + { + var services = new ServiceCollection(); + + services.AddConsoleMenusFromAssembly(typeof(ServiceInjectionTests).Assembly); + + var provider = services.BuildServiceProvider(); + + var tools = provider.GetServices(); + + Assert.Equal(2, tools.Count()); + Assert.Contains(tools, x => x.GetType() == typeof(TestMenu1)); + Assert.Contains(tools, x => x.GetType() == typeof(TestMenu2)); + } + + [Fact] + public void AddConsoleToolsFromAssemblyContaining_Typeof_AddsBoth() + { + var services = new ServiceCollection(); + + services.AddConsoleMenusFromAssemblyContaining(typeof(ServiceInjectionTests)); + + var provider = services.BuildServiceProvider(); + + var tools = provider.GetServices(); + + Assert.Equal(2, tools.Count()); + Assert.Contains(tools, x => x.GetType() == typeof(TestMenu1)); + Assert.Contains(tools, x => x.GetType() == typeof(TestMenu2)); + } + + [Fact] + public void AddConsoleToolsFromAssemblyContaining_Generic_AddsBoth() + { + var services = new ServiceCollection(); + + services.AddConsoleMenusFromAssemblyContaining(); + + var provider = services.BuildServiceProvider(); + + var tools = provider.GetServices(); + + Assert.Equal(2, tools.Count()); + Assert.Contains(tools, x => x.GetType() == typeof(TestMenu1)); + Assert.Contains(tools, x => x.GetType() == typeof(TestMenu2)); + } +}