using Microsoft.AspNetCore.Components; using Bunit; using SufiChain.SufiBlazor.Components.Navigation; using Xunit; namespace SufiChain.SufiBlazor.Tests.Components.Navigation; public class SbAccordionTests : BunitContext { private IRenderedComponent RenderAccordion( Action>? configure = null) { return Render(p => configure?.Invoke(p)); } private IRenderedComponent RenderAccordionItem( Action>? configure = null) { return Render(p => configure?.Invoke(p)); } // --- SbAccordion tests --- [Fact] public void RendersAccordionStructure() { // Arrange & Act var cut = RenderAccordion(); // Assert var div = cut.Find(".sb-accordion"); Assert.NotNull(div); Assert.Equal("div", div.TagName.ToLowerInvariant()); } [Fact] public void RendersChildContent() { // Arrange & Act var cut = RenderAccordion(p => p.AddChildContent("Accordion content")); // Assert Assert.Contains("Accordion content", cut.Markup); Assert.NotNull(cut.Find("span")); } [Fact] public void AppliesClassParameter() { // Arrange & Act var cut = RenderAccordion(p => p.Add(x => x.Class, "my-accordion")); // Assert var div = cut.Find(".sb-accordion"); Assert.Contains("my-accordion", div.ClassList); } [Fact] public void RendersAccordionItemChildren() { // Arrange & Act var cut = Render(p => p.AddChildContent(b => { b.OpenComponent(0); b.AddAttribute(1, "Title", "Section 1"); b.AddAttribute(2, "ChildContent", (RenderFragment)(c => c.AddMarkupContent(0, "

Content 1

"))); b.CloseComponent(); b.OpenComponent(3); b.AddAttribute(4, "Title", "Section 2"); b.AddAttribute(5, "ChildContent", (RenderFragment)(c => c.AddMarkupContent(0, "

Content 2

"))); b.CloseComponent(); })); // Assert var accordion = cut.Find(".sb-accordion"); Assert.NotNull(accordion); var items = cut.FindAll(".sb-accordion-item"); Assert.Equal(2, items.Count); Assert.Contains("Section 1", cut.Markup); Assert.Contains("Section 2", cut.Markup); Assert.Contains("Content 1", cut.Markup); Assert.Contains("Content 2", cut.Markup); } // --- SbAccordionItem tests --- [Fact] public void AccordionItem_RendersStructure() { // Arrange & Act var cut = RenderAccordionItem(p => p.Add(x => x.Title, "Item Title")); // Assert var div = cut.Find(".sb-accordion-item"); Assert.NotNull(div); Assert.Equal("div", div.TagName.ToLowerInvariant()); var button = cut.Find(".sb-accordion-item__header"); Assert.NotNull(button); Assert.Equal("button", button.TagName.ToLowerInvariant()); } [Fact] public void AccordionItem_RendersTitle() { // Arrange & Act var cut = RenderAccordionItem(p => p.Add(x => x.Title, "My Section Title")); // Assert var title = cut.Find(".sb-accordion-item__title"); Assert.NotNull(title); Assert.Contains("My Section Title", title.TextContent); } [Fact] public void AccordionItem_RendersChildContent() { // Arrange & Act var cut = RenderAccordionItem(p => p .Add(x => x.Title, "Section") .AddChildContent("

Body content

")); // Assert Assert.Contains("Body content", cut.Markup); var body = cut.Find(".sb-accordion-item__body"); Assert.NotNull(body); Assert.NotNull(cut.Find("p")); } [Fact] public void AccordionItem_IsCollapsedByDefault() { // Arrange & Act var cut = RenderAccordionItem(p => p.Add(x => x.Title, "Section")); // Assert var item = cut.Find(".sb-accordion-item"); Assert.DoesNotContain("sb-accordion-item--expanded", item.ClassList); var content = cut.Find(".sb-accordion-item__content"); Assert.NotNull(content.GetAttribute("hidden")); } [Fact] public void AccordionItem_ShowsExpandedWhenDefaultExpandedTrue() { // Arrange & Act var cut = RenderAccordionItem(p => p .Add(x => x.Title, "Section") .Add(x => x.DefaultExpanded, true)); // Assert - standalone item uses its own _isExpanded var item = cut.Find(".sb-accordion-item"); Assert.Contains("sb-accordion-item--expanded", item.ClassList); var content = cut.Find(".sb-accordion-item__content"); Assert.Null(content.GetAttribute("hidden")); } [Fact] public void AccordionItem_AddsDisabledClassWhenDisabledTrue() { // Arrange & Act var cut = RenderAccordionItem(p => p .Add(x => x.Title, "Section") .Add(x => x.Disabled, true)); // Assert var item = cut.Find(".sb-accordion-item"); Assert.Contains("sb-accordion-item--disabled", item.ClassList); var button = cut.Find("button"); Assert.NotNull(button.GetAttribute("disabled")); } [Fact] public async Task AccordionItem_Standalone_ClickTogglesExpansion() { // Arrange - standalone item (no Parent) var cut = RenderAccordionItem(p => p .Add(x => x.Title, "Section") .AddChildContent("

Content

")); // Assert initially collapsed var item = cut.Find(".sb-accordion-item"); Assert.DoesNotContain("sb-accordion-item--expanded", item.ClassList); // Act - click to expand var button = cut.Find("button"); await cut.InvokeAsync(() => button.Click()); // Assert expanded cut.Render(); item = cut.Find(".sb-accordion-item"); Assert.Contains("sb-accordion-item--expanded", item.ClassList); // Act - click to collapse button = cut.Find("button"); await cut.InvokeAsync(() => button.Click()); // Assert collapsed again cut.Render(); item = cut.Find(".sb-accordion-item"); Assert.DoesNotContain("sb-accordion-item--expanded", item.ClassList); } [Fact] public void AccordionItem_InsideAccordion_DefaultExpandedShowsExpanded() { // Arrange & Act var cut = Render(p => p.AddChildContent(b => { b.OpenComponent(0); b.AddAttribute(1, "Title", "Expanded Section"); b.AddAttribute(2, "DefaultExpanded", true); b.AddAttribute(3, "ChildContent", (RenderFragment)(c => c.AddMarkupContent(0, "Expanded content"))); b.CloseComponent(); })); // Assert var item = cut.Find(".sb-accordion-item"); Assert.Contains("sb-accordion-item--expanded", item.ClassList); Assert.Contains("Expanded content", cut.Markup); } [Fact] public async Task AccordionItem_InsideAccordion_ClickTogglesExpansion() { // Arrange var cut = Render(p => p.AddChildContent(b => { b.OpenComponent(0); b.AddAttribute(1, "Title", "Section A"); b.AddAttribute(2, "ChildContent", (RenderFragment)(c => c.AddMarkupContent(0, "Content A"))); b.CloseComponent(); })); // Assert initially collapsed var item = cut.Find(".sb-accordion-item"); Assert.DoesNotContain("sb-accordion-item--expanded", item.ClassList); // Act - click to expand var button = cut.Find("button.sb-accordion-item__header"); await cut.InvokeAsync(() => button.Click()); // Assert expanded cut.Render(); item = cut.Find(".sb-accordion-item"); Assert.Contains("sb-accordion-item--expanded", item.ClassList); } [Fact] public async Task AccordionItem_Disabled_DoesNotToggleOnClick() { // Arrange var cut = Render(p => p.AddChildContent(b => { b.OpenComponent(0); b.AddAttribute(1, "Title", "Disabled Section"); b.AddAttribute(2, "Disabled", true); b.AddAttribute(3, "ChildContent", (RenderFragment)(c => c.AddMarkupContent(0, "Hidden"))); b.CloseComponent(); })); // Act - click disabled button var button = cut.Find("button.sb-accordion-item__header"); await cut.InvokeAsync(() => button.Click()); // Assert - still collapsed (disabled prevents toggle) var item = cut.Find(".sb-accordion-item"); Assert.DoesNotContain("sb-accordion-item--expanded", item.ClassList); } [Fact] public async Task Accordion_Multiple_AllowsMultipleExpanded() { // Arrange var cut = Render(p => p .Add(x => x.Multiple, true) .AddChildContent(b => { b.OpenComponent(0); b.AddAttribute(1, "Title", "Item 1"); b.AddAttribute(2, "ChildContent", (RenderFragment)(c => c.AddMarkupContent(0, "C1"))); b.CloseComponent(); b.OpenComponent(3); b.AddAttribute(4, "Title", "Item 2"); b.AddAttribute(5, "ChildContent", (RenderFragment)(c => c.AddMarkupContent(0, "C2"))); b.CloseComponent(); })); var items = cut.FindAll(".sb-accordion-item"); var buttons = cut.FindAll("button.sb-accordion-item__header"); Assert.Equal(2, buttons.Count); // Act - expand first, then second await cut.InvokeAsync(() => buttons[0].Click()); cut.Render(); await cut.InvokeAsync(() => buttons[1].Click()); cut.Render(); // Assert - both expanded items = cut.FindAll(".sb-accordion-item"); Assert.Contains("sb-accordion-item--expanded", items[0].ClassList); Assert.Contains("sb-accordion-item--expanded", items[1].ClassList); } [Fact] public async Task Accordion_OnExpandedChanged_FiresWhenItemToggled() { // Arrange HashSet? receivedItems = null; var cut = Render(p => p .Add(x => x.OnExpandedChanged, EventCallback.Factory.Create>(this, items => receivedItems = items)) .AddChildContent(b => { b.OpenComponent(0); b.AddAttribute(1, "Title", "Section"); b.AddAttribute(2, "ChildContent", (RenderFragment)(c => c.AddMarkupContent(0, "Content"))); b.CloseComponent(); })); // Act - expand var button = cut.Find("button.sb-accordion-item__header"); await cut.InvokeAsync(() => button.Click()); // Assert Assert.NotNull(receivedItems); Assert.Single(receivedItems); } }