Files
BootstrapBlazor/test/UnitTest/Components/InputTest.cs
Argo Zhang fd396e36ea feat(BootstrapInputGroupLabel): add IsGroupLabel parameter (#7257)
* feat(Table): use dropdown-column selector

* feat: 增加 IsGroupLabel 参数

* style: 增加样式

* test: 增加单元测试
2025-12-05 14:29:04 +08:00

452 lines
14 KiB
C#

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
namespace UnitTest.Components;
public class InputTest : BootstrapBlazorTestBase
{
[Fact]
public void Color_Ok()
{
var cut = Context.Render<BootstrapInput<string>>(builder =>
{
builder.Add(a => a.Color, Color.None);
builder.Add(a => a.IsDisabled, false);
});
Assert.DoesNotContain("border-", cut.Markup);
cut.Render(builder => builder.Add(a => a.Color, Color.Primary));
Assert.Contains("border-primary", cut.Markup);
}
[Fact]
public void PlaceHolder_Ok()
{
var ph = "placeholder_test";
var cut = Context.Render<BootstrapInput<string>>(builder => builder.Add(a => a.PlaceHolder, ph));
Assert.Contains($"placeholder=\"{ph}\"", cut.Markup);
}
[Fact]
public void Value_Ok()
{
var model = new Foo() { Name = "Test" };
var valueChanged = false;
var cut = Context.Render<BootstrapInput<string>>(builder =>
{
builder.Add(a => a.Value, model.Name);
builder.Add(a => a.ValueChanged, v => model.Name = v);
builder.Add(a => a.ValueExpression, model.GenerateValueExpression());
builder.Add(a => a.OnValueChanged, v => { valueChanged = true; return Task.CompletedTask; });
});
cut.Find("input").Change("Test1");
Assert.Contains(model.Name, "Test1");
Assert.True(valueChanged);
}
[Fact]
public void IsAutoFocus_Ok()
{
var cut = Context.Render<BootstrapInput<string>>(builder => builder.Add(a => a.IsAutoFocus, true));
}
[Fact]
public void Type_Ok()
{
var cut = Context.Render<BootstrapInput<string>>(builder => builder.AddUnmatched("type", "number"));
Assert.Contains($"type=\"number\"", cut.Markup);
cut = Context.Render<BootstrapInput<string>>();
Assert.Contains($"type=\"text\"", cut.Markup);
}
[Fact]
public void Readonly_Ok()
{
var cut = Context.Render<BootstrapInput<string>>(builder => builder.Add(a => a.Readonly, true));
cut.Contains("readonly=\"true\"");
}
[Fact]
public void Clearable_Ok()
{
var cut = Context.Render<BootstrapInput<string>>(builder => builder.Add(a => a.IsClearable, false));
cut.DoesNotContain("bb-clearable-input");
cut.Render(pb => pb.Add(a => a.IsClearable, true));
cut.Contains("bb-clearable-input");
cut.Contains("form-control-clear-icon");
cut.Render(pb => pb.Add(a => a.Readonly, true));
cut.DoesNotContain("form-control-clear-icon");
cut.Render(pb => pb.Add(a => a.Readonly, false));
cut.Render(pb => pb.Add(a => a.IsDisabled, true));
cut.DoesNotContain("form-control-clear-icon");
}
[Fact]
public async Task OnClear_Ok()
{
var clicked = false;
var cut = Context.Render<BootstrapInput<string>>(builder =>
{
builder.Add(a => a.IsClearable, true);
builder.Add(a => a.OnClear, v =>
{
clicked = true;
return Task.CompletedTask;
});
});
var icon = cut.Find(".form-control-clear-icon");
await cut.InvokeAsync(() => icon.Click());
Assert.True(clicked);
}
[Fact]
public async Task OnInput_Ok()
{
var foo = new Foo() { Name = "Test" };
var cut = Context.Render<BootstrapInput<string>>(builder =>
{
builder.Add(a => a.Value, foo.Name);
builder.Add(a => a.UseInputEvent, true);
builder.Add(a => a.ValueChanged, EventCallback.Factory.Create<string?>(this, v =>
{
foo.Name = v;
}));
});
cut.Contains("blazor:oninput");
// 输入字符
var input = cut.Find("input");
await cut.InvokeAsync(() =>
{
input.Input("1");
});
Assert.Equal("1", foo.Name);
}
[Fact]
public void Password_Ok()
{
var cut = Context.Render<BootstrapPassword>();
cut.Contains("type=\"password\"");
}
[Fact]
public void IsTrim_Ok()
{
var val = " test ";
var cut = Context.Render<BootstrapInput<string>>(builder =>
{
builder.Add(a => a.IsTrim, true);
builder.Add(a => a.Value, "");
});
Assert.Equal("", cut.Instance.Value);
var input = cut.Find("input");
cut.InvokeAsync(() => input.Change(val));
cut.WaitForAssertion(() => Assert.Equal(val.Trim(), cut.Instance.Value));
cut.Render(builder =>
{
builder.Add(a => a.IsTrim, false);
builder.Add(a => a.Value, "");
});
cut.WaitForAssertion(() => Assert.Equal("", cut.Instance.Value));
input = cut.Find("input");
cut.InvokeAsync(() => input.Change(val));
cut.WaitForAssertion(() => Assert.Equal(val, cut.Instance.Value));
}
[Fact]
public void Formatter_Ok()
{
var cut = Context.Render<BootstrapInput<DateTime>>(builder =>
{
builder.Add(a => a.FormatString, "yyyy-MM-dd");
builder.Add(a => a.Value, DateTime.Now);
});
Assert.Contains($"value=\"{DateTime.Now:yyyy-MM-dd}\"", cut.Markup);
cut.Render(builder =>
{
builder.Add(a => a.FormatString, null);
builder.Add(a => a.Formatter, dt => dt.ToString("HH:mm"));
builder.Add(a => a.Value, DateTime.Now);
});
cut.WaitForAssertion(() => Assert.Contains($"value=\"{DateTime.Now:HH:mm}\"", cut.Markup));
}
[Fact]
public async Task EnterCallback_Ok()
{
var val = "";
var cut = Context.Render<BootstrapInput<string>>(builder =>
{
builder.Add(a => a.OnEnterAsync, v => { val = v; return Task.CompletedTask; });
builder.Add(a => a.Value, "test");
});
await cut.Instance.EnterCallback();
Assert.Equal("test", val);
}
[Fact]
public async Task EscCallback_Ok()
{
var val = "";
var cut = Context.Render<BootstrapInput<string>>(builder =>
{
builder.Add(a => a.OnEscAsync, v => { val = v; return Task.CompletedTask; });
builder.Add(a => a.Value, "test");
});
await cut.Instance.EscCallback();
Assert.Equal("test", val);
}
[Fact]
public void IsSelectAllTextOnFocus_Ok()
{
var cut = Context.Render<BootstrapInput<string>>(builder =>
{
builder.Add(a => a.IsSelectAllTextOnFocus, true);
});
}
[Fact]
public async Task IsSelectAllTextOnEnter_Ok()
{
var cut = Context.Render<BootstrapInput<string>>(builder =>
{
builder.Add(a => a.IsSelectAllTextOnEnter, true);
});
await cut.Instance.SelectAllTextAsync();
}
[Fact]
public void FloatingLabel_Ok()
{
var cut = Context.Render<FloatingLabel<string>>();
cut.Contains("<div class=\"form-floating\">");
cut.Render(pb =>
{
pb.Add(a => a.IsGroupBox, true);
});
cut.Contains("<div class=\"form-floating is-group\">");
// PlaceHolder
var foo = new Foo() { Name = "Foo" };
cut.Render(pb =>
{
pb.Add(a => a.Value, "test");
});
var input = cut.Find("input");
Assert.Null(input.GetAttribute("placeholder"));
cut.Render(pb =>
{
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(foo, "Name", typeof(string)));
});
input = cut.Find("input");
Assert.Equal("姓名", input.GetAttribute("placeholder"));
// PlaceHolder
cut.Render(pb =>
{
pb.Add(a => a.PlaceHolder, "fl-pl");
});
input = cut.Find("input");
Assert.Equal("fl-pl", input.GetAttribute("placeholder"));
}
[Fact]
public void GroupLabel_Ok()
{
var cut = Context.Render<BootstrapInputGroupLabel>(builder =>
{
builder.Add(s => s.DisplayText, "DisplayText");
});
Assert.Contains("DisplayText", cut.Markup);
cut.Render(pb =>
{
pb.Add(a => a.ChildContent, builder => builder.AddContent(0, "test-child-content"));
});
cut.Contains("test-child-content");
cut.DoesNotContain("DisplayText");
}
[Fact]
public void IsGroupLabel_Ok()
{
var cut = Context.Render<BootstrapInputGroupLabel>();
cut.DoesNotContain("input-group-text");
cut.Render(pb =>
{
pb.Add(a => a.IsGroupLabel, true);
});
cut.Contains("input-group-text");
}
[Fact]
public void ShowRequiredMark_Ok()
{
var cut = Context.Render<BootstrapInputGroupLabel>(builder =>
{
builder.Add(s => s.DisplayText, "DisplayText");
builder.Add(s => s.ShowRequiredMark, true);
});
cut.MarkupMatches("<label class=\"form-label\" required=\"true\">DisplayText</label>");
}
[Fact]
public void GroupIcon_Ok()
{
var cut = Context.Render<BootstrapInputGroupIcon>(builder =>
{
builder.Add(s => s.Icon, "fa-solid fa-user");
});
var ele = cut.Find(".fa-user");
Assert.NotNull(ele);
}
[Fact]
public void InputGroup_Width()
{
var cut = Context.Render<BootstrapInputGroup>(builder =>
{
builder.Add(s => s.ChildContent, new RenderFragment(builder =>
{
builder.OpenComponent<BootstrapInputGroupLabel>(0);
builder.AddAttribute(1, nameof(BootstrapInputGroupLabel.DisplayText), "BootstrapInputGroup");
builder.AddAttribute(2, nameof(BootstrapInputGroupLabel.ShowRequiredMark), true);
builder.AddAttribute(2, nameof(BootstrapInputGroupLabel.Width), 120);
builder.CloseComponent();
}));
});
cut.MarkupMatches("<div class=\"input-group\"><div class=\"input-group-text\" style=\"--bb-input-group-label-width: 120px;\" required=\"true\">BootstrapInputGroup</div></div>");
}
[Fact]
public void InputGroup_ChildContent()
{
var cut = Context.Render<BootstrapInputGroup>(builder =>
{
builder.Add(s => s.ChildContent, new RenderFragment(builder =>
{
builder.OpenComponent<BootstrapInputGroupLabel>(0);
builder.AddAttribute(1, nameof(BootstrapInputGroupLabel.ChildContent), new RenderFragment(builder => builder.AddContent(0, "child-content")));
builder.CloseComponent();
}));
});
cut.Contains("child-content");
}
[Theory]
[InlineData(Alignment.Center, "center")]
[InlineData(Alignment.Right, "end")]
public void InputGroup_Alignment(Alignment alignment, string expected)
{
var cut = Context.Render<BootstrapInputGroup>(builder =>
{
builder.Add(s => s.ChildContent, new RenderFragment(builder =>
{
builder.OpenComponent<BootstrapInputGroupLabel>(0);
builder.AddAttribute(1, nameof(BootstrapInputGroupLabel.DisplayText), "BootstrapInputGroup");
builder.AddAttribute(2, nameof(BootstrapInputGroupLabel.Alignment), alignment);
builder.CloseComponent();
}));
});
cut.Contains($"justify-content-{expected}");
}
[Fact]
public void Focus_Ok()
{
var cut = Context.Render<BootstrapBlazorRoot>(pb =>
{
pb.AddChildContent<Modal>(pb =>
{
pb.AddChildContent<BootstrapInput<string>>(pb =>
{
pb.Add(a => a.IsAutoFocus, true);
});
});
});
}
[Fact]
public async Task AutoSetDefaultWhenNull_Ok()
{
var cut = Context.Render<BootstrapInput<int>>(builder =>
{
builder.Add(a => a.Value, 123);
builder.Add(a => a.AutoSetDefaultWhenNull, true);
});
await cut.InvokeAsync(() =>
{
var input = cut.Find("input");
input.Change("");
});
Assert.Equal(0, cut.Instance.Value);
}
[Fact]
public void OnValueChanged_Ok()
{
var val = "";
var foo = new Foo() { Name = "Test" };
var cut = Context.Render<BootstrapInput<string>>(builder =>
{
builder.Add(a => a.Value, foo.Name);
builder.Add(a => a.ValueChanged, EventCallback.Factory.Create<string?>(this, v =>
{
foo.Name = v;
}));
builder.Add(a => a.OnValueChanged, v =>
{
val = $"{foo.Name}-{v}";
return Task.CompletedTask;
});
});
cut.InvokeAsync(() =>
{
var input = cut.Find("input");
input.Change("Test_Test");
// 保证 ValueChanged 先触发,再触发 OnValueChanged
Assert.Equal("Test_Test", foo.Name);
Assert.Equal("Test_Test-Test_Test", val);
});
}
[Fact]
public async Task OnBlurAsync_Ok()
{
var blur = false;
var cut = Context.Render<BootstrapInput<string>>(builder =>
{
builder.Add(a => a.OnBlurAsync, v =>
{
blur = true;
return Task.CompletedTask;
});
});
var input = cut.Find("input");
await cut.InvokeAsync(() => { input.Blur(); });
Assert.True(blur);
}
}