Files
2026-05-18 15:53:59 +03:30

338 lines
9.4 KiB
C#

using Microsoft.AspNetCore.Components;
using Bunit;
using SufiChain.SufiBlazor.Components.Forms;
using Xunit;
namespace SufiChain.SufiBlazor.Tests.Components.Forms;
public class SbNumberFieldTests : BunitContext
{
private IRenderedComponent<SbNumberField<int>> RenderNumberField(
Action<ComponentParameterCollectionBuilder<SbNumberField<int>>>? configure = null)
{
return Render<SbNumberField<int>>(p =>
{
configure?.Invoke(p);
});
}
[Fact]
public void RendersNumberFieldStructure()
{
// Arrange & Act
var cut = RenderNumberField();
// Assert
var wrapper = cut.Find(".sb-number-field");
Assert.NotNull(wrapper);
Assert.NotNull(cut.Find(".sb-number-field__input-group"));
var input = cut.Find("input.sb-number-field__input");
Assert.NotNull(input);
Assert.Equal("number", input.GetAttribute("type"));
}
[Fact]
public void RendersLabelWhenProvided()
{
// Arrange & Act
var cut = RenderNumberField(p => p
.Add(x => x.Label, "Quantity")
.Add(x => x.Id, "qty"));
// Assert
var label = cut.Find(".sb-number-field__label");
Assert.NotNull(label);
Assert.Contains("Quantity", label.TextContent);
}
[Fact]
public void DoesNotRenderLabelWhenEmpty()
{
// Arrange & Act
var cut = RenderNumberField();
// Assert
Assert.Empty(cut.FindAll(".sb-number-field__label"));
}
[Fact]
public void RendersRequiredAsteriskWhenRequired()
{
// Arrange & Act
var cut = RenderNumberField(p => p
.Add(x => x.Label, "Amount")
.Add(x => x.Required, true));
// Assert
var required = cut.Find(".sb-number-field__required");
Assert.NotNull(required);
Assert.Contains("*", required.TextContent);
}
[Fact]
public void AssociatesLabelWithInputId()
{
// Arrange & Act
var cut = RenderNumberField(p => p
.Add(x => x.Label, "Value")
.Add(x => x.Id, "value-input"));
// Assert
var label = cut.Find("label.sb-number-field__label");
Assert.Equal("value-input", label.GetAttribute("for"));
}
[Fact]
public void RendersHelperTextWhenProvided()
{
// Arrange & Act
var cut = RenderNumberField(p => p.Add(x => x.HelperText, "Enter a number between 1 and 100"));
// Assert
var helper = cut.Find(".sb-number-field__helper");
Assert.NotNull(helper);
Assert.Contains("Enter a number between 1 and 100", helper.TextContent);
}
[Fact]
public void DisplaysValueInInput()
{
// Arrange & Act
var cut = RenderNumberField(p => p.Add(x => x.Value, 42));
// Assert
var input = cut.Find("input");
Assert.Equal("42", input.GetAttribute("value"));
}
[Fact]
public void AppliesMinMaxStepAttributes()
{
// Arrange & Act
var cut = RenderNumberField(p => p
.Add(x => x.Value, 5)
.Add(x => x.Min, 0)
.Add(x => x.Max, 10)
.Add(x => x.Step, 2));
// Assert
var input = cut.Find("input");
Assert.Equal("0", input.GetAttribute("min"));
Assert.Equal("10", input.GetAttribute("max"));
Assert.Equal("2", input.GetAttribute("step"));
}
[Fact]
public void AppliesPlaceholder()
{
// Arrange & Act
var cut = RenderNumberField(p => p.Add(x => x.Placeholder, "0.00"));
// Assert
var input = cut.Find("input");
Assert.Equal("0.00", input.GetAttribute("placeholder"));
}
[Fact]
public void InputIsDisabledWhenDisabledTrue()
{
// Arrange & Act
var cut = RenderNumberField(p => p.Add(x => x.Disabled, true));
// Assert
var input = cut.Find("input");
Assert.NotNull(input.GetAttribute("disabled"));
}
[Fact]
public void InputIsReadOnlyWhenReadOnlyTrue()
{
// Arrange & Act
var cut = RenderNumberField(p => p.Add(x => x.ReadOnly, true));
// Assert
var input = cut.Find("input");
Assert.NotNull(input.GetAttribute("readonly"));
}
[Fact]
public void AppliesDisabledClassWhenDisabled()
{
// Arrange & Act
var cut = RenderNumberField(p => p.Add(x => x.Disabled, true));
// Assert
var wrapper = cut.Find(".sb-number-field");
Assert.Contains("sb-number-field--disabled", wrapper.ClassList);
}
[Fact]
public void AppliesClassParameter()
{
// Arrange & Act
var cut = RenderNumberField(p => p.Add(x => x.Class, "custom-field"));
// Assert
var wrapper = cut.Find(".sb-number-field");
Assert.Contains("custom-field", wrapper.ClassList);
}
[Fact]
public void AppliesStyleParameter()
{
// Arrange & Act
var cut = RenderNumberField(p => p.Add(x => x.Style, "max-width: 200px;"));
// Assert
var wrapper = cut.Find(".sb-number-field");
Assert.Contains("max-width: 200px", wrapper.GetAttribute("style"));
}
[Fact]
public void RendersSpinButtonsByDefault()
{
// Arrange & Act
var cut = RenderNumberField();
// Assert
var spinners = cut.FindAll(".sb-number-field__spinner");
Assert.Equal(2, spinners.Count);
}
[Fact]
public void DoesNotRenderSpinButtonsWhenShowSpinButtonsFalse()
{
// Arrange & Act
var cut = RenderNumberField(p => p.Add(x => x.ShowSpinButtons, false));
// Assert
Assert.Empty(cut.FindAll(".sb-number-field__spinners"));
}
[Fact]
public async Task InvokesValueChangedWhenInputChanged()
{
// Arrange
int? received = null;
var cut = RenderNumberField(p => p
.Add(x => x.Value, 10)
.Add(x => x.ValueChanged, EventCallback.Factory.Create<int>(this, v => received = v)));
// Act
var input = cut.Find("input");
await cut.InvokeAsync(() => input!.Change("25"));
// Assert
Assert.NotNull(received);
Assert.Equal(25, received);
}
[Fact]
public async Task InvokesValueChangedWhenIncrementClicked()
{
// Arrange
int? received = null;
var cut = RenderNumberField(p => p
.Add(x => x.Value, 5)
.Add(x => x.ValueChanged, EventCallback.Factory.Create<int>(this, v => received = v)));
// Act
var incrementBtn = cut.FindAll(".sb-number-field__spinner")[0];
await cut.InvokeAsync(() => incrementBtn!.Click());
// Assert
Assert.NotNull(received);
Assert.Equal(6, received);
}
[Fact]
public async Task InvokesValueChangedWhenDecrementClicked()
{
// Arrange
int? received = null;
var cut = RenderNumberField(p => p
.Add(x => x.Value, 5)
.Add(x => x.ValueChanged, EventCallback.Factory.Create<int>(this, v => received = v)));
// Act
var decrementBtn = cut.FindAll(".sb-number-field__spinner")[1];
await cut.InvokeAsync(() => decrementBtn!.Click());
// Assert
Assert.NotNull(received);
Assert.Equal(4, received);
}
[Fact]
public void IncrementButtonDisabledWhenAtMax()
{
// Arrange & Act
var cut = RenderNumberField(p => p
.Add(x => x.Value, 10)
.Add(x => x.Max, 10));
// Assert
var spinnerButtons = cut.FindAll(".sb-number-field__spinner");
var incrementBtn = spinnerButtons[0];
Assert.NotNull(incrementBtn.GetAttribute("disabled"));
}
[Fact]
public void DecrementButtonDisabledWhenAtMin()
{
// Arrange & Act
var cut = RenderNumberField(p => p
.Add(x => x.Value, 0)
.Add(x => x.Min, 0));
// Assert
var spinnerButtons = cut.FindAll(".sb-number-field__spinner");
var decrementBtn = spinnerButtons[1];
Assert.NotNull(decrementBtn.GetAttribute("disabled"));
}
[Fact]
public void UsesCustomIncreaseDecreaseAriaLabels()
{
// Arrange & Act
var cut = RenderNumberField(p => p
.Add(x => x.IncreaseAriaLabel, "Add one")
.Add(x => x.DecreaseAriaLabel, "Subtract one"));
// Assert
var spinners = cut.FindAll(".sb-number-field__spinner");
Assert.Equal("Add one", spinners[0].GetAttribute("aria-label"));
Assert.Equal("Subtract one", spinners[1].GetAttribute("aria-label"));
}
[Fact]
public void WorksWithDecimalType()
{
// Arrange & Act
var cut = Render<SbNumberField<decimal>>(p => p
.Add(x => x.Value, 3.5m)
.Add(x => x.Step, 0.5m));
// Assert
var input = cut.Find("input");
Assert.Equal("3.5", input.GetAttribute("value"));
Assert.Equal("0.5", input.GetAttribute("step"));
}
[Fact]
public void AppliesAdditionalAttributes()
{
// Arrange & Act
var cut = RenderNumberField(p => p.Add(x => x.AdditionalAttributes, new Dictionary<string, object>
{
{ "data-testid", "number-input" },
{ "aria-label", "Quantity" }
}));
// Assert
var input = cut.Find("input");
Assert.Equal("number-input", input.GetAttribute("data-testid"));
Assert.Equal("Quantity", input.GetAttribute("aria-label"));
}
}