From 538a479afca56148cd6a7469580f10244d4e2923 Mon Sep 17 00:00:00 2001 From: kooliokey Date: Tue, 30 Aug 2022 08:03:35 -0400 Subject: [PATCH] test(module: avatar): Add Avatar and AvatarGroup tests (#2648) Small refactors: - Fix typo in Avatar - Remove code in Avatar that wasn't affecting visuals as expected - Make Avatar shape a constant. Update Skeleton component and demos to use it - Update Avatar component to calculate size after parameters set method due to it changing one of the properties it relies on. --- components/avatar/Avatar.razor.cs | 29 ++- components/avatar/AvatarShape.cs | 8 + components/skeleton/Skeleton.razor.cs | 2 +- components/skeleton/SkeletonElement.razor.cs | 4 +- components/skeleton/SkeletonType.cs | 6 - .../Demos/Components/Avatar/demo/Badge_.razor | 4 +- .../Demos/Components/Avatar/demo/Basic.razor | 8 +- .../Components/Skeleton/demo/Element.razor | 6 +- .../Shared/ContributorsList.razor | 2 +- .../Avatar/AvatarGroupTests.cs | 135 ++++++++++++ tests/AntDesign.Tests/Avatar/AvatarTests.cs | 202 ++++++++++++++++++ 11 files changed, 372 insertions(+), 34 deletions(-) create mode 100644 components/avatar/AvatarShape.cs create mode 100644 tests/AntDesign.Tests/Avatar/AvatarGroupTests.cs create mode 100644 tests/AntDesign.Tests/Avatar/AvatarTests.cs diff --git a/components/avatar/Avatar.razor.cs b/components/avatar/Avatar.razor.cs index 0d4f0e003..ef504baef 100644 --- a/components/avatar/Avatar.razor.cs +++ b/components/avatar/Avatar.razor.cs @@ -16,7 +16,7 @@ namespace AntDesign set { _childContent = value; - _waitingCaclSize = true; + _waitingCalcSize = true; } } @@ -35,7 +35,7 @@ namespace AntDesign if (_text != value) { _text = value; - _waitingCaclSize = true; + _waitingCalcSize = true; } } } @@ -81,16 +81,13 @@ namespace AntDesign private string _text; private RenderFragment _childContent; - private bool _waitingCaclSize; + private bool _waitingCalcSize; protected override void OnInitialized() { base.OnInitialized(); Group?.AddAvatar(this); - - SetClassMap(); - SetSizeStyle(); } protected override void OnParametersSet() @@ -99,14 +96,17 @@ namespace AntDesign _hasIcon = string.IsNullOrEmpty(Src) && !string.IsNullOrEmpty(Icon); _hasSrc = !string.IsNullOrEmpty(Src); + SetClassMap(); + SetSizeStyle(); + base.OnParametersSet(); } protected override async Task OnAfterRenderAsync(bool firstRender) { - if (firstRender || _waitingCaclSize) + if (firstRender || _waitingCalcSize) { - _waitingCaclSize = false; + _waitingCalcSize = false; await CalcStringSize(); } @@ -128,7 +128,7 @@ namespace AntDesign _hasText = true; } - _waitingCaclSize = true; + _waitingCalcSize = true; } private void SetClassMap() @@ -149,7 +149,7 @@ namespace AntDesign _sizeStyles = $"width:{size};height:{size};line-height:{size};"; if (_hasIcon) { - _sizeStyles += $"font-size:calc(${size} / 2);"; + _sizeStyles += $"font-size:calc({size} / 2);"; } } } @@ -163,12 +163,11 @@ namespace AntDesign var childrenWidth = (await JsInvokeAsync(JSInteropConstants.GetDomInfo, TextEl))?.OffsetWidth ?? 0; var avatarWidth = (await JsInvokeAsync(JSInteropConstants.GetBoundingClientRect, Ref))?.Width ?? 0; - var scale = childrenWidth != 0 && avatarWidth - 8 < childrenWidth ? (avatarWidth - 8) / childrenWidth : 1; + var scale = childrenWidth != 0 && avatarWidth - 8 < childrenWidth + ? (avatarWidth - 8) / childrenWidth + : 1; + _textStyles = $"transform: scale({new CssSizeLength(scale, true)}) translateX(-50%);"; - if (decimal.TryParse(Size, out var pxSize)) - { - _textStyles += $"lineHeight:{(CssSizeLength)pxSize};"; - } StateHasChanged(); } diff --git a/components/avatar/AvatarShape.cs b/components/avatar/AvatarShape.cs new file mode 100644 index 000000000..cfc31ce1c --- /dev/null +++ b/components/avatar/AvatarShape.cs @@ -0,0 +1,8 @@ +namespace AntDesign +{ + public static class AvatarShape + { + public const string Square = "square"; + public const string Circle = "circle"; + } +} diff --git a/components/skeleton/Skeleton.razor.cs b/components/skeleton/Skeleton.razor.cs index 288167958..f2e914ad1 100644 --- a/components/skeleton/Skeleton.razor.cs +++ b/components/skeleton/Skeleton.razor.cs @@ -115,7 +115,7 @@ namespace AntDesign if (AvatarShape == null) { - AvatarShape = SkeletonAvatarShape.Circle; + AvatarShape = AntDesign.AvatarShape.Circle; } if (AvatarSize.Value == null) diff --git a/components/skeleton/SkeletonElement.razor.cs b/components/skeleton/SkeletonElement.razor.cs index 5cbe4a5a6..0e9e9555d 100644 --- a/components/skeleton/SkeletonElement.razor.cs +++ b/components/skeleton/SkeletonElement.razor.cs @@ -62,8 +62,8 @@ namespace AntDesign private void SetAvatarMap() { _spanClassMapper.Clear().If("ant-skeleton-avatar", () => true) - .If("ant-skeleton-avatar-square", () => Shape == SkeletonAvatarShape.Square) - .If("ant-skeleton-avatar-circle", () => Shape == SkeletonAvatarShape.Circle) + .If("ant-skeleton-avatar-square", () => Shape == AvatarShape.Square) + .If("ant-skeleton-avatar-circle", () => Shape == AvatarShape.Circle) .If("ant-skeleton-avatar-lg", () => Size.AsT1 == SkeletonElementSize.Large) .If("ant-skeleton-avatar-sm", () => Size.AsT1 == SkeletonElementSize.Small); } diff --git a/components/skeleton/SkeletonType.cs b/components/skeleton/SkeletonType.cs index 7cdfa73d9..5e8dac91b 100644 --- a/components/skeleton/SkeletonType.cs +++ b/components/skeleton/SkeletonType.cs @@ -12,12 +12,6 @@ namespace AntDesign public const string Small = "small"; } - public static class SkeletonAvatarShape - { - public const string Square = "square"; - public const string Circle = "circle"; - } - public static class SkeletonButtonShape { public const string Default = "default"; diff --git a/site/AntDesign.Docs/Demos/Components/Avatar/demo/Badge_.razor b/site/AntDesign.Docs/Demos/Components/Avatar/demo/Badge_.razor index fc28c5531..403567c1f 100644 --- a/site/AntDesign.Docs/Demos/Components/Avatar/demo/Badge_.razor +++ b/site/AntDesign.Docs/Demos/Components/Avatar/demo/Badge_.razor @@ -1,12 +1,12 @@ 
- + - +
diff --git a/site/AntDesign.Docs/Demos/Components/Avatar/demo/Basic.razor b/site/AntDesign.Docs/Demos/Components/Avatar/demo/Basic.razor index 9606e1c68..3c562925b 100644 --- a/site/AntDesign.Docs/Demos/Components/Avatar/demo/Basic.razor +++ b/site/AntDesign.Docs/Demos/Components/Avatar/demo/Basic.razor @@ -6,9 +6,9 @@
- - - - + + + +
\ No newline at end of file diff --git a/site/AntDesign.Docs/Demos/Components/Skeleton/demo/Element.razor b/site/AntDesign.Docs/Demos/Components/Skeleton/demo/Element.razor index c9b474279..7921f22ee 100644 --- a/site/AntDesign.Docs/Demos/Components/Skeleton/demo/Element.razor +++ b/site/AntDesign.Docs/Demos/Components/Skeleton/demo/Element.razor @@ -40,8 +40,8 @@ AvatarShape: - Circle - Square + Circle + Square @@ -74,5 +74,5 @@ string _avatarSize = SkeletonElementSize.Default; string _inputSize = SkeletonElementSize.Default; string _buttonShape = SkeletonButtonShape.Default; - string _avatarShape = SkeletonAvatarShape.Circle; + string _avatarShape = AvatarShape.Circle; } \ No newline at end of file diff --git a/site/AntDesign.Docs/Shared/ContributorsList.razor b/site/AntDesign.Docs/Shared/ContributorsList.razor index 0a5fb8180..24160ee5a 100644 --- a/site/AntDesign.Docs/Shared/ContributorsList.razor +++ b/site/AntDesign.Docs/Shared/ContributorsList.razor @@ -17,7 +17,7 @@ for (var i = 0; i < 3; i++) { + Active Size="SkeletonElementSize.Default" Shape="@AvatarShape.Circle" /> } } diff --git a/tests/AntDesign.Tests/Avatar/AvatarGroupTests.cs b/tests/AntDesign.Tests/Avatar/AvatarGroupTests.cs new file mode 100644 index 000000000..6d87315f1 --- /dev/null +++ b/tests/AntDesign.Tests/Avatar/AvatarGroupTests.cs @@ -0,0 +1,135 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using AntDesign.JsInterop; +using Bunit; +using FluentAssertions; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.Extensions.Options; +using Xunit; + +namespace AntDesign.Tests.Avatar +{ + public class AvatarGroupTests : AntDesignTestBase + { + [Fact] + public void ItShouldRenderAvatarChildCntent() + { + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getInfo", _ => true) + .SetResult(new HtmlElement()); + + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getBoundingClientRect", _ => true) + .SetResult(new DomRect()); + + RenderFragment fragment = builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, "Icon", "user"); + builder.CloseComponent(); + + builder.OpenComponent(1); + builder.AddAttribute(1, "Text", "U"); + builder.CloseComponent(); + + builder.OpenComponent(2); + builder.AddAttribute(1, "Text", "Username"); + builder.CloseComponent(); + }; + + var systemUnderTest = RenderComponent(parameters => parameters + .Add(x => x.ChildContent, fragment)); + + systemUnderTest.FindAll(".ant-avatar").Count.Should().Be(3); + } + + [Fact] + public void ItShouldRenderOverflowAvatarsProperly() + { + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getInfo", _ => true) + .SetResult(new HtmlElement()); + + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getBoundingClientRect", _ => true) + .SetResult(new DomRect()); + + RenderFragment fragment = builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, "Icon", "user"); + builder.CloseComponent(); + + builder.OpenComponent(1); + builder.AddAttribute(1, "Text", "U"); + builder.CloseComponent(); + + builder.OpenComponent(2); + builder.AddAttribute(1, "Text", "Username"); + builder.CloseComponent(); + + builder.OpenComponent(3); + builder.AddAttribute(1, "Text", "Username"); + builder.CloseComponent(); + }; + + var systemUnderTest = RenderComponent(parameters => parameters + .Add(x => x.ChildContent, fragment) + .Add(x => x.MaxCount, 2)); + + systemUnderTest.MarkupMatches(@"
+ + + + + + + + + + U + + + + + +2 + + +
"); + } + + [Fact] + public void ItShouldWrapChildContentInDiv() + { + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getInfo", _ => true) + .SetResult(new HtmlElement()); + + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getBoundingClientRect", _ => true) + .SetResult(new DomRect()); + + RenderFragment fragment = builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, "Icon", "user"); + builder.CloseComponent(); + + builder.OpenComponent(1); + builder.AddAttribute(1, "Text", "U"); + builder.CloseComponent(); + + builder.OpenComponent(3); + builder.AddAttribute(1, "Text", "Username"); + builder.CloseComponent(); + }; + + var systemUnderTest = RenderComponent(parameters => parameters + .Add(x => x.ChildContent, fragment)); + + systemUnderTest.MarkupMatches("
"); + } + } +} diff --git a/tests/AntDesign.Tests/Avatar/AvatarTests.cs b/tests/AntDesign.Tests/Avatar/AvatarTests.cs new file mode 100644 index 000000000..c4805a1c4 --- /dev/null +++ b/tests/AntDesign.Tests/Avatar/AvatarTests.cs @@ -0,0 +1,202 @@ +using System; +using System.Threading.Tasks; +using AntDesign.JsInterop; +using Bunit; +using FluentAssertions; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; +using Xunit; + +namespace AntDesign.Tests.Avatar +{ + public class AvatarTests : AntDesignTestBase + { + [Theory] + [InlineData(AntSizeLDSType.Small, "ant-avatar-sm", "")] + [InlineData(AntSizeLDSType.Large, "ant-avatar-lg", "")] + [InlineData("64.5", "", "width:64.5px;height:64.5px;line-height:64.5px;font-size:calc(64.5px / 2);")] + public void ItShouldSetSizeProperly(string size, string expectedClass, string expectedStyle) + { + var systemUnderTest = RenderComponent(parameters => parameters + .Add(x => x.Size, size) + .Add(x => x.Icon, "user")); + + systemUnderTest.MarkupMatches(@$" + + + + + + + "); + } + + [Theory] + [InlineData(AvatarShape.Square, "ant-avatar-square")] + [InlineData(AvatarShape.Circle, "ant-avatar-circle")] + [InlineData(null, "")] + public void ItShouldProperlyStyleShapes(string shape, string expectedClass) + { + var systemUnderTest = RenderComponent(parameters => parameters + .Add(x => x.Shape, shape) + .Add(x => x.Icon, "user")); + + systemUnderTest.MarkupMatches(@$" + + + + + + + "); + } + + [Theory] + [InlineData(200, 64, 0.28)] + [InlineData(56, 64, 1)] + [InlineData(10, 64, 1)] + [InlineData(57, 64, 0.9824561403508771929824561404)] + public void ItShouldRenderAndScaleTextProperly(int textWidth, decimal avatarWidth, double expectedScale) + { + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getInfo", _ => true) + .SetResult(new HtmlElement + { + OffsetWidth = textWidth + }); + + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getBoundingClientRect", _ => true) + .SetResult(new DomRect + { + Width = avatarWidth + }); + + var systemUnderTest = RenderComponent(parameters => parameters + .Add(x => x.Text, "KR")); + + systemUnderTest.MarkupMatches(@$" + KR + "); + } + + [Fact] + public void ItShouldRenderAndScaleTextProperlyWhenUnableToGetJsInfo() + { + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getInfo", _ => true) + .SetResult(null); + + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getBoundingClientRect", _ => true) + .SetResult(null); + + var systemUnderTest = RenderComponent(parameters => parameters + .Add(x => x.Text, "KR")); + + systemUnderTest.MarkupMatches(@$" + KR + "); + } + + [Theory] + [InlineData(200, 64, 0.28)] + [InlineData(56, 64, 1)] + [InlineData(10, 64, 1)] + [InlineData(57, 64, 0.9824561403508771929824561404)] + public void ItShouldRenderAndScaleChildContentProperly(int textWidth, decimal avatarWidth, double expectedScale) + { + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getInfo", _ => true) + .SetResult(new HtmlElement + { + OffsetWidth = textWidth + }); + + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getBoundingClientRect", _ => true) + .SetResult(new DomRect + { + Width = avatarWidth + }); + + RenderFragment fragment = builder => + { + builder.OpenElement(0, "span"); + builder.AddContent(0, "Text"); + + builder.CloseElement(); + }; + + var systemUnderTest = RenderComponent(parameters => parameters + .Add(x => x.ChildContent, fragment)); + + systemUnderTest.MarkupMatches(@$" + + Text + + "); + } + + [Fact] + public void ItShouldCallOnErrorWhenImageLoadErrors() + { + var calledOnError = false; + + var systemUnderTest = RenderComponent(parameters => parameters + .Add(x => x.Size, AntSizeLDSType.Default) + .Add(x => x.Src, "InvalidImage") + .Add(x => x.OnError, () => calledOnError = true) + .Add(x => x.Icon, "user")); + + systemUnderTest.Find("img").TriggerEvent("onerror", new ErrorEventArgs()); + + systemUnderTest.WaitForAssertion(() => calledOnError.Should().BeTrue()); + } + + [Fact] + public void ItShouldRenderIconWhenImageErrorsAndGivenIcon() + { + var systemUnderTest = RenderComponent(parameters => parameters + .Add(x => x.Size, AntSizeLDSType.Default) + .Add(x => x.Src, "InvalidImage") + .Add(x => x.Icon, "user") + .Add(x => x.Text, "Won't be used")); + + systemUnderTest.Find("img").TriggerEvent("onerror", new ErrorEventArgs()); + + systemUnderTest.WaitForAssertion(() => systemUnderTest.MarkupMatches(@$" + + + + + + + ")); + } + + [Fact] + public void ItShouldRenderTextWhenImageErrorsAndGivenTextButNotIcon() + { + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getInfo", _ => true) + .SetResult(new HtmlElement()); + + JSInterop + .Setup("AntDesign.interop.domInfoHelper.getBoundingClientRect", _ => true) + .SetResult(new DomRect()); + + var systemUnderTest = RenderComponent(parameters => parameters + .Add(x => x.Size, AntSizeLDSType.Default) + .Add(x => x.Src, "InvalidImage") + .Add(x => x.Text, "USER")); + + systemUnderTest.Find("img").TriggerEvent("onerror", new ErrorEventArgs()); + + systemUnderTest.WaitForAssertion(() => systemUnderTest.Find(".ant-avatar-string") + .MarkupMatches(@$" + USER + ")); + } + } +}