using Bunit; using LANCommander.Server.Services; using Microsoft.Extensions.DependencyInjection; using TagsIndex = LANCommander.Server.UI.Pages.Metadata.Tags.Index; namespace LANCommander.Server.UI.Tests.Components; /// /// bUnit component tests for the metadata (Tags) management page. Replaces the flaky /// Playwright MetadataTests CRUD flows. Rendering and the add-via-modal flow run /// synchronously in-process, removing the SignalR circuit races that made the modal + /// data-table-reload Playwright tests unreliable. /// [Collection("BUnit")] public class MetadataComponentTests : BUnitTestContext { public MetadataComponentTests(BUnitServerFixture fixture) : base(fixture) { } private async Task ClearTagsAsync() { using var scope = Fixture.Factory.RealServices.CreateScope(); var tagService = scope.ServiceProvider.GetRequiredService(); foreach (var tag in await tagService.GetAsync()) await tagService.DeleteAsync(tag); } [Fact] public async Task Tags_ShowsAddButton_AndEmptyState() { await ClearTagsAsync(); var cut = RenderComponent(); Assert.Contains( cut.FindAll("button"), b => b.TextContent.Contains("Add Tag", StringComparison.OrdinalIgnoreCase)); // The empty DataTable renders AntDesign's "No Data" placeholder once the async load // completes. cut.WaitForAssertion( () => Assert.Contains("No Data", cut.Markup, StringComparison.OrdinalIgnoreCase), timeout: TimeSpan.FromSeconds(10)); } [Fact] public async Task Tags_CanAddTag() { await ClearTagsAsync(); var cut = RenderComponent(); // Open the "New Tag" modal. var addButton = cut.FindAll("button") .First(b => b.TextContent.Contains("Add Tag", StringComparison.OrdinalIgnoreCase)); addButton.Click(); // Fill in the tag name inside the modal and confirm. AntDesign's Input commits its // bound value on the change event, so dispatch both input and change. var input = cut.WaitForElement(".ant-modal input", timeout: TimeSpan.FromSeconds(5)); input.Input("Action"); input.Change("Action"); var okButton = cut.FindAll(".ant-modal button") .First(b => b.TextContent.Trim().Equals("OK", StringComparison.OrdinalIgnoreCase)); okButton.Click(); // The new tag is persisted and the data table reloads to show it. cut.WaitForAssertion( () => Assert.Contains("Action", cut.Markup), timeout: TimeSpan.FromSeconds(10)); // Verify persistence at the service layer. using var scope = Fixture.Factory.RealServices.CreateScope(); var tagService = scope.ServiceProvider.GetRequiredService(); Assert.Contains(await tagService.GetAsync(), t => t.Name == "Action"); } }