From 191023fb5e99fe4647b79850ca2b9e1faed400f5 Mon Sep 17 00:00:00 2001 From: Argo Date: Wed, 2 Mar 2022 06:20:44 +0000 Subject: [PATCH] =?UTF-8?q?!2475=20test(#I4VXYM):=20add=20unit=20test=20fo?= =?UTF-8?q?r=20BootstrapInputNumber=20*=20test:=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=BC=82=E5=B8=B8=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=20*=20test:=20=E5=A2=9E=E5=8A=A0=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E4=BF=A1=E6=81=AF=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=20*=20test:=20=E5=A2=9E=E5=8A=A0=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=20*=20re?= =?UTF-8?q?vert:=20=E6=81=A2=E5=A4=8D=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=20*=20test:=20=E5=A2=9E=E5=8A=A0=E6=95=B0=E6=8D=AE=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=20*=20test:=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20ShowButtons=20=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=20*=20test:=20=E5=A2=9E=E5=8A=A0=20Blur=20=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95=20*=20doc:=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E6=A1=A3=20*=20Merge=20branch=20'main'=20into=20test-?= =?UTF-8?q?input=20*=20test:=20=E5=A2=9E=E5=8A=A0=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=20*=20te?= =?UTF-8?q?st:=20=E5=A2=9E=E5=8A=A0=20int=20=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=20*=20test:=20=E5=A2=9E=E5=8A=A0=20double=20=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95=20*=20test:=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=20*=20refactor:=20?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=E9=9D=99=E6=80=81=E6=9E=84=E9=80=A0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20*=20doc:=20=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Samples/Popovers.razor.cs | 62 ++++--- .../InputNumber/BootstrapInputNumber.razor.cs | 38 ++-- .../ValidateForm/ValidateForm.razor.cs | 12 +- test/UnitTest/Components/InputNumberTest.cs | 167 ++++++++++++++++++ 4 files changed, 232 insertions(+), 47 deletions(-) create mode 100644 test/UnitTest/Components/InputNumberTest.cs diff --git a/src/BootstrapBlazor.Shared/Samples/Popovers.razor.cs b/src/BootstrapBlazor.Shared/Samples/Popovers.razor.cs index d9d2ba090..9d4066a9c 100644 --- a/src/BootstrapBlazor.Shared/Samples/Popovers.razor.cs +++ b/src/BootstrapBlazor.Shared/Samples/Popovers.razor.cs @@ -23,34 +23,38 @@ public sealed partial class Popovers /// private static IEnumerable GetAttributes() => new AttributeItem[] { - // TODO: 移动到数据库中 - new AttributeItem() { - Name = "Cotent", - Description = "Popover 弹窗内容", - Type = "string", - ValueList = "", - DefaultValue = "Popover" - }, - new AttributeItem() { - Name = "IsHtml", - Description = "内容中是否包含 Html 代码", - Type = "boolean", - ValueList = "", - DefaultValue = "false" - }, - new AttributeItem() { - Name = "Placement", - Description = "位置", - Type = "Placement", - ValueList = "Auto / Top / Left / Bottom / Right", - DefaultValue = "Auto" - }, - new AttributeItem() { - Name = "Title", - Description = "Popover 弹窗标题", - Type = "string", - ValueList = "", - DefaultValue = "Popover" - }, + // TODO: 移动到数据库中 + new AttributeItem() + { + Name = "Cotent", + Description = "Popover 弹窗内容", + Type = "string", + ValueList = "", + DefaultValue = "Popover" + }, + new AttributeItem() + { + Name = "IsHtml", + Description = "内容中是否包含 Html 代码", + Type = "boolean", + ValueList = "", + DefaultValue = "false" + }, + new AttributeItem() + { + Name = "Placement", + Description = "位置", + Type = "Placement", + ValueList = "Auto / Top / Left / Bottom / Right", + DefaultValue = "Auto" + }, + new AttributeItem() + { + Name = "Title", + Description = "Popover 弹窗标题", + Type = "string", + ValueList = "", + DefaultValue = "Popover" + } }; } diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index df1ab9892..107835972 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -74,7 +74,12 @@ public partial class BootstrapInputNumber [NotNull] private IStringLocalizer>? Localizer { get; set; } - static BootstrapInputNumber() + /// + /// SetParametersAsync 方法 + /// + /// + /// + public override Task SetParametersAsync(ParameterView parameters) { // Unwrap Nullable, because InputBase already deals with the Nullable aspect // of it for us. We will only get asked to parse the T for nonempty inputs. @@ -83,6 +88,8 @@ public partial class BootstrapInputNumber { throw new InvalidOperationException($"The type '{targetType}' is not a supported numeric type."); } + + return base.SetParametersAsync(parameters); } /// @@ -120,17 +127,24 @@ public partial class BootstrapInputNumber ? Formatter.Invoke(value) : (!string.IsNullOrEmpty(FormatString) && value != null ? Utility.Format(value, FormatString) - : value switch - { - null => null, - int @int => BindConverter.FormatValue(@int, CultureInfo.InvariantCulture), - long @long => BindConverter.FormatValue(@long, CultureInfo.InvariantCulture), - short @short => BindConverter.FormatValue(@short, CultureInfo.InvariantCulture), - float @float => BindConverter.FormatValue(@float, CultureInfo.InvariantCulture), - double @double => BindConverter.FormatValue(@double, CultureInfo.InvariantCulture), - decimal @decimal => BindConverter.FormatValue(@decimal, CultureInfo.InvariantCulture), - _ => throw new InvalidOperationException($"Unsupported type {value!.GetType()}"), - }); + : InternalFormat(value)); + + /// + /// + /// + /// + /// + /// + protected virtual string? InternalFormat(TValue value) => value switch + { + int @int => BindConverter.FormatValue(@int, CultureInfo.InvariantCulture), + long @long => BindConverter.FormatValue(@long, CultureInfo.InvariantCulture), + short @short => BindConverter.FormatValue(@short, CultureInfo.InvariantCulture), + float @float => BindConverter.FormatValue(@float, CultureInfo.InvariantCulture), + double @double => BindConverter.FormatValue(@double, CultureInfo.InvariantCulture), + decimal @decimal => BindConverter.FormatValue(@decimal, CultureInfo.InvariantCulture), + _ => throw new InvalidOperationException($"Unsupported type {value!.GetType()}"), + }; private void SetStep() { diff --git a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs index 1e60c0944..600d00432 100644 --- a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs +++ b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs @@ -130,9 +130,9 @@ public partial class ValidateForm : IAsyncDisposable if (validator != null) { var results = new List - { - new ValidationResult(errorMessage, new string[] { fieldName }) - }; + { + new ValidationResult(errorMessage, new string[] { fieldName }) + }; validator.ToggleMessage(results, true); } } @@ -150,9 +150,9 @@ public partial class ValidateForm : IAsyncDisposable if (validator != null) { var results = new List - { - new ValidationResult(errorMessage, new string[] { fieldName }) - }; + { + new ValidationResult(errorMessage, new string[] { fieldName }) + }; validator.ToggleMessage(results, true); } } diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs new file mode 100644 index 000000000..9b33ff56d --- /dev/null +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -0,0 +1,167 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using BootstrapBlazor.Shared; +using System.ComponentModel.DataAnnotations; + +namespace UnitTest.Components; + +public class InputNumberTest : BootstrapBlazorTestBase +{ + [Fact] + public void OnBlur_Ok() + { + var cut = Context.RenderComponent>(pb => + { + pb.Add(a => a.Min, "0"); + pb.Add(a => a.Max, "10"); + pb.Add(a => a.Step, "2"); + }); + cut.Contains("min=\"0\""); + cut.Contains("max=\"10\""); + cut.Contains("step=\"2\""); + + var input = cut.Find("input"); + cut.InvokeAsync(() => input.Blur()); + } + + [Fact] + public void ValidateForm() + { + var foo = new Cat() { Count = 20 }; + var cut = Context.RenderComponent(pb => + { + pb.Add(a => a.Model, foo); + pb.AddChildContent>(pb => + { + pb.Add(a => a.Value, foo.Count); + pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(foo, nameof(Cat.Count), typeof(int))); + }); + }); + cut.Contains("class=\"form-label\""); + + var input = cut.Find("input"); + cut.InvokeAsync(() => input.Change("")); + + var form = cut.Find("form"); + cut.InvokeAsync(() => form.Submit()); + cut.Contains("is-invalid"); + } + + [Fact] + public void InvalidOperationException_Error() + { + Assert.ThrowsAny(() => Context.RenderComponent>()); + } + + [Fact] + public void Formatter_Ok() + { + var cut = Context.RenderComponent>(pb => + { + pb.Add(a => a.Value, 10.01m); + pb.Add(a => a.Formatter, v => $"{v + 1}"); + }); + var input = cut.Find("input"); + Assert.Equal("11.01", input.GetAttribute("value")); + + cut.SetParametersAndRender(pb => + { + pb.Add(a => a.Formatter, null); + pb.Add(a => a.FormatString, "#0.0"); + }); + Assert.Equal("10.0", input.GetAttribute("value")); + + input = cut.Find("input"); + cut.InvokeAsync(() => input.Change("")); + } + + [Fact] + public void Formatter_Error() + { + Assert.ThrowsAny(() => Context.RenderComponent()); + } + + [Fact] + public void ShowButton_Ok() + { + var inc = false; + var dec = false; + var cut = Context.RenderComponent>(pb => + { + pb.Add(a => a.ShowButton, true); + pb.Add(a => a.OnIncrement, v => + { + dec = true; + return Task.CompletedTask; + }); + pb.Add(a => a.OnDecrement, v => + { + inc = true; + return Task.CompletedTask; + }); + }); + cut.Contains("class=\"input-group\""); + + var buttons = cut.FindAll("button"); + cut.InvokeAsync(() => buttons[0].Click()); + Assert.True(inc); + + cut.InvokeAsync(() => buttons[1].Click()); + Assert.True(dec); + } + + [Theory] + [InlineData(typeof(short))] + [InlineData(typeof(int))] + [InlineData(typeof(long))] + [InlineData(typeof(float))] + [InlineData(typeof(double))] + [InlineData(typeof(decimal))] + public void Type_Ok(Type t) + { + var cut = Context.Render(builder => + { + builder.OpenComponent(0, typeof(BootstrapInputNumber<>).MakeGenericType(t)); + builder.AddAttribute(1, "ShowButton", true); + builder.AddAttribute(1, "Min", "-10"); + builder.AddAttribute(1, "Max", "10"); + builder.CloseComponent(); + }); + var buttons = cut.FindAll("button"); + cut.InvokeAsync(() => buttons[0].Click()); + cut.InvokeAsync(() => buttons[1].Click()); + } + + private class Cat + { + [Range(1, 10)] + public int Count { get; set; } + } + + [ExcludeFromCodeCoverage] + private class MockInputNumber : BootstrapInputNumber + { + public override Task SetParametersAsync(ParameterView parameters) + { + parameters.SetParameterProperties(this); + + OnInitialized(); + + return Task.CompletedTask; + } + + protected override string? InternalFormat(string value) + { + return base.InternalFormat(value); + } + + protected override void OnInitialized() + { + base.OnInitialized(); + + InternalFormat(""); + } + } +}