| File: Controls\AspireMenuButtonTests.cs | Web Access |
| Project: src\tests\Aspire.Dashboard.Components.Tests\Aspire.Dashboard.Components.Tests.csproj (Aspire.Dashboard.Components.Tests) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Aspire.Dashboard.Components.Tests.Shared; using Aspire.Dashboard.Model; using Bunit; using Microsoft.Extensions.DependencyInjection; using Microsoft.FluentUI.AspNetCore.Components; using Xunit; namespace Aspire.Dashboard.Components.Tests.Controls; public class AspireMenuButtonTests : DashboardTestContext { [Fact] public void Render_AddsMenuPopupAriaWithoutExpandedState() { FluentUISetupHelpers.SetupFluentUIComponents(this); FluentUISetupHelpers.SetupFluentAnchoredRegion(this); FluentUISetupHelpers.SetupFluentButton(this); FluentUISetupHelpers.SetupFluentMenu(this); RenderComponent<FluentMenuProvider>(); var cut = RenderComponent<AspireMenuButton>(builder => { builder.Add(p => p.MenuButtonId, "view-options-button"); builder.Add(p => p.Text, "View options"); builder.Add(p => p.ItemsProvider, () => [new MenuButtonItem { Text = "Show hidden resources" }]); }); var button = cut.Find("#view-options-button"); Assert.Equal("menu", button.GetAttribute("aria-haspopup")); Assert.False(button.HasAttribute("aria-expanded")); } [Fact] public async Task ItemsProvider_AddsMenuWhenButtonIsClicked() { FluentUISetupHelpers.SetupFluentUIComponents(this, setupAspireMenuButtonModule: false); FluentUISetupHelpers.SetupFluentAnchoredRegion(this); FluentUISetupHelpers.SetupFluentButton(this); FluentUISetupHelpers.SetupFluentMenu(this); var menuButtonModule = JSInterop.SetupModule("./Components/Controls/AspireMenuButton.razor.js"); menuButtonModule.SetupVoid("prepareForFluentMenuInitialization", _ => true).SetVoidResult(); menuButtonModule.SetupVoid("waitForFluentMenuInitialization", _ => true).SetVoidResult(); menuButtonModule.SetupVoid("cancelFluentMenuInitialization", _ => true).SetVoidResult(); var providerInvocationCount = 0; var provider = RenderComponent<FluentMenuProvider>(); var cut = RenderComponent<AspireMenuButton>(builder => { builder.Add(p => p.MenuButtonId, "lazy-menu-button"); builder.Add(p => p.Text, "View options"); builder.Add(p => p.ItemsProvider, () => { providerInvocationCount++; return [new MenuButtonItem { Text = $"Item {providerInvocationCount}" }]; }); }); Assert.Equal(0, providerInvocationCount); Assert.Empty(cut.FindComponents<AspireMenu>()); Assert.Equal(0, JSInterop.Invocations.Count(invocation => invocation.Identifier == "import" && invocation.Arguments.Contains("./Components/Controls/AspireMenuButton.razor.js"))); cut.Find("#lazy-menu-button").Click(); Assert.Equal(1, providerInvocationCount); Assert.Single(JSInterop.Invocations, invocation => invocation.Identifier == "import" && invocation.Arguments.Contains("./Components/Controls/AspireMenuButton.razor.js")); var prepareInvocation = Assert.Single(menuButtonModule.Invocations, invocation => invocation.Identifier == "prepareForFluentMenuInitialization"); var waitInvocation = Assert.Single(menuButtonModule.Invocations, invocation => invocation.Identifier == "waitForFluentMenuInitialization"); Assert.Equal(100, waitInvocation.Arguments[1]); var invocations = menuButtonModule.Invocations.ToList(); Assert.True(invocations.IndexOf(prepareInvocation) < invocations.IndexOf(waitInvocation)); Assert.Single(cut.FindComponents<AspireMenu>()); Assert.Single(cut.FindComponents<FluentMenu>()); cut.WaitForAssertion(() => Assert.True(cut.FindComponent<AspireMenu>().Instance.Open)); var menuService = Services.GetRequiredService<IMenuService>(); var registeredMenu = menuService.Menus.Single(); Assert.True(registeredMenu.OpenChanged.HasDelegate); provider.WaitForAssertion(() => { Assert.True(registeredMenu.Open); Assert.Single(provider.FindComponents<FluentMenu>()); }); Assert.True(provider.FindComponent<FluentMenu>().Instance.Open); Assert.Equal("Item 1", provider.FindComponent<FluentMenuItem>().Instance.Label); var menu = cut.FindComponent<AspireMenu>().Instance; cut.Find("#lazy-menu-button").Click(); Assert.Equal(1, providerInvocationCount); Assert.Same(menu, cut.FindComponent<AspireMenu>().Instance); Assert.False(Services.GetRequiredService<IMenuService>().Menus.Single().Open); cut.Find("#lazy-menu-button").Click(); Assert.Equal(2, providerInvocationCount); Assert.Same(menu, cut.FindComponent<AspireMenu>().Instance); Assert.True(registeredMenu.Open); provider.WaitForAssertion(() => Assert.Equal("Item 2", provider.FindComponent<FluentMenuItem>().Instance.Label)); await cut.InvokeAsync(provider.FindComponent<FluentMenu>().Instance.CloseAsync); Assert.False(cut.FindComponent<AspireMenu>().Instance.Open); Assert.False(registeredMenu.Open); } [Fact] public void ItemsProvider_RefreshesOpenMenuWhenParametersChange() { FluentUISetupHelpers.SetupFluentUIComponents(this); FluentUISetupHelpers.SetupFluentAnchoredRegion(this); FluentUISetupHelpers.SetupFluentButton(this); FluentUISetupHelpers.SetupFluentMenu(this); var itemText = "First item"; Func<IList<MenuButtonItem>> itemsProvider = () => [new MenuButtonItem { Text = itemText }]; var provider = RenderComponent<FluentMenuProvider>(); var cut = RenderComponent<AspireMenuButton>(builder => { builder.Add(p => p.MenuButtonId, "refresh-menu-button"); builder.Add(p => p.Text, "View options"); builder.Add(p => p.ItemsProvider, itemsProvider); }); cut.Find("#refresh-menu-button").Click(); provider.WaitForAssertion(() => Assert.Equal("First item", provider.FindComponent<FluentMenuItem>().Instance.Label)); itemText = "Second item"; cut.SetParametersAndRender(builder => builder.Add(p => p.Text, "Updated view options")); provider.WaitForAssertion(() => Assert.Equal("Second item", provider.FindComponent<FluentMenuItem>().Instance.Label)); } [Fact] public async Task ItemsProvider_ParameterChangeDuringPendingInitialization_UsesLatestItems() { FluentUISetupHelpers.SetupFluentUIComponents(this, setupAspireMenuButtonModule: false); FluentUISetupHelpers.SetupFluentAnchoredRegion(this); FluentUISetupHelpers.SetupFluentButton(this); FluentUISetupHelpers.SetupFluentMenu(this); var menuButtonModule = JSInterop.SetupModule("./Components/Controls/AspireMenuButton.razor.js"); var preparation = menuButtonModule.SetupVoid("prepareForFluentMenuInitialization", "pending-items-menu-button"); menuButtonModule.SetupVoid("waitForFluentMenuInitialization", _ => true).SetVoidResult(); var provider = RenderComponent<FluentMenuProvider>(); var cut = RenderComponent<AspireMenuButton>(builder => { builder.Add(p => p.MenuButtonId, "pending-items-menu-button"); builder.Add(p => p.ItemsProvider, () => [new MenuButtonItem { Text = "First item" }]); }); var clickTask = cut.Find("#pending-items-menu-button").ClickAsync(new()); cut.WaitForAssertion(() => Assert.Single(preparation.Invocations)); cut.SetParametersAndRender(builder => builder.Add(p => p.ItemsProvider, () => [new MenuButtonItem { Text = "Second item" }])); preparation.SetVoidResult(); await clickTask; provider.WaitForAssertion(() => Assert.Equal("Second item", provider.FindComponent<FluentMenuItem>().Instance.Label)); } [Fact] public async Task DisposeAsync_CancelsPendingMenuInitialization() { FluentUISetupHelpers.SetupFluentUIComponents(this, setupAspireMenuButtonModule: false); FluentUISetupHelpers.SetupFluentAnchoredRegion(this); FluentUISetupHelpers.SetupFluentButton(this); FluentUISetupHelpers.SetupFluentMenu(this); var menuButtonModule = JSInterop.SetupModule("./Components/Controls/AspireMenuButton.razor.js"); menuButtonModule.SetupVoid("prepareForFluentMenuInitialization", "pending-menu-button").SetVoidResult(); var initialization = menuButtonModule.SetupVoid("waitForFluentMenuInitialization", "pending-menu-button", 100); menuButtonModule.SetupVoid("cancelFluentMenuInitialization", "pending-menu-button").SetVoidResult(); var cut = RenderComponent<AspireMenuButton>(builder => { builder.Add(p => p.MenuButtonId, "pending-menu-button"); builder.Add(p => p.ItemsProvider, () => [new MenuButtonItem { Text = "Item" }]); }); cut.Find("#pending-menu-button").Click(); await cut.Instance.DisposeAsync(); var cancellationInvocation = Assert.Single(menuButtonModule.Invocations, invocation => invocation.Identifier == "cancelFluentMenuInitialization"); Assert.Equal("pending-menu-button", cancellationInvocation.Arguments[0]); initialization.SetVoidResult(); } }