first commit

This commit is contained in:
2026-05-18 15:53:59 +03:30
commit 2c100028a1
534 changed files with 94240 additions and 0 deletions
@@ -0,0 +1,340 @@
using Bunit;
using Bunit.TestDoubles;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using SufiChain.SufiBlazor.Components.Navigation;
using Xunit;
namespace SufiChain.SufiBlazor.Tests.Components.Navigation;
public class SbNavMenuTests : BunitContext
{
private IRenderedComponent<SbNavMenu> RenderNavMenu(
Action<ComponentParameterCollectionBuilder<SbNavMenu>>? configure = null)
{
return Render<SbNavMenu>(p => configure?.Invoke(p));
}
private IRenderedComponent<SbNavItem> RenderNavItem(
Action<ComponentParameterCollectionBuilder<SbNavItem>>? configure = null)
{
return Render<SbNavItem>(p => configure?.Invoke(p));
}
// --- SbNavMenu tests ---
[Fact]
public void RendersNavMenuStructure()
{
// Arrange & Act
var cut = RenderNavMenu(p => p.AddChildContent("<span>Nav content</span>"));
// Assert
var nav = cut.Find("nav.sb-nav-menu");
Assert.NotNull(nav);
Assert.Equal("nav", nav.TagName.ToLowerInvariant());
Assert.Equal("Navigation", nav.GetAttribute("aria-label"));
}
[Fact]
public void RendersChildContent()
{
// Arrange & Act
var cut = RenderNavMenu(p => p.AddChildContent("<ul><li>Item</li></ul>"));
// Assert
Assert.Contains("Item", cut.Markup);
Assert.NotNull(cut.Find("ul"));
}
[Fact]
public void UsesCustomAriaLabel()
{
// Arrange & Act
var cut = RenderNavMenu(p => p
.Add(x => x.AriaLabel, "Main menu")
.AddChildContent("<span></span>"));
// Assert
var nav = cut.Find("nav");
Assert.Equal("Main menu", nav.GetAttribute("aria-label"));
}
[Fact]
public void AddsCollapsedClassWhenCollapsedTrue()
{
// Arrange & Act
var cut = RenderNavMenu(p => p
.Add(x => x.Collapsed, true)
.AddChildContent("<span></span>"));
// Assert
var nav = cut.Find("nav.sb-nav-menu");
Assert.Contains("sb-nav-menu--collapsed", nav.ClassList);
}
[Fact]
public void DoesNotAddCollapsedClassWhenCollapsedFalse()
{
// Arrange & Act
var cut = RenderNavMenu(p => p
.Add(x => x.Collapsed, false)
.AddChildContent("<span></span>"));
// Assert
var nav = cut.Find("nav.sb-nav-menu");
Assert.DoesNotContain("sb-nav-menu--collapsed", nav.ClassList);
}
[Fact]
public void AppliesClassParameter()
{
// Arrange & Act
var cut = RenderNavMenu(p => p
.Add(x => x.Class, "sidebar-menu")
.AddChildContent("<span></span>"));
// Assert
var nav = cut.Find("nav.sb-nav-menu");
Assert.Contains("sidebar-menu", nav.ClassList);
}
[Fact]
public void AppliesStyleParameter()
{
// Arrange & Act
var cut = RenderNavMenu(p => p
.Add(x => x.Style, "width: 200px;")
.AddChildContent("<span></span>"));
// Assert
var nav = cut.Find("nav");
var style = nav.GetAttribute("style");
Assert.Contains("width: 200px", style);
}
[Fact]
public async Task ToggleAsync_FlipsCollapsedAndInvokesCallback()
{
// Arrange
var received = false;
var cut = RenderNavMenu(p => p
.Add(x => x.Collapsed, false)
.Add(x => x.CollapsedChanged, EventCallback.Factory.Create<bool>(this, v => received = v))
.AddChildContent("<span></span>"));
// Act
await cut.InvokeAsync(() => cut.Instance.ToggleAsync());
// Assert
Assert.True(received);
cut.Render();
var nav = cut.Find("nav.sb-nav-menu");
Assert.Contains("sb-nav-menu--collapsed", nav.ClassList);
}
[Fact]
public void PassesAdditionalAttributes()
{
// Arrange & Act
var cut = RenderNavMenu(p => p
.Add(x => x.AdditionalAttributes, new Dictionary<string, object> { { "data-testid", "main-nav" } })
.AddChildContent("<span></span>"));
// Assert
var nav = cut.Find("nav");
Assert.Equal("main-nav", nav.GetAttribute("data-testid"));
}
[Fact]
public void RendersNavItemChildren()
{
// Arrange & Act
var cut = Render<SbNavMenu>(p => p.AddChildContent(b =>
{
b.OpenComponent<SbNavItem>(0);
b.AddAttribute(1, "Href", "/");
b.AddAttribute(2, "Text", "Home");
b.CloseComponent();
b.OpenComponent<SbNavItem>(3);
b.AddAttribute(4, "Href", "/about");
b.AddAttribute(5, "Text", "About");
b.CloseComponent();
}));
// Assert
var nav = cut.Find("nav.sb-nav-menu");
Assert.NotNull(nav);
var items = cut.FindAll("a.sb-nav-item");
Assert.Equal(2, items.Count);
Assert.Contains("Home", cut.Markup);
Assert.Contains("About", cut.Markup);
}
// --- SbNavItem tests ---
[Fact]
public void NavItem_RendersAsAnchor()
{
// Arrange & Act
var cut = RenderNavItem(p => p
.Add(x => x.Href, "/dashboard")
.Add(x => x.Text, "Dashboard"));
// Assert
var anchor = cut.Find("a.sb-nav-item");
Assert.NotNull(anchor);
Assert.Equal("/dashboard", anchor.GetAttribute("href"));
}
[Fact]
public void NavItem_RendersText()
{
// Arrange & Act
var cut = RenderNavItem(p => p
.Add(x => x.Href, "/")
.Add(x => x.Text, "Home"));
// Assert
var text = cut.Find(".sb-nav-item__text");
Assert.NotNull(text);
Assert.Contains("Home", text.TextContent);
}
[Fact]
public void NavItem_RendersIconWhenProvided()
{
// Arrange & Act
var cut = RenderNavItem(p => p
.Add(x => x.Href, "/")
.Add(x => x.Text, "Home")
.Add(x => x.Icon, (RenderFragment)(b => b.AddMarkupContent(0, "<span>ico</span>"))));
// Assert
var icon = cut.Find(".sb-nav-item__icon");
Assert.NotNull(icon);
Assert.Contains("ico", icon.InnerHtml);
}
[Fact]
public void NavItem_RendersBadgeWhenProvided()
{
// Arrange & Act
var cut = RenderNavItem(p => p
.Add(x => x.Href, "/inbox")
.Add(x => x.Text, "Inbox")
.Add(x => x.Badge, (RenderFragment)(b => b.AddMarkupContent(0, "3"))));
// Assert
var badge = cut.Find(".sb-nav-item__badge");
Assert.NotNull(badge);
Assert.Contains("3", badge.TextContent);
}
[Fact]
public void NavItem_AddsActiveClassWhenCurrentPage()
{
// Arrange - navigate to /products then render item with Href="/products"
var navMan = Services.GetRequiredService<BunitNavigationManager>();
navMan.NavigateTo("http://localhost/products");
var cut = RenderNavItem(p => p
.Add(x => x.Href, "/products")
.Add(x => x.Text, "Products"));
// Assert
var anchor = cut.Find("a.sb-nav-item");
Assert.Contains("sb-nav-item--active", anchor.ClassList);
}
[Fact]
public void NavItem_DoesNotAddActiveClassWhenNotCurrentPage()
{
// Arrange - default URI is base, render item with Href="/other"
var cut = RenderNavItem(p => p
.Add(x => x.Href, "/other-page")
.Add(x => x.Text, "Other"));
// Assert
var anchor = cut.Find("a.sb-nav-item");
Assert.DoesNotContain("sb-nav-item--active", anchor.ClassList);
}
[Fact]
public void NavItem_AddsDisabledClassWhenDisabledTrue()
{
// Arrange & Act
var cut = RenderNavItem(p => p
.Add(x => x.Href, "/")
.Add(x => x.Text, "Disabled")
.Add(x => x.Disabled, true));
// Assert
var anchor = cut.Find("a.sb-nav-item");
Assert.Contains("sb-nav-item--disabled", anchor.ClassList);
}
[Fact]
public void NavItem_AppliesClassParameter()
{
// Arrange & Act
var cut = RenderNavItem(p => p
.Add(x => x.Href, "/")
.Add(x => x.Text, "Item")
.Add(x => x.Class, "custom-item"));
// Assert
var anchor = cut.Find("a.sb-nav-item");
Assert.Contains("custom-item", anchor.ClassList);
}
[Fact]
public void NavItem_AppliesStyleParameter()
{
// Arrange & Act
var cut = RenderNavItem(p => p
.Add(x => x.Href, "/")
.Add(x => x.Text, "Item")
.Add(x => x.Style, "font-weight: bold;"));
// Assert
var anchor = cut.Find("a");
var style = anchor.GetAttribute("style");
Assert.Contains("font-weight: bold", style);
}
[Fact]
public void NavItem_PassesAdditionalAttributes()
{
// Arrange & Act
var cut = RenderNavItem(p => p
.Add(x => x.Href, "/")
.Add(x => x.Text, "Item")
.Add(x => x.AdditionalAttributes, new Dictionary<string, object> { { "data-testid", "nav-home" } }));
// Assert
var anchor = cut.Find("a");
Assert.Equal("nav-home", anchor.GetAttribute("data-testid"));
}
[Fact]
public void NavItem_WorksInsideSbNavMenu()
{
// Arrange & Act
var cut = Render<SbNavMenu>(p => p.AddChildContent(b =>
{
b.OpenComponent<SbNavItem>(0);
b.AddAttribute(1, "Href", "/dashboard");
b.AddAttribute(2, "Text", "Dashboard");
b.CloseComponent();
}));
// Assert
var nav = cut.Find("nav.sb-nav-menu");
var item = cut.Find("a.sb-nav-item");
Assert.NotNull(nav);
Assert.NotNull(item);
Assert.Equal("/dashboard", item.GetAttribute("href"));
Assert.Contains("Dashboard", cut.Markup);
}
}