using Microsoft.AspNetCore.Components; using Bunit; using SufiChain.SufiBlazor.Components.Data; using Xunit; namespace SufiChain.SufiBlazor.Tests.Components.Data; public class SbTableTests : BunitContext { private static List CreateTestItems(int count) { return Enumerable.Range(1, count) .Select(i => new TableTestItem { Id = i, Name = $"Item {i}", Value = i * 10 }) .ToList(); } private static RenderFragment RowTemplate => item => builder => { builder.OpenElement(0, "td"); builder.AddContent(1, item.Name); builder.CloseElement(); builder.OpenElement(2, "td"); builder.AddContent(3, item.Value.ToString()); builder.CloseElement(); }; private IRenderedComponent> RenderTable( List? items = null, Action>>? configure = null) { items ??= CreateTestItems(3); return Render>(p => { p.Add(x => x.Items, items) .Add(x => x.RowTemplate, RowTemplate); configure?.Invoke(p); }); } [Fact] public void RendersTableStructure() { // Arrange & Act var cut = RenderTable(); // Assert var container = cut.Find(".sb-table-container"); Assert.NotNull(container); var table = cut.Find("table.sb-table"); Assert.NotNull(table); Assert.NotNull(cut.Find("tbody.sb-table__body")); } [Fact] public void RendersDataRows() { // Arrange var items = CreateTestItems(2); // Act var cut = RenderTable(items); // Assert Assert.Contains("Item 1", cut.Markup); Assert.Contains("Item 2", cut.Markup); Assert.Contains("10", cut.Markup); Assert.Contains("20", cut.Markup); var rows = cut.FindAll(".sb-table__row"); Assert.Equal(2, rows.Count); } [Fact] public void RendersHeaderWhenHeaderContentProvided() { // Arrange & Act var cut = RenderTable(configure: p => p .Add(x => x.HeaderContent, h => h.AddMarkupContent(0, "NameValue"))); // Assert var thead = cut.Find("thead.sb-table__head"); Assert.NotNull(thead); Assert.Contains("Name", cut.Markup); Assert.Contains("Value", cut.Markup); } [Fact] public void DoesNotRenderHeaderWhenHeaderContentNull() { // Arrange & Act var cut = RenderTable(); // Assert Assert.Empty(cut.FindAll("thead")); } [Fact] public void RendersFooterWhenFooterContentProvided() { // Arrange & Act var cut = RenderTable(configure: p => p .Add(x => x.FooterContent, f => f.AddMarkupContent(0, "Total: 3 items"))); // Assert var tfoot = cut.Find("tfoot.sb-table__foot"); Assert.NotNull(tfoot); Assert.Contains("Total: 3 items", cut.Markup); } [Fact] public void DoesNotRenderFooterWhenFooterContentNull() { // Arrange & Act var cut = RenderTable(); // Assert Assert.Empty(cut.FindAll("tfoot")); } [Fact] public void RendersEmptyContentWhenNoItems() { // Arrange & Act var cut = Render>(p => p .Add(x => x.Items, new List()) .Add(x => x.RowTemplate, RowTemplate) .Add(x => x.EmptyContent, e => e.AddMarkupContent(0, "No data"))); // Assert var emptyCell = cut.Find(".sb-table__empty"); Assert.NotNull(emptyCell); var customEmpty = cut.Find(".custom-empty"); Assert.NotNull(customEmpty); Assert.Contains("No data", customEmpty.TextContent); } [Fact] public void RendersEmptyContentWhenItemsNull() { // Arrange & Act var cut = Render>(p => p .Add(x => x.Items, (IEnumerable?)null) .Add(x => x.RowTemplate, RowTemplate) .Add(x => x.EmptyContent, e => e.AddMarkupContent(0, "Nothing here"))); // Assert Assert.Contains("Nothing here", cut.Markup); } [Fact] public void RendersEmptyBodyWhenNoItemsAndNoEmptyContent() { // Arrange & Act var cut = Render>(p => p .Add(x => x.Items, new List()) .Add(x => x.RowTemplate, RowTemplate)); // Assert - tbody exists but has no data rows var tbody = cut.Find("tbody.sb-table__body"); Assert.NotNull(tbody); Assert.Empty(cut.FindAll(".sb-table__row")); } [Fact] public void AppliesStripedClass() { // Arrange & Act var cut = RenderTable(configure: p => p.Add(x => x.Striped, true)); // Assert var table = cut.Find("table.sb-table"); Assert.Contains("sb-table--striped", table.ClassList); } [Fact] public void AppliesHoverClass() { // Arrange & Act var cut = RenderTable(configure: p => p.Add(x => x.Hover, true)); // Assert - Hover defaults to true var table = cut.Find("table.sb-table"); Assert.Contains("sb-table--hover", table.ClassList); } [Fact] public void DoesNotApplyHoverClassWhenHoverFalse() { // Arrange & Act var cut = RenderTable(configure: p => p.Add(x => x.Hover, false)); // Assert var table = cut.Find("table.sb-table"); Assert.DoesNotContain("sb-table--hover", table.ClassList); } [Fact] public void AppliesBorderedClass() { // Arrange & Act var cut = RenderTable(configure: p => p.Add(x => x.Bordered, true)); // Assert var table = cut.Find("table.sb-table"); Assert.Contains("sb-table--bordered", table.ClassList); } [Fact] public void AppliesCompactClass() { // Arrange & Act var cut = RenderTable(configure: p => p.Add(x => x.Compact, true)); // Assert var table = cut.Find("table.sb-table"); Assert.Contains("sb-table--compact", table.ClassList); } [Fact] public void AppliesCustomClassToContainer() { // Arrange & Act var cut = RenderTable(configure: p => p.Add(x => x.Class, "my-table")); // Assert var container = cut.Find(".sb-table-container"); Assert.Contains("my-table", container.ClassList); } [Fact] public async Task InvokesOnRowClickWhenRowClicked() { // Arrange TableTestItem? capturedItem = null; var items = CreateTestItems(3); var cut = Render>(p => p .Add(x => x.Items, items) .Add(x => x.RowTemplate, RowTemplate) .Add(x => x.OnRowClick, EventCallback.Factory.Create(this, item => capturedItem = item))); // Act - click second row (Item 2) var rows = cut.FindAll(".sb-table__row"); var row2 = rows.FirstOrDefault(r => r.TextContent.Contains("Item 2")); Assert.NotNull(row2); await cut.InvokeAsync(() => row2!.Click()); cut.WaitForState(() => capturedItem != null); // Assert Assert.NotNull(capturedItem); Assert.Equal(2, capturedItem!.Id); Assert.Equal("Item 2", capturedItem.Name); Assert.Equal(20, capturedItem.Value); } [Fact] public void RendersAllStyleVariantsTogether() { // Arrange & Act var cut = RenderTable(configure: p => p .Add(x => x.Striped, true) .Add(x => x.Hover, true) .Add(x => x.Bordered, true) .Add(x => x.Compact, true)); // Assert var table = cut.Find("table.sb-table"); Assert.Contains("sb-table--striped", table.ClassList); Assert.Contains("sb-table--hover", table.ClassList); Assert.Contains("sb-table--bordered", table.ClassList); Assert.Contains("sb-table--compact", table.ClassList); } private class TableTestItem { public int Id { get; set; } public string Name { get; set; } = string.Empty; public int Value { get; set; } } }