LANCommander/LANCommander.Server.UI.Tests/Components/MetadataComponentTests.cs
Pat Hartl 004a254c37 Migrate flaky Playwright admin UI tests to bUnit
Replace the SignalR-circuit-race-prone Playwright component tests for
GameEdit, Settings, metadata, profile, roles and users with in-process
bUnit component tests. Keep a thin Playwright smoke layer for the true
E2E paths (login, admin navigation/routing, first-time setup, game
import) that bUnit cannot cover.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-24 20:45:21 -05:00

79 lines
2.9 KiB
C#

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;
/// <summary>
/// bUnit component tests for the metadata (Tags) management page. Replaces the flaky
/// Playwright <c>MetadataTests</c> 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.
/// </summary>
[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<TagService>();
foreach (var tag in await tagService.GetAsync())
await tagService.DeleteAsync(tag);
}
[Fact]
public async Task Tags_ShowsAddButton_AndEmptyState()
{
await ClearTagsAsync();
var cut = RenderComponent<TagsIndex>();
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<TagsIndex>();
// 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<TagService>();
Assert.Contains(await tagService.GetAsync(), t => t.Name == "Action");
}
}