From 56a4214cbd46b62abe1b7b9fc57b1b7a6b95d3db Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 26 Mar 2026 14:31:50 +0800 Subject: [PATCH] Add 'aspire dashboard' CLI command --- src/Aspire.Cli/Commands/DashboardCommand.cs | 120 ++++++++++++++++ src/Aspire.Cli/Commands/RootCommand.cs | 2 + src/Aspire.Cli/Program.cs | 1 + .../DashboardCommandStrings.Designer.cs | 79 ++++++++++ .../Resources/DashboardCommandStrings.resx | 135 ++++++++++++++++++ .../xlf/DashboardCommandStrings.cs.xlf | 32 +++++ .../xlf/DashboardCommandStrings.de.xlf | 32 +++++ .../xlf/DashboardCommandStrings.es.xlf | 32 +++++ .../xlf/DashboardCommandStrings.fr.xlf | 32 +++++ .../xlf/DashboardCommandStrings.it.xlf | 32 +++++ .../xlf/DashboardCommandStrings.ja.xlf | 32 +++++ .../xlf/DashboardCommandStrings.ko.xlf | 32 +++++ .../xlf/DashboardCommandStrings.pl.xlf | 32 +++++ .../xlf/DashboardCommandStrings.pt-BR.xlf | 32 +++++ .../xlf/DashboardCommandStrings.ru.xlf | 32 +++++ .../xlf/DashboardCommandStrings.tr.xlf | 32 +++++ .../xlf/DashboardCommandStrings.zh-Hans.xlf | 32 +++++ .../xlf/DashboardCommandStrings.zh-Hant.xlf | 32 +++++ .../Commands/DashboardCommandTests.cs | 52 +++++++ tests/Aspire.Cli.Tests/Utils/CliTestHelper.cs | 1 + 20 files changed, 806 insertions(+) create mode 100644 src/Aspire.Cli/Commands/DashboardCommand.cs create mode 100644 src/Aspire.Cli/Resources/DashboardCommandStrings.Designer.cs create mode 100644 src/Aspire.Cli/Resources/DashboardCommandStrings.resx create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.cs.xlf create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.de.xlf create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.es.xlf create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.fr.xlf create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.it.xlf create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.ja.xlf create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.ko.xlf create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.pl.xlf create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.pt-BR.xlf create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.ru.xlf create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.tr.xlf create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.zh-Hans.xlf create mode 100644 src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.zh-Hant.xlf create mode 100644 tests/Aspire.Cli.Tests/Commands/DashboardCommandTests.cs diff --git a/src/Aspire.Cli/Commands/DashboardCommand.cs b/src/Aspire.Cli/Commands/DashboardCommand.cs new file mode 100644 index 00000000000..93494971696 --- /dev/null +++ b/src/Aspire.Cli/Commands/DashboardCommand.cs @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.CommandLine; +using System.Globalization; +using Aspire.Cli.Configuration; +using Aspire.Cli.Interaction; +using Aspire.Cli.Layout; +using Aspire.Cli.Processes; +using Aspire.Cli.Resources; +using Aspire.Cli.Telemetry; +using Aspire.Cli.Utils; +using Microsoft.Extensions.Logging; + +namespace Aspire.Cli.Commands; + +/// +/// Command that starts a standalone Aspire Dashboard instance. +/// +internal sealed class DashboardCommand : BaseCommand +{ + internal override HelpGroup HelpGroup => HelpGroup.Monitoring; + + private readonly IInteractionService _interactionService; + private readonly ILayoutDiscovery _layoutDiscovery; + private readonly ILogger _logger; + + private static readonly Option s_detachOption = new("--detach") + { + Description = DashboardCommandStrings.DetachOptionDescription + }; + + public DashboardCommand( + IInteractionService interactionService, + ILayoutDiscovery layoutDiscovery, + IFeatures features, + ICliUpdateNotifier updateNotifier, + CliExecutionContext executionContext, + ILogger logger, + AspireCliTelemetry telemetry) + : base("dashboard", DashboardCommandStrings.Description, features, updateNotifier, executionContext, interactionService, telemetry) + { + _interactionService = interactionService; + _layoutDiscovery = layoutDiscovery; + _logger = logger; + + Options.Add(s_detachOption); + TreatUnmatchedTokensAsErrors = false; + } + + protected override async Task ExecuteAsync(ParseResult parseResult, CancellationToken cancellationToken) + { + var layout = _layoutDiscovery.DiscoverLayout(); + if (layout is null) + { + _interactionService.DisplayError(DashboardCommandStrings.BundleNotAvailable); + return ExitCodeConstants.DashboardFailure; + } + + var managedPath = layout.GetManagedPath(); + if (managedPath is null || !File.Exists(managedPath)) + { + _interactionService.DisplayError(DashboardCommandStrings.BundleNotAvailable); + return ExitCodeConstants.DashboardFailure; + } + + var dashboardArgs = new List { "dashboard" }; + dashboardArgs.AddRange(parseResult.UnmatchedTokens); + + var detach = parseResult.GetValue(s_detachOption); + + if (detach) + { + return ExecuteDetached(managedPath, dashboardArgs); + } + + return await ExecuteForegroundAsync(managedPath, dashboardArgs, cancellationToken).ConfigureAwait(false); + } + + private int ExecuteDetached(string managedPath, List dashboardArgs) + { + _logger.LogDebug("Starting dashboard in detached mode: {ManagedPath}", managedPath); + + var process = DetachedProcessLauncher.Start(managedPath, dashboardArgs, Directory.GetCurrentDirectory()); + + _interactionService.DisplayMessage(KnownEmojis.Rocket, + string.Format(CultureInfo.CurrentCulture, DashboardCommandStrings.DashboardStarted, process.Id)); + + return ExitCodeConstants.Success; + } + + private async Task ExecuteForegroundAsync(string managedPath, List dashboardArgs, CancellationToken cancellationToken) + { + _logger.LogDebug("Starting dashboard in foreground: {ManagedPath}", managedPath); + + using var process = LayoutProcessRunner.Start(managedPath, dashboardArgs, redirectOutput: false); + + try + { + await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + if (!process.HasExited) + { + process.Kill(entireProcessTree: true); + } + + return ExitCodeConstants.Success; + } + + if (process.ExitCode != 0) + { + _interactionService.DisplayError( + string.Format(CultureInfo.CurrentCulture, DashboardCommandStrings.DashboardExitedWithError, process.ExitCode)); + } + + return process.ExitCode == 0 ? ExitCodeConstants.Success : ExitCodeConstants.DashboardFailure; + } +} diff --git a/src/Aspire.Cli/Commands/RootCommand.cs b/src/Aspire.Cli/Commands/RootCommand.cs index 38b24cf00df..7a0c06743f8 100644 --- a/src/Aspire.Cli/Commands/RootCommand.cs +++ b/src/Aspire.Cli/Commands/RootCommand.cs @@ -131,6 +131,7 @@ public RootCommand( AgentCommand agentCommand, TelemetryCommand telemetryCommand, ExportCommand exportCommand, + DashboardCommand dashboardCommand, DocsCommand docsCommand, SecretCommand secretCommand, SdkCommand sdkCommand, @@ -222,6 +223,7 @@ public RootCommand( Subcommands.Add(telemetryCommand); Subcommands.Add(exportCommand); Subcommands.Add(docsCommand); + Subcommands.Add(dashboardCommand); Subcommands.Add(secretCommand); #if DEBUG diff --git a/src/Aspire.Cli/Program.cs b/src/Aspire.Cli/Program.cs index 55881912e73..e16c047691e 100644 --- a/src/Aspire.Cli/Program.cs +++ b/src/Aspire.Cli/Program.cs @@ -454,6 +454,7 @@ internal static async Task BuildApplicationAsync(string[] args, CliStartu builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); diff --git a/src/Aspire.Cli/Resources/DashboardCommandStrings.Designer.cs b/src/Aspire.Cli/Resources/DashboardCommandStrings.Designer.cs new file mode 100644 index 00000000000..a3003f980fa --- /dev/null +++ b/src/Aspire.Cli/Resources/DashboardCommandStrings.Designer.cs @@ -0,0 +1,79 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Aspire.Cli.Resources { + using System; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class DashboardCommandStrings { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DashboardCommandStrings() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Aspire.Cli.Resources.DashboardCommandStrings", typeof(DashboardCommandStrings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + public static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + public static string DetachOptionDescription { + get { + return ResourceManager.GetString("DetachOptionDescription", resourceCulture); + } + } + + public static string BundleNotAvailable { + get { + return ResourceManager.GetString("BundleNotAvailable", resourceCulture); + } + } + + public static string DashboardStarted { + get { + return ResourceManager.GetString("DashboardStarted", resourceCulture); + } + } + + public static string DashboardExitedWithError { + get { + return ResourceManager.GetString("DashboardExitedWithError", resourceCulture); + } + } + + } +} diff --git a/src/Aspire.Cli/Resources/DashboardCommandStrings.resx b/src/Aspire.Cli/Resources/DashboardCommandStrings.resx new file mode 100644 index 00000000000..8d8d4a4cbb4 --- /dev/null +++ b/src/Aspire.Cli/Resources/DashboardCommandStrings.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Start the Aspire dashboard + + + Run the dashboard in the background + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + Dashboard started (PID {0}). + + + Dashboard exited with exit code {0}. + + diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.cs.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.cs.xlf new file mode 100644 index 00000000000..60d19618023 --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.cs.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.de.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.de.xlf new file mode 100644 index 00000000000..59d268e4782 --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.de.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.es.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.es.xlf new file mode 100644 index 00000000000..486feaf2354 --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.es.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.fr.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.fr.xlf new file mode 100644 index 00000000000..4cd7f16a4b6 --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.fr.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.it.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.it.xlf new file mode 100644 index 00000000000..feae1bd9393 --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.it.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.ja.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.ja.xlf new file mode 100644 index 00000000000..be2d45829bb --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.ja.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.ko.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.ko.xlf new file mode 100644 index 00000000000..bb58ec56ae9 --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.ko.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.pl.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.pl.xlf new file mode 100644 index 00000000000..fbeaac64a66 --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.pl.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.pt-BR.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.pt-BR.xlf new file mode 100644 index 00000000000..9c3b7353465 --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.pt-BR.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.ru.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.ru.xlf new file mode 100644 index 00000000000..df0ef1b1bca --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.ru.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.tr.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.tr.xlf new file mode 100644 index 00000000000..dcab25db754 --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.tr.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.zh-Hans.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.zh-Hans.xlf new file mode 100644 index 00000000000..c67b5c84195 --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.zh-Hans.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.zh-Hant.xlf b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.zh-Hant.xlf new file mode 100644 index 00000000000..ad377eb8214 --- /dev/null +++ b/src/Aspire.Cli/Resources/xlf/DashboardCommandStrings.zh-Hant.xlf @@ -0,0 +1,32 @@ + + + + + + The Aspire dashboard requires the CLI bundle. The bundle was not found. + The Aspire dashboard requires the CLI bundle. The bundle was not found. + + + + Dashboard exited with exit code {0}. + Dashboard exited with exit code {0}. + + + + Dashboard started (PID {0}). + Dashboard started (PID {0}). + + + + Start the Aspire dashboard + Start the Aspire dashboard + + + + Run the dashboard in the background + Run the dashboard in the background + + + + + \ No newline at end of file diff --git a/tests/Aspire.Cli.Tests/Commands/DashboardCommandTests.cs b/tests/Aspire.Cli.Tests/Commands/DashboardCommandTests.cs new file mode 100644 index 00000000000..fa817945071 --- /dev/null +++ b/tests/Aspire.Cli.Tests/Commands/DashboardCommandTests.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Aspire.Cli.Commands; +using Aspire.Cli.Resources; +using Aspire.Cli.Tests.TestServices; +using Aspire.Cli.Tests.Utils; +using Microsoft.AspNetCore.InternalTesting; +using Microsoft.Extensions.DependencyInjection; + +namespace Aspire.Cli.Tests.Commands; + +public class DashboardCommandTests(ITestOutputHelper outputHelper) +{ + [Fact] + public async Task DashboardCommand_BundleNotAvailable_DisplaysError() + { + using var workspace = TemporaryWorkspace.Create(outputHelper); + + var testInteractionService = new TestInteractionService(); + + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, options => + { + options.InteractionServiceFactory = _ => testInteractionService; + }); + + var provider = services.BuildServiceProvider(); + var command = provider.GetRequiredService(); + var result = command.Parse("dashboard"); + + var exitCode = await result.InvokeAsync().DefaultTimeout(); + + Assert.Equal(ExitCodeConstants.DashboardFailure, exitCode); + var errorMessage = Assert.Single(testInteractionService.DisplayedErrors); + Assert.Equal(DashboardCommandStrings.BundleNotAvailable, errorMessage); + } + + [Fact] + public async Task DashboardCommand_Help_ReturnsSuccess() + { + using var workspace = TemporaryWorkspace.Create(outputHelper); + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper); + var provider = services.BuildServiceProvider(); + + var command = provider.GetRequiredService(); + var result = command.Parse("dashboard --help"); + + var exitCode = await result.InvokeAsync().DefaultTimeout(); + + Assert.Equal(ExitCodeConstants.Success, exitCode); + } +} diff --git a/tests/Aspire.Cli.Tests/Utils/CliTestHelper.cs b/tests/Aspire.Cli.Tests/Utils/CliTestHelper.cs index e89b6fdca5c..028e1feeff7 100644 --- a/tests/Aspire.Cli.Tests/Utils/CliTestHelper.cs +++ b/tests/Aspire.Cli.Tests/Utils/CliTestHelper.cs @@ -190,6 +190,7 @@ public static IServiceCollection CreateServiceCollection(TemporaryWorkspace work services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient();