281 lines
8.3 KiB
C#
281 lines
8.3 KiB
C#
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<TableTestItem> CreateTestItems(int count)
|
|
{
|
|
return Enumerable.Range(1, count)
|
|
.Select(i => new TableTestItem { Id = i, Name = $"Item {i}", Value = i * 10 })
|
|
.ToList();
|
|
}
|
|
|
|
private static RenderFragment<TableTestItem> 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<SbTable<TableTestItem>> RenderTable(
|
|
List<TableTestItem>? items = null,
|
|
Action<ComponentParameterCollectionBuilder<SbTable<TableTestItem>>>? configure = null)
|
|
{
|
|
items ??= CreateTestItems(3);
|
|
return Render<SbTable<TableTestItem>>(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, "<tr><th>Name</th><th>Value</th></tr>")));
|
|
|
|
// 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, "<tr><td colspan=\"2\">Total: 3 items</td></tr>")));
|
|
|
|
// 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<SbTable<TableTestItem>>(p => p
|
|
.Add(x => x.Items, new List<TableTestItem>())
|
|
.Add(x => x.RowTemplate, RowTemplate)
|
|
.Add(x => x.EmptyContent, e => e.AddMarkupContent(0, "<span class=\"custom-empty\">No data</span>")));
|
|
|
|
// 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<SbTable<TableTestItem>>(p => p
|
|
.Add(x => x.Items, (IEnumerable<TableTestItem>?)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<SbTable<TableTestItem>>(p => p
|
|
.Add(x => x.Items, new List<TableTestItem>())
|
|
.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<SbTable<TableTestItem>>(p => p
|
|
.Add(x => x.Items, items)
|
|
.Add(x => x.RowTemplate, RowTemplate)
|
|
.Add(x => x.OnRowClick, EventCallback.Factory.Create<TableTestItem>(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; }
|
|
}
|
|
}
|