diff --git a/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor b/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor index bedc48c15a3..23a1c620f49 100644 --- a/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor +++ b/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor @@ -34,7 +34,8 @@ + OnClick="@(() => ExecuteResourceCommandAsync(command))" + Class="highlighted-command"> @if (!string.IsNullOrEmpty(command.IconName) && CommandViewModel.ResolveIconName(command.IconName, command.IconVariant) is { } icon) { diff --git a/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs b/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs index 3bd71ca1b34..70a528bf877 100644 --- a/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs +++ b/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs @@ -202,7 +202,14 @@ async Task TrackResourceSnapshotsAsync() } } - await InvokeAsync(StateHasChanged); + await InvokeAsync(() => + { + // The selected resource may have changed, so update resource action buttons. + // Update inside in the render's sync context so the buttons don't change while the UI is rendering. + UpdateMenuButtons(); + + StateHasChanged(); + }); } }); } diff --git a/tests/Aspire.Dashboard.Components.Tests/Pages/ConsoleLogsTests.cs b/tests/Aspire.Dashboard.Components.Tests/Pages/ConsoleLogsTests.cs index 714b86a32c0..5f4b49b1d02 100644 --- a/tests/Aspire.Dashboard.Components.Tests/Pages/ConsoleLogsTests.cs +++ b/tests/Aspire.Dashboard.Components.Tests/Pages/ConsoleLogsTests.cs @@ -246,6 +246,70 @@ public async Task ConsoleLogsManager_ClearLogs_LogsFilteredOutAsync() cut.WaitForState(() => instance._logEntries.EntriesCount > 0); } + [Fact] + public void MenuButtons_SelectedResourceChanged_ButtonsUpdated() + { + // Arrange + var testResource = ModelTestHelpers.CreateResource( + appName: "test-resource", + state: KnownResourceState.Running, + commands: [new CommandViewModel("test-name", CommandViewModelState.Enabled, "test-displayname", "test-displaydescription", confirmationMessage: "", parameter: null, isHighlighted: true, iconName: string.Empty, iconVariant: IconVariant.Regular)]); + var subscribedResourceNameTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var consoleLogsChannel = Channel.CreateUnbounded>(); + var resourceChannel = Channel.CreateUnbounded>(); + var dashboardClient = new TestDashboardClient( + isEnabled: true, + consoleLogsChannelProvider: name => + { + subscribedResourceNameTcs.TrySetResult(name); + return consoleLogsChannel; + }, + resourceChannelProvider: () => resourceChannel, + initialResources: [testResource]); + + SetupConsoleLogsServices(dashboardClient); + + var dimensionManager = Services.GetRequiredService(); + var viewport = new ViewportInformation(IsDesktop: true, IsUltraLowHeight: false, IsUltraLowWidth: false); + dimensionManager.InvokeOnViewportInformationChanged(viewport); + + // Act 1 + var cut = RenderComponent(builder => + { + builder.Add(p => p.ResourceName, "test-resource"); + builder.Add(p => p.ViewportInformation, viewport); + }); + + var instance = cut.Instance; + var logger = Services.GetRequiredService>(); + var loc = Services.GetRequiredService>(); + + // Assert 1 + cut.WaitForState(() => instance.PageViewModel.SelectedResource == testResource); + + cut.WaitForAssertion(() => + { + var highlightedCommands = cut.FindAll(".highlighted-command"); + Assert.Single(highlightedCommands); + }); + + // Act 2 + testResource = ModelTestHelpers.CreateResource( + appName: "test-resource", + state: KnownResourceState.Running, + commands: []); + resourceChannel.Writer.TryWrite([ + new ResourceViewModelChange(ResourceViewModelChangeType.Upsert, testResource) + ]); + + // Assert 2 + cut.WaitForAssertion(() => + { + var highlightedCommands = cut.FindAll(".highlighted-command"); + Assert.Empty(highlightedCommands); + }); + } + private void SetupConsoleLogsServices(TestDashboardClient? dashboardClient = null, TestTimeProvider? timeProvider = null) { var version = typeof(FluentMain).Assembly.GetName().Version!; diff --git a/tests/Shared/DashboardModel/ModelTestHelpers.cs b/tests/Shared/DashboardModel/ModelTestHelpers.cs index 301458aa317..8b783d90a85 100644 --- a/tests/Shared/DashboardModel/ModelTestHelpers.cs +++ b/tests/Shared/DashboardModel/ModelTestHelpers.cs @@ -19,7 +19,8 @@ public static ResourceViewModel CreateResource( string? resourceType = null, string? stateStyle = null, HealthStatus? reportHealthStatus = null, - bool createNullHealthReport = false) + bool createNullHealthReport = false, + ImmutableArray? commands = null) { return new ResourceViewModel { @@ -38,7 +39,7 @@ public static ResourceViewModel CreateResource( KnownState = state, StateStyle = stateStyle, HealthReports = reportHealthStatus is null && !createNullHealthReport ? [] : [new HealthReportViewModel("healthcheck", reportHealthStatus, null, null)], - Commands = [], + Commands = commands ?? [], Relationships = [], }; }