This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Bunit;
|
||||
using SufiChain.SufiBlazor.Components.Feedback;
|
||||
using Xunit;
|
||||
|
||||
namespace SufiChain.SufiBlazor.Tests.Components.Feedback;
|
||||
|
||||
public class SbAlertTests : BunitContext
|
||||
{
|
||||
private IRenderedComponent<SbAlert> RenderAlert(
|
||||
Action<ComponentParameterCollectionBuilder<SbAlert>>? configure = null)
|
||||
{
|
||||
return Render<SbAlert>(p =>
|
||||
{
|
||||
p.AddChildContent("<span>Alert message</span>");
|
||||
configure?.Invoke(p);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersAlertStructure()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert();
|
||||
|
||||
// Assert
|
||||
var alert = cut.Find(".sb-alert");
|
||||
Assert.NotNull(alert);
|
||||
Assert.NotNull(cut.Find(".sb-alert__icon"));
|
||||
Assert.NotNull(cut.Find(".sb-alert__content"));
|
||||
Assert.NotNull(cut.Find(".sb-alert__message"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersChildContent()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert();
|
||||
|
||||
// Assert
|
||||
Assert.Contains("Alert message", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersTitleWhenProvided()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert(p => p.Add(x => x.Title, "Heads up"));
|
||||
|
||||
// Assert
|
||||
var title = cut.Find(".sb-alert__title");
|
||||
Assert.NotNull(title);
|
||||
Assert.Contains("Heads up", title.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotRenderTitleWhenEmpty()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(cut.FindAll(".sb-alert__title"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesInfoSeverityByDefault()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert();
|
||||
|
||||
// Assert
|
||||
var alert = cut.Find(".sb-alert");
|
||||
Assert.Contains("sb-alert--info", alert.ClassList);
|
||||
Assert.Contains("ℹ", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesSuccessSeverity()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert(p => p.Add(x => x.Severity, SbAlertSeverity.Success));
|
||||
|
||||
// Assert
|
||||
var alert = cut.Find(".sb-alert");
|
||||
Assert.Contains("sb-alert--success", alert.ClassList);
|
||||
Assert.Contains("✓", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesWarningSeverity()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert(p => p.Add(x => x.Severity, SbAlertSeverity.Warning));
|
||||
|
||||
// Assert
|
||||
var alert = cut.Find(".sb-alert");
|
||||
Assert.Contains("sb-alert--warning", alert.ClassList);
|
||||
Assert.Contains("⚠", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesDangerSeverity()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert(p => p.Add(x => x.Severity, SbAlertSeverity.Danger));
|
||||
|
||||
// Assert
|
||||
var alert = cut.Find(".sb-alert");
|
||||
Assert.Contains("sb-alert--danger", alert.ClassList);
|
||||
Assert.Contains("✕", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasRoleAlertWhenDanger()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert(p => p.Add(x => x.Severity, SbAlertSeverity.Danger));
|
||||
|
||||
// Assert
|
||||
var alert = cut.Find(".sb-alert");
|
||||
Assert.Equal("alert", alert.GetAttribute("role"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasRoleStatusWhenNotDanger()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert(p => p.Add(x => x.Severity, SbAlertSeverity.Info));
|
||||
|
||||
// Assert
|
||||
var alert = cut.Find(".sb-alert");
|
||||
Assert.Equal("status", alert.GetAttribute("role"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersDismissButtonWhenDismissible()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert(p => p.Add(x => x.Dismissible, true));
|
||||
|
||||
// Assert
|
||||
var dismissBtn = cut.Find(".sb-alert__dismiss");
|
||||
Assert.NotNull(dismissBtn);
|
||||
Assert.Equal("button", dismissBtn.TagName.ToLowerInvariant());
|
||||
Assert.Equal("Dismiss", dismissBtn.GetAttribute("aria-label"));
|
||||
Assert.Contains("×", dismissBtn.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotRenderDismissButtonWhenNotDismissible()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(cut.FindAll(".sb-alert__dismiss"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokesOnDismissWhenDismissButtonClicked()
|
||||
{
|
||||
// Arrange
|
||||
var dismissed = false;
|
||||
|
||||
var cut = RenderAlert(p => p
|
||||
.Add(x => x.Dismissible, true)
|
||||
.Add(x => x.OnDismiss, EventCallback.Factory.Create(this, () => dismissed = true)));
|
||||
|
||||
var dismissBtn = cut.Find(".sb-alert__dismiss");
|
||||
|
||||
// Act
|
||||
await cut.InvokeAsync(() => dismissBtn!.Click());
|
||||
|
||||
cut.WaitForState(() => dismissed);
|
||||
|
||||
// Assert
|
||||
Assert.True(dismissed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesDenseClass()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert(p => p.Add(x => x.Dense, true));
|
||||
|
||||
// Assert
|
||||
var alert = cut.Find(".sb-alert");
|
||||
Assert.Contains("sb-alert--dense", alert.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesCustomClass()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert(p => p.Add(x => x.Class, "my-alert"));
|
||||
|
||||
// Assert
|
||||
var alert = cut.Find(".sb-alert");
|
||||
Assert.Contains("my-alert", alert.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesInlineStyle()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderAlert(p => p.Add(x => x.Style, "margin-top: 10px;"));
|
||||
|
||||
// Assert
|
||||
var alert = cut.Find(".sb-alert");
|
||||
Assert.Contains("margin-top: 10px", alert.GetAttribute("style"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Bunit;
|
||||
using SufiChain.SufiBlazor.Components;
|
||||
using SufiChain.SufiBlazor.Components.Feedback;
|
||||
using Xunit;
|
||||
|
||||
namespace SufiChain.SufiBlazor.Tests.Components.Feedback;
|
||||
|
||||
public class SbBadgeTests : BunitContext
|
||||
{
|
||||
private IRenderedComponent<SbBadge> RenderBadge(
|
||||
Action<ComponentParameterCollectionBuilder<SbBadge>>? configure = null)
|
||||
{
|
||||
return Render<SbBadge>(p =>
|
||||
{
|
||||
p.Add(x => x.Value, 5);
|
||||
configure?.Invoke(p);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersBadgeStructure()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderBadge();
|
||||
|
||||
// Assert
|
||||
var badge = cut.Find(".sb-badge");
|
||||
Assert.NotNull(badge);
|
||||
Assert.Equal("span", badge.TagName.ToLowerInvariant());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersValueWhenValueSet()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderBadge();
|
||||
|
||||
// Assert
|
||||
Assert.Contains("5", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersChildContentOverValue()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = Render<SbBadge>(p => p
|
||||
.Add(x => x.Value, 10)
|
||||
.AddChildContent("New"));
|
||||
|
||||
// Assert - ChildContent takes precedence
|
||||
Assert.Contains("New", cut.Markup);
|
||||
Assert.DoesNotContain("10", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersMaxPlusWhenValueExceedsMax()
|
||||
{
|
||||
// Arrange & Act - use Render directly to avoid Value added twice
|
||||
var cut = Render<SbBadge>(p => p
|
||||
.Add(x => x.Value, 150)
|
||||
.Add(x => x.Max, 99));
|
||||
|
||||
// Assert
|
||||
Assert.Contains("99+", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersExactValueWhenEqualToMax()
|
||||
{
|
||||
// Arrange & Act - use Render directly to avoid Value added twice
|
||||
var cut = Render<SbBadge>(p => p
|
||||
.Add(x => x.Value, 99)
|
||||
.Add(x => x.Max, 99));
|
||||
|
||||
// Assert
|
||||
Assert.Contains("99", cut.Markup);
|
||||
Assert.DoesNotContain("99+", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersCustomMaxPlus()
|
||||
{
|
||||
// Arrange & Act - use Render directly to avoid Value added twice
|
||||
var cut = Render<SbBadge>(p => p
|
||||
.Add(x => x.Value, 50)
|
||||
.Add(x => x.Max, 10));
|
||||
|
||||
// Assert
|
||||
Assert.Contains("10+", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersEmptyWhenValueNullAndNoChildContent()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = Render<SbBadge>(p => p.Add(x => x.Value, (int?)null));
|
||||
|
||||
// Assert - badge still renders, content is empty
|
||||
var badge = cut.Find(".sb-badge");
|
||||
Assert.NotNull(badge);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersDotMode()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderBadge(p => p.Add(x => x.Dot, true));
|
||||
|
||||
// Assert
|
||||
var badge = cut.Find(".sb-badge");
|
||||
Assert.Contains("sb-badge--dot", badge.ClassList);
|
||||
// Dot mode shows no value/child content inside
|
||||
Assert.DoesNotContain("5", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesPrimaryColorByDefault()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderBadge();
|
||||
|
||||
// Assert
|
||||
var badge = cut.Find(".sb-badge");
|
||||
Assert.Contains("sb-badge--primary", badge.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesSuccessColor()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderBadge(p => p.Add(x => x.Color, SbColor.Success));
|
||||
|
||||
// Assert
|
||||
var badge = cut.Find(".sb-badge");
|
||||
Assert.Contains("sb-badge--success", badge.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesWarningColor()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderBadge(p => p.Add(x => x.Color, SbColor.Warning));
|
||||
|
||||
// Assert
|
||||
var badge = cut.Find(".sb-badge");
|
||||
Assert.Contains("sb-badge--warning", badge.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesDangerColor()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderBadge(p => p.Add(x => x.Color, SbColor.Danger));
|
||||
|
||||
// Assert
|
||||
var badge = cut.Find(".sb-badge");
|
||||
Assert.Contains("sb-badge--danger", badge.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesAriaLabel()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderBadge(p => p.Add(x => x.AriaLabel, "5 unread messages"));
|
||||
|
||||
// Assert
|
||||
var badge = cut.Find(".sb-badge");
|
||||
Assert.Equal("5 unread messages", badge.GetAttribute("aria-label"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesCustomClass()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderBadge(p => p.Add(x => x.Class, "my-badge"));
|
||||
|
||||
// Assert
|
||||
var badge = cut.Find(".sb-badge");
|
||||
Assert.Contains("my-badge", badge.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesInlineStyle()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderBadge(p => p.Add(x => x.Style, "top: 0;"));
|
||||
|
||||
// Assert
|
||||
var badge = cut.Find(".sb-badge");
|
||||
Assert.Contains("top: 0", badge.GetAttribute("style"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
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<SbBanner> RenderBanner(
|
||||
Action<ComponentParameterCollectionBuilder<SbBanner>>? configure = null)
|
||||
{
|
||||
return Render<SbBanner>(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<SbBanner>(p => p
|
||||
.Add(x => x.Message, "Fallback")
|
||||
.AddChildContent("<span class=\"custom-content\">Custom content</span>"));
|
||||
|
||||
// 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, "<button class=\"banner-action\">Learn more</button>")));
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Bunit;
|
||||
using SufiChain.SufiBlazor.Components;
|
||||
using SufiChain.SufiBlazor.Components.Feedback;
|
||||
using Xunit;
|
||||
|
||||
namespace SufiChain.SufiBlazor.Tests.Components.Feedback;
|
||||
|
||||
public class SbChipTests : BunitContext
|
||||
{
|
||||
private IRenderedComponent<SbChip> RenderChip(
|
||||
Action<ComponentParameterCollectionBuilder<SbChip>>? configure = null)
|
||||
{
|
||||
return Render<SbChip>(p =>
|
||||
{
|
||||
p.AddChildContent("Chip label");
|
||||
configure?.Invoke(p);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersChipStructure()
|
||||
{
|
||||
// Arrange & Act - no OnClick, renders as span
|
||||
var cut = RenderChip();
|
||||
|
||||
// Assert
|
||||
var chip = cut.Find(".sb-chip");
|
||||
Assert.NotNull(chip);
|
||||
Assert.Equal("span", chip.TagName.ToLowerInvariant());
|
||||
Assert.NotNull(cut.Find(".sb-chip__label"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersChildContent()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip();
|
||||
|
||||
// Assert
|
||||
Assert.Contains("Chip label", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersAsButtonWhenOnClickProvided()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip(p => p.Add(x => x.OnClick, EventCallback.Factory.Create<MouseEventArgs>(this, _ => { })));
|
||||
|
||||
// Assert
|
||||
var chip = cut.Find(".sb-chip");
|
||||
Assert.Equal("button", chip.TagName.ToLowerInvariant());
|
||||
Assert.Contains("sb-chip--clickable", chip.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesFilledVariantByDefault()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip();
|
||||
|
||||
// Assert
|
||||
var chip = cut.Find(".sb-chip");
|
||||
Assert.Contains("sb-chip--filled", chip.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesOutlinedVariant()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip(p => p.Add(x => x.Variant, SbChipVariant.Outlined));
|
||||
|
||||
// Assert
|
||||
var chip = cut.Find(".sb-chip");
|
||||
Assert.Contains("sb-chip--outlined", chip.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesMdSizeByDefault()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip();
|
||||
|
||||
// Assert
|
||||
var chip = cut.Find(".sb-chip");
|
||||
Assert.Contains("sb-chip--md", chip.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesSmSize()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip(p => p.Add(x => x.Size, SbSize.Sm));
|
||||
|
||||
// Assert
|
||||
var chip = cut.Find(".sb-chip");
|
||||
Assert.Contains("sb-chip--sm", chip.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotApplyColorClassWhenDefault()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip();
|
||||
|
||||
// Assert - SbColor.Default adds no color class
|
||||
var chip = cut.Find(".sb-chip");
|
||||
Assert.DoesNotContain("sb-chip--default", chip.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesPrimaryColor()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip(p => p.Add(x => x.Color, SbColor.Primary));
|
||||
|
||||
// Assert
|
||||
var chip = cut.Find(".sb-chip");
|
||||
Assert.Contains("sb-chip--primary", chip.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesDisabledClass()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip(p => p.Add(x => x.Disabled, true));
|
||||
|
||||
// Assert
|
||||
var chip = cut.Find(".sb-chip");
|
||||
Assert.Contains("sb-chip--disabled", chip.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ButtonIsDisabledWhenDisabledTrue()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip(p => p
|
||||
.Add(x => x.OnClick, EventCallback.Factory.Create<MouseEventArgs>(this, _ => { }))
|
||||
.Add(x => x.Disabled, true));
|
||||
|
||||
// Assert - disabled attr can render as "disabled", "true", or "" (boolean attrs vary by renderer)
|
||||
var chip = cut.Find(".sb-chip");
|
||||
Assert.True(chip.GetAttribute("disabled") != null || chip.OuterHtml.Contains("disabled"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersStartIconWhenProvided()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip(p => p
|
||||
.Add(x => x.StartIcon, i => i.AddMarkupContent(0, "<span class=\"test-icon\">★</span>")));
|
||||
|
||||
// Assert
|
||||
var iconContainer = cut.Find(".sb-chip__icon");
|
||||
Assert.NotNull(iconContainer);
|
||||
var icon = cut.Find(".test-icon");
|
||||
Assert.NotNull(icon);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersRemoveButtonWhenOnRemoveProvided()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip(p => p.Add(x => x.OnRemove, EventCallback.Factory.Create(this, () => { })));
|
||||
|
||||
// Assert
|
||||
var removeBtn = cut.Find(".sb-chip__remove");
|
||||
Assert.NotNull(removeBtn);
|
||||
Assert.Equal("Remove", removeBtn.GetAttribute("aria-label"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotRenderRemoveButtonWhenOnRemoveNull()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(cut.FindAll(".sb-chip__remove"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokesOnClickWhenChipClicked()
|
||||
{
|
||||
// Arrange
|
||||
var clicked = false;
|
||||
var cut = RenderChip(p => p.Add(x => x.OnClick, EventCallback.Factory.Create<MouseEventArgs>(this, _ => clicked = true)));
|
||||
|
||||
var chip = cut.Find(".sb-chip");
|
||||
|
||||
// Act
|
||||
await cut.InvokeAsync(() => chip!.Click());
|
||||
|
||||
cut.WaitForState(() => clicked);
|
||||
|
||||
// Assert
|
||||
Assert.True(clicked);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokesOnRemoveWhenRemoveButtonClicked()
|
||||
{
|
||||
// Arrange
|
||||
var removed = false;
|
||||
var cut = RenderChip(p => p.Add(x => x.OnRemove, EventCallback.Factory.Create(this, () => removed = true)));
|
||||
|
||||
var removeBtn = cut.Find(".sb-chip__remove");
|
||||
|
||||
// Act
|
||||
await cut.InvokeAsync(() => removeBtn!.Click());
|
||||
|
||||
cut.WaitForState(() => removed);
|
||||
|
||||
// Assert
|
||||
Assert.True(removed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DoesNotInvokeOnClickWhenDisabled()
|
||||
{
|
||||
// Arrange
|
||||
var clicked = false;
|
||||
var cut = RenderChip(p => p
|
||||
.Add(x => x.OnClick, EventCallback.Factory.Create<MouseEventArgs>(this, _ => clicked = true))
|
||||
.Add(x => x.Disabled, true));
|
||||
|
||||
var chip = cut.Find(".sb-chip");
|
||||
|
||||
// Act
|
||||
await cut.InvokeAsync(() => chip!.Click());
|
||||
|
||||
// Assert - click should not fire when disabled
|
||||
Assert.False(clicked);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesCustomClass()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip(p => p.Add(x => x.Class, "my-chip"));
|
||||
|
||||
// Assert
|
||||
var chip = cut.Find(".sb-chip");
|
||||
Assert.Contains("my-chip", chip.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesInlineStyle()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderChip(p => p.Add(x => x.Style, "margin: 4px;"));
|
||||
|
||||
// Assert
|
||||
var chip = cut.Find(".sb-chip");
|
||||
Assert.Contains("margin: 4px", chip.GetAttribute("style"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Bunit;
|
||||
using SufiChain.SufiBlazor.Components.Feedback;
|
||||
using Xunit;
|
||||
|
||||
namespace SufiChain.SufiBlazor.Tests.Components.Feedback;
|
||||
|
||||
public class SbEmptyStateTests : BunitContext
|
||||
{
|
||||
private IRenderedComponent<SbEmptyState> RenderEmptyState(
|
||||
Action<ComponentParameterCollectionBuilder<SbEmptyState>>? configure = null)
|
||||
{
|
||||
return Render<SbEmptyState>(p => configure?.Invoke(p));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersEmptyStateStructure()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState();
|
||||
|
||||
// Assert
|
||||
var container = cut.Find(".sb-empty-state");
|
||||
Assert.NotNull(container);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersDefaultTitle()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState();
|
||||
|
||||
// Assert
|
||||
var title = cut.Find(".sb-empty-state__title");
|
||||
Assert.NotNull(title);
|
||||
Assert.Contains("No items found", title.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersDefaultIconText()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState();
|
||||
|
||||
// Assert
|
||||
var iconText = cut.Find(".sb-empty-state__icon-text");
|
||||
Assert.NotNull(iconText);
|
||||
Assert.Contains("📭", iconText.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersCustomTitle()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState(p => p.Add(x => x.Title, "No documents"));
|
||||
|
||||
// Assert
|
||||
var title = cut.Find(".sb-empty-state__title");
|
||||
Assert.NotNull(title);
|
||||
Assert.Contains("No documents", title.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersDescriptionWhenProvided()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState(p => p.Add(x => x.Description, "Create your first item to get started."));
|
||||
|
||||
// Assert
|
||||
var description = cut.Find(".sb-empty-state__description");
|
||||
Assert.NotNull(description);
|
||||
Assert.Contains("Create your first item to get started.", description.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotRenderDescriptionWhenEmpty()
|
||||
{
|
||||
// Arrange & Act - Description defaults to null
|
||||
var cut = RenderEmptyState();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(cut.FindAll(".sb-empty-state__description"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersIconWhenProvided()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState(p => p
|
||||
.Add(x => x.Icon, i => i.AddMarkupContent(0, "<span class=\"custom-icon\">📦</span>"))
|
||||
.Add(x => x.IconText, (string?)null)); // clear default to test Icon takes precedence
|
||||
|
||||
// Assert
|
||||
var iconContainer = cut.Find(".sb-empty-state__icon");
|
||||
Assert.NotNull(iconContainer);
|
||||
var customIcon = cut.Find(".custom-icon");
|
||||
Assert.NotNull(customIcon);
|
||||
Assert.Contains("📦", customIcon.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersIconTextWhenIconNull()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState(p => p.Add(x => x.IconText, "🔍"));
|
||||
|
||||
// Assert
|
||||
var iconText = cut.Find(".sb-empty-state__icon-text");
|
||||
Assert.NotNull(iconText);
|
||||
Assert.Contains("🔍", iconText.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IconTakesPrecedenceOverIconText()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState(p => p
|
||||
.Add(x => x.Icon, i => i.AddMarkupContent(0, "<span>Custom</span>"))
|
||||
.Add(x => x.IconText, "Fallback"));
|
||||
|
||||
// Assert - Icon used, not IconText
|
||||
Assert.Contains("Custom", cut.Markup);
|
||||
Assert.Empty(cut.FindAll(".sb-empty-state__icon-text"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersChildContentWhenProvided()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState(p => p
|
||||
.AddChildContent("<a href=\"#\" class=\"create-link\">Create item</a>"));
|
||||
|
||||
// Assert
|
||||
var content = cut.Find(".sb-empty-state__content");
|
||||
Assert.NotNull(content);
|
||||
var link = cut.Find(".create-link");
|
||||
Assert.NotNull(link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotRenderContentWhenChildContentNull()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(cut.FindAll(".sb-empty-state__content"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersActionsWhenProvided()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState(p => p
|
||||
.Add(x => x.Actions, a => a.AddMarkupContent(0, "<button class=\"action-btn\">Add</button>")));
|
||||
|
||||
// Assert
|
||||
var actions = cut.Find(".sb-empty-state__actions");
|
||||
Assert.NotNull(actions);
|
||||
var btn = cut.Find(".action-btn");
|
||||
Assert.NotNull(btn);
|
||||
Assert.Contains("Add", btn.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotRenderActionsWhenNull()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(cut.FindAll(".sb-empty-state__actions"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesCustomClass()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderEmptyState(p => p.Add(x => x.Class, "my-empty-state"));
|
||||
|
||||
// Assert
|
||||
var container = cut.Find(".sb-empty-state");
|
||||
Assert.Contains("my-empty-state", container.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersCompleteWithAllParts()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = Render<SbEmptyState>(p => p
|
||||
.Add(x => x.IconText, "📋")
|
||||
.Add(x => x.Title, "No tasks")
|
||||
.Add(x => x.Description, "Add a task to begin.")
|
||||
.AddChildContent("<p>Or import from file.</p>")
|
||||
.Add(x => x.Actions, a => a.AddMarkupContent(0, "<button>New Task</button>")));
|
||||
|
||||
// Assert
|
||||
Assert.Contains("📋", cut.Markup);
|
||||
Assert.Contains("No tasks", cut.Markup);
|
||||
Assert.Contains("Add a task to begin.", cut.Markup);
|
||||
Assert.Contains("Or import from file.", cut.Markup);
|
||||
Assert.Contains("New Task", cut.Markup);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Bunit;
|
||||
using SufiChain.SufiBlazor.Components;
|
||||
using SufiChain.SufiBlazor.Components.Feedback;
|
||||
using Xunit;
|
||||
|
||||
namespace SufiChain.SufiBlazor.Tests.Components.Feedback;
|
||||
|
||||
public class SbProgressTests : BunitContext
|
||||
{
|
||||
private IRenderedComponent<SbProgress> RenderProgress(
|
||||
Action<ComponentParameterCollectionBuilder<SbProgress>>? configure = null)
|
||||
{
|
||||
return Render<SbProgress>(p =>
|
||||
{
|
||||
p.Add(x => x.Value, 50);
|
||||
configure?.Invoke(p);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersLinearStructureByDefault()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderProgress();
|
||||
|
||||
// Assert
|
||||
var progress = cut.Find(".sb-progress");
|
||||
Assert.NotNull(progress);
|
||||
Assert.Equal("progressbar", progress.GetAttribute("role"));
|
||||
Assert.NotNull(cut.Find(".sb-progress__track"));
|
||||
Assert.NotNull(cut.Find(".sb-progress__bar"));
|
||||
Assert.Contains("sb-progress--linear", progress.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersCircularStructure()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderProgress(p => p.Add(x => x.Type, SbProgressType.Circular));
|
||||
|
||||
// Assert
|
||||
var progress = cut.Find(".sb-progress");
|
||||
Assert.NotNull(progress);
|
||||
Assert.Equal("svg", progress.TagName.ToLowerInvariant());
|
||||
Assert.NotNull(cut.Find(".sb-progress__track-circle"));
|
||||
Assert.NotNull(cut.Find(".sb-progress__bar-circle"));
|
||||
Assert.Contains("sb-progress--circular", progress.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesValueToLinearBar()
|
||||
{
|
||||
// Arrange & Act - use Render directly to avoid Value added twice
|
||||
var cut = Render<SbProgress>(p => p.Add(x => x.Value, 75));
|
||||
|
||||
// Assert
|
||||
var bar = cut.Find(".sb-progress__bar");
|
||||
Assert.NotNull(bar);
|
||||
Assert.Contains("width: 75%", bar.GetAttribute("style"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClampsValueTo100()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = Render<SbProgress>(p => p.Add(x => x.Value, 150));
|
||||
|
||||
// Assert
|
||||
var bar = cut.Find(".sb-progress__bar");
|
||||
Assert.NotNull(bar);
|
||||
Assert.Contains("width: 100%", bar.GetAttribute("style"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClampsValueTo0()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = Render<SbProgress>(p => p.Add(x => x.Value, -10));
|
||||
|
||||
// Assert
|
||||
var bar = cut.Find(".sb-progress__bar");
|
||||
Assert.NotNull(bar);
|
||||
Assert.Contains("width: 0%", bar.GetAttribute("style"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetsAriaValuenowWhenNotIndeterminate()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = Render<SbProgress>(p => p.Add(x => x.Value, 60));
|
||||
|
||||
// Assert
|
||||
var progress = cut.Find(".sb-progress");
|
||||
Assert.Equal("60", progress.GetAttribute("aria-valuenow"));
|
||||
Assert.Equal("0", progress.GetAttribute("aria-valuemin"));
|
||||
Assert.Equal("100", progress.GetAttribute("aria-valuemax"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetsAriaValuenowNullWhenIndeterminate()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderProgress(p => p.Add(x => x.Indeterminate, true));
|
||||
|
||||
// Assert
|
||||
var progress = cut.Find(".sb-progress");
|
||||
Assert.Null(progress.GetAttribute("aria-valuenow"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesIndeterminateClass()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderProgress(p => p.Add(x => x.Indeterminate, true));
|
||||
|
||||
// Assert
|
||||
var progress = cut.Find(".sb-progress");
|
||||
Assert.Contains("sb-progress--indeterminate", progress.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersLabelWhenShowLabelTrue()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = Render<SbProgress>(p => p
|
||||
.Add(x => x.Value, 42)
|
||||
.Add(x => x.ShowLabel, true));
|
||||
|
||||
// Assert
|
||||
var label = cut.Find(".sb-progress__label");
|
||||
Assert.NotNull(label);
|
||||
Assert.Contains("42%", label.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotRenderLabelWhenShowLabelFalse()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderProgress();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(cut.FindAll(".sb-progress__label"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotRenderLabelWhenIndeterminate()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderProgress(p => p
|
||||
.Add(x => x.ShowLabel, true)
|
||||
.Add(x => x.Indeterminate, true));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(cut.FindAll(".sb-progress__label"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesPrimaryColorByDefault()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderProgress();
|
||||
|
||||
// Assert
|
||||
var progress = cut.Find(".sb-progress");
|
||||
Assert.Contains("sb-progress--primary", progress.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesSuccessColor()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderProgress(p => p.Add(x => x.Color, SbColor.Success));
|
||||
|
||||
// Assert
|
||||
var progress = cut.Find(".sb-progress");
|
||||
Assert.Contains("sb-progress--success", progress.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesMdSizeByDefault()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderProgress();
|
||||
|
||||
// Assert
|
||||
var progress = cut.Find(".sb-progress");
|
||||
Assert.Contains("sb-progress--md", progress.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesSmSize()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderProgress(p => p.Add(x => x.Size, SbSize.Sm));
|
||||
|
||||
// Assert
|
||||
var progress = cut.Find(".sb-progress");
|
||||
Assert.Contains("sb-progress--sm", progress.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesCustomClass()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderProgress(p => p.Add(x => x.Class, "my-progress"));
|
||||
|
||||
// Assert
|
||||
var progress = cut.Find(".sb-progress");
|
||||
Assert.Contains("my-progress", progress.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesInlineStyle()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderProgress(p => p.Add(x => x.Style, "height: 8px;"));
|
||||
|
||||
// Assert
|
||||
var progress = cut.Find(".sb-progress");
|
||||
Assert.Contains("height: 8px", progress.GetAttribute("style"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CircularRendersLabelWhenShowLabelTrue()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = Render<SbProgress>(p => p
|
||||
.Add(x => x.Type, SbProgressType.Circular)
|
||||
.Add(x => x.Value, 33)
|
||||
.Add(x => x.ShowLabel, true));
|
||||
|
||||
// Assert
|
||||
Assert.Contains("33%", cut.Markup);
|
||||
var labelText = cut.Find(".sb-progress__label-text");
|
||||
Assert.NotNull(labelText);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Bunit;
|
||||
using SufiChain.SufiBlazor.Components.Feedback;
|
||||
using Xunit;
|
||||
|
||||
namespace SufiChain.SufiBlazor.Tests.Components.Feedback;
|
||||
|
||||
public class SbSkeletonTests : BunitContext
|
||||
{
|
||||
private IRenderedComponent<SbSkeleton> RenderSkeleton(
|
||||
Action<ComponentParameterCollectionBuilder<SbSkeleton>>? configure = null)
|
||||
{
|
||||
return Render<SbSkeleton>(p => configure?.Invoke(p));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersSkeletonStructure()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderSkeleton();
|
||||
|
||||
// Assert
|
||||
var skeleton = cut.Find(".sb-skeleton");
|
||||
Assert.NotNull(skeleton);
|
||||
Assert.Equal("div", skeleton.TagName.ToLowerInvariant());
|
||||
Assert.Equal("true", skeleton.GetAttribute("aria-hidden"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesTextVariantByDefault()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderSkeleton();
|
||||
|
||||
// Assert
|
||||
var skeleton = cut.Find(".sb-skeleton");
|
||||
Assert.Contains("sb-skeleton--text", skeleton.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesCircularVariant()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderSkeleton(p => p.Add(x => x.Variant, SbSkeletonVariant.Circular));
|
||||
|
||||
// Assert
|
||||
var skeleton = cut.Find(".sb-skeleton");
|
||||
Assert.Contains("sb-skeleton--circular", skeleton.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesRectangularVariant()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderSkeleton(p => p.Add(x => x.Variant, SbSkeletonVariant.Rectangular));
|
||||
|
||||
// Assert
|
||||
var skeleton = cut.Find(".sb-skeleton");
|
||||
Assert.Contains("sb-skeleton--rectangular", skeleton.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesRoundedVariant()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderSkeleton(p => p.Add(x => x.Variant, SbSkeletonVariant.Rounded));
|
||||
|
||||
// Assert
|
||||
var skeleton = cut.Find(".sb-skeleton");
|
||||
Assert.Contains("sb-skeleton--rounded", skeleton.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesAnimatedClassByDefault()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderSkeleton();
|
||||
|
||||
// Assert
|
||||
var skeleton = cut.Find(".sb-skeleton");
|
||||
Assert.Contains("sb-skeleton--animated", skeleton.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotApplyAnimatedClassWhenAnimationFalse()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderSkeleton(p => p.Add(x => x.Animation, false));
|
||||
|
||||
// Assert
|
||||
var skeleton = cut.Find(".sb-skeleton");
|
||||
Assert.DoesNotContain("sb-skeleton--animated", skeleton.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesWidthStyle()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderSkeleton(p => p.Add(x => x.Width, "200px"));
|
||||
|
||||
// Assert
|
||||
var skeleton = cut.Find(".sb-skeleton");
|
||||
Assert.Contains("width: 200px", skeleton.GetAttribute("style"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesHeightStyle()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderSkeleton(p => p.Add(x => x.Height, "24px"));
|
||||
|
||||
// Assert
|
||||
var skeleton = cut.Find(".sb-skeleton");
|
||||
Assert.Contains("height: 24px", skeleton.GetAttribute("style"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesBorderRadiusStyle()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderSkeleton(p => p.Add(x => x.BorderRadius, "8px"));
|
||||
|
||||
// Assert
|
||||
var skeleton = cut.Find(".sb-skeleton");
|
||||
Assert.Contains("border-radius: 8px", skeleton.GetAttribute("style"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesMultipleDimensionStyles()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderSkeleton(p => p
|
||||
.Add(x => x.Width, "100%")
|
||||
.Add(x => x.Height, "200px")
|
||||
.Add(x => x.BorderRadius, "4px"));
|
||||
|
||||
// Assert
|
||||
var style = cut.Find(".sb-skeleton").GetAttribute("style");
|
||||
Assert.Contains("width: 100%", style);
|
||||
Assert.Contains("height: 200px", style);
|
||||
Assert.Contains("border-radius: 4px", style);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesCustomClass()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderSkeleton(p => p.Add(x => x.Class, "my-skeleton"));
|
||||
|
||||
// Assert
|
||||
var skeleton = cut.Find(".sb-skeleton");
|
||||
Assert.Contains("my-skeleton", skeleton.ClassList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Bunit;
|
||||
using SufiChain.SufiBlazor.Components.Feedback;
|
||||
using SufiChain.SufiBlazor.Localization;
|
||||
using Xunit;
|
||||
|
||||
namespace SufiChain.SufiBlazor.Tests.Components.Feedback;
|
||||
|
||||
/// <summary>
|
||||
/// Stub localizer that returns the key as the value (for testing).
|
||||
/// </summary>
|
||||
file class StubStringLocalizer : IStringLocalizer<SufiBlazorResource>
|
||||
{
|
||||
public LocalizedString this[string name] => new(name, name);
|
||||
public LocalizedString this[string name, params object[] arguments] => new(name, string.Format(CultureInfo.InvariantCulture, name, arguments));
|
||||
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures) => Array.Empty<LocalizedString>();
|
||||
}
|
||||
|
||||
public class SbToastTests : BunitContext
|
||||
{
|
||||
public SbToastTests()
|
||||
{
|
||||
Services.AddSingleton<IStringLocalizer<SufiBlazorResource>>(new StubStringLocalizer());
|
||||
}
|
||||
|
||||
private IRenderedComponent<SbToast> RenderToast(
|
||||
Action<ComponentParameterCollectionBuilder<SbToast>>? configure = null)
|
||||
{
|
||||
return Render<SbToast>(p =>
|
||||
{
|
||||
p.Add(x => x.Message, "Toast message");
|
||||
configure?.Invoke(p);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersToastStructure()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderToast();
|
||||
|
||||
// Assert
|
||||
var toast = cut.Find(".sb-toast");
|
||||
Assert.NotNull(toast);
|
||||
Assert.Equal("alert", toast.GetAttribute("role"));
|
||||
Assert.Equal("polite", toast.GetAttribute("aria-live"));
|
||||
Assert.NotNull(cut.Find(".sb-toast__icon"));
|
||||
Assert.NotNull(cut.Find(".sb-toast__content"));
|
||||
Assert.NotNull(cut.Find(".sb-toast__message"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersMessage()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderToast();
|
||||
|
||||
// Assert
|
||||
Assert.Contains("Toast message", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersChildContentOverMessage()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = Render<SbToast>(p => p
|
||||
.Add(x => x.Message, "Fallback")
|
||||
.AddChildContent("<span class=\"custom-msg\">Custom content</span>"));
|
||||
|
||||
// Assert - ChildContent takes precedence
|
||||
Assert.Contains("Custom content", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersTitleWhenProvided()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderToast(p => p.Add(x => x.Title, "Success"));
|
||||
|
||||
// Assert
|
||||
var title = cut.Find(".sb-toast__title");
|
||||
Assert.NotNull(title);
|
||||
Assert.Contains("Success", title.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotRenderTitleWhenEmpty()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderToast();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(cut.FindAll(".sb-toast__title"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesInfoSeverityByDefault()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderToast();
|
||||
|
||||
// Assert
|
||||
var toast = cut.Find(".sb-toast");
|
||||
Assert.Contains("sb-toast--info", toast.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesSuccessSeverity()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderToast(p => p.Add(x => x.Severity, SbToastSeverity.Success));
|
||||
|
||||
// Assert
|
||||
var toast = cut.Find(".sb-toast");
|
||||
Assert.Contains("sb-toast--success", toast.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesWarningSeverity()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderToast(p => p.Add(x => x.Severity, SbToastSeverity.Warning));
|
||||
|
||||
// Assert
|
||||
var toast = cut.Find(".sb-toast");
|
||||
Assert.Contains("sb-toast--warning", toast.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesErrorSeverity()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderToast(p => p.Add(x => x.Severity, SbToastSeverity.Error));
|
||||
|
||||
// Assert
|
||||
var toast = cut.Find(".sb-toast");
|
||||
Assert.Contains("sb-toast--error", toast.ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersCloseButtonByDefault()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderToast();
|
||||
|
||||
// Assert
|
||||
var closeBtn = cut.Find(".sb-toast__close");
|
||||
Assert.NotNull(closeBtn);
|
||||
Assert.Equal("Close", closeBtn.GetAttribute("aria-label"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotRenderCloseButtonWhenShowCloseButtonFalse()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderToast(p => p.Add(x => x.ShowCloseButton, false));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(cut.FindAll(".sb-toast__close"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokesOnCloseWhenCloseButtonClicked()
|
||||
{
|
||||
// Arrange
|
||||
var closed = false;
|
||||
var cut = RenderToast(p => p.Add(x => x.OnClose, EventCallback.Factory.Create(this, () => closed = true)));
|
||||
|
||||
var closeBtn = cut.Find(".sb-toast__close");
|
||||
|
||||
// Act
|
||||
await cut.InvokeAsync(() => closeBtn!.Click());
|
||||
|
||||
cut.WaitForState(() => closed);
|
||||
|
||||
// Assert
|
||||
Assert.True(closed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesCustomClass()
|
||||
{
|
||||
// Arrange & Act
|
||||
var cut = RenderToast(p => p.Add(x => x.Class, "my-toast"));
|
||||
|
||||
// Assert
|
||||
var toast = cut.Find(".sb-toast");
|
||||
Assert.Contains("my-toast", toast.ClassList);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user