using Microsoft.AspNetCore.Components; using Bunit; using SufiChain.SufiBlazor.Components.Feedback; using Xunit; namespace SufiChain.SufiBlazor.Tests.Components.Feedback; public class SbBannerTests : BunitContext { private IRenderedComponent RenderBanner( Action>? configure = null) { return Render(p => { p.Add(x => x.Message, "Banner message"); configure?.Invoke(p); }); } [Fact] public void RendersBannerStructure() { // Arrange & Act var cut = RenderBanner(); // Assert var banner = cut.Find(".sb-banner"); Assert.NotNull(banner); Assert.Equal("alert", banner.GetAttribute("role")); Assert.NotNull(cut.Find(".sb-banner__header")); Assert.NotNull(cut.Find(".sb-banner__content")); Assert.NotNull(cut.Find(".sb-banner__icon")); } [Fact] public void RendersMessageWhenNoChildContent() { // Arrange & Act var cut = RenderBanner(); // Assert Assert.Contains("Banner message", cut.Markup); var message = cut.Find(".sb-banner__message"); Assert.NotNull(message); } [Fact] public void RendersChildContentOverMessage() { // Arrange & Act var cut = Render(p => p .Add(x => x.Message, "Fallback") .AddChildContent("Custom content")); // Assert - ChildContent takes precedence Assert.Contains("Custom content", cut.Markup); Assert.Empty(cut.FindAll(".sb-banner__message")); } [Fact] public void RendersTitleWhenProvided() { // Arrange & Act var cut = RenderBanner(p => p.Add(x => x.Title, "Important Notice")); // Assert var title = cut.Find(".sb-banner__title"); Assert.NotNull(title); Assert.Contains("Important Notice", title.TextContent); } [Fact] public void DoesNotRenderTitleWhenEmpty() { // Arrange & Act var cut = RenderBanner(); // Assert Assert.Empty(cut.FindAll(".sb-banner__title")); } [Fact] public void AppliesInfoSeverityByDefault() { // Arrange & Act var cut = RenderBanner(); // Assert var banner = cut.Find(".sb-banner"); Assert.Contains("sb-banner--info", banner.ClassList); } [Fact] public void AppliesSuccessSeverity() { // Arrange & Act var cut = RenderBanner(p => p.Add(x => x.Severity, SbBannerSeverity.Success)); // Assert var banner = cut.Find(".sb-banner"); Assert.Contains("sb-banner--success", banner.ClassList); } [Fact] public void AppliesWarningSeverity() { // Arrange & Act var cut = RenderBanner(p => p.Add(x => x.Severity, SbBannerSeverity.Warning)); // Assert var banner = cut.Find(".sb-banner"); Assert.Contains("sb-banner--warning", banner.ClassList); } [Fact] public void AppliesErrorSeverity() { // Arrange & Act var cut = RenderBanner(p => p.Add(x => x.Severity, SbBannerSeverity.Error)); // Assert var banner = cut.Find(".sb-banner"); Assert.Contains("sb-banner--error", banner.ClassList); } [Fact] public void RendersCloseButtonWhenDismissible() { // Arrange & Act var cut = RenderBanner(p => p.Add(x => x.Dismissible, true)); // Assert var closeBtn = cut.Find(".sb-banner__close"); Assert.NotNull(closeBtn); Assert.Equal("Dismiss", closeBtn.GetAttribute("aria-label")); } [Fact] public void DoesNotRenderCloseButtonWhenNotDismissible() { // Arrange & Act var cut = RenderBanner(p => p.Add(x => x.Dismissible, false)); // Assert Assert.Empty(cut.FindAll(".sb-banner__close")); } [Fact] public async Task HidesBannerWhenDismissed() { // Arrange var cut = RenderBanner(); Assert.NotNull(cut.Find(".sb-banner")); // Act var closeBtn = cut.Find(".sb-banner__close"); await cut.InvokeAsync(() => closeBtn!.Click()); cut.WaitForState(() => cut.FindAll(".sb-banner").Count == 0); // Assert Assert.Empty(cut.FindAll(".sb-banner")); } [Fact] public async Task InvokesOnDismissWhenDismissed() { // Arrange var dismissed = false; var cut = RenderBanner(p => p .Add(x => x.Dismissible, true) .Add(x => x.OnDismiss, EventCallback.Factory.Create(this, () => dismissed = true))); var closeBtn = cut.Find(".sb-banner__close"); // Act await cut.InvokeAsync(() => closeBtn!.Click()); cut.WaitForState(() => dismissed); // Assert Assert.True(dismissed); } [Fact] public async Task ShowRestoresVisibilityAfterDismiss() { // Arrange var cut = RenderBanner(); var closeBtn = cut.Find(".sb-banner__close"); await cut.InvokeAsync(() => closeBtn!.Click()); cut.WaitForState(() => cut.FindAll(".sb-banner").Count == 0); // Act - Show() calls StateHasChanged, must run on Dispatcher await cut.InvokeAsync(() => cut.Instance.Show()); cut.WaitForState(() => cut.FindAll(".sb-banner").Count > 0); // Assert Assert.NotNull(cut.Find(".sb-banner")); } [Fact] public void RendersActionsWhenProvided() { // Arrange & Act var cut = RenderBanner(p => p .Add(x => x.Actions, a => a.AddMarkupContent(0, ""))); // Assert var actions = cut.Find(".sb-banner__actions"); Assert.NotNull(actions); var actionBtn = cut.Find(".banner-action"); Assert.NotNull(actionBtn); Assert.Contains("Learn more", actionBtn.TextContent); } [Fact] public void DoesNotRenderActionsWhenNull() { // Arrange & Act var cut = RenderBanner(); // Assert Assert.Empty(cut.FindAll(".sb-banner__actions")); } [Fact] public void AppliesCustomClass() { // Arrange & Act var cut = RenderBanner(p => p.Add(x => x.Class, "my-banner")); // Assert var banner = cut.Find(".sb-banner"); Assert.Contains("my-banner", banner.ClassList); } }