From f8c7f1ab780f8a984a26eca28b172ea808a4d48d Mon Sep 17 00:00:00 2001 From: Doug Bunting Date: Fri, 10 Jun 2016 14:30:09 -0700 Subject: [PATCH] Add more tests using `[ViewComponent(Name = "...")]` - #4851 --- .../DefaultViewComponentSelectorTest.cs | 58 +++++++++++++++++-- .../ViewComponentConventionsTest.cs | 57 +++++++++++++++++- 2 files changed, 110 insertions(+), 5 deletions(-) diff --git a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/ViewComponents/DefaultViewComponentSelectorTest.cs b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/ViewComponents/DefaultViewComponentSelectorTest.cs index 5a36085676..3bb4026467 100644 --- a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/ViewComponents/DefaultViewComponentSelectorTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/ViewComponents/DefaultViewComponentSelectorTest.cs @@ -97,7 +97,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewComponents { // Arrange var selector = CreateSelector(); - var expected = "The view component name 'Ambiguous' matched multiple types:" + Environment.NewLine + $"Type: '{typeof(ViewComponentContainer.Ambiguous1)}' - " + @@ -112,6 +111,27 @@ namespace Microsoft.AspNetCore.Mvc.ViewComponents Assert.Equal(expected, ex.Message); } + [Theory] + [InlineData("Name")] + [InlineData("Ambiguous.Name")] + public void SelectComponent_AmbiguityDueToDerivation(string name) + { + // Arrange + var selector = CreateSelector(); + var expected = + $"The view component name '{name}' matched multiple types:" + Environment.NewLine + + $"Type: '{typeof(ViewComponentContainer.AmbiguousBase)}' - " + + "Name: 'Ambiguous.Name'" + Environment.NewLine + + $"Type: '{typeof(ViewComponentContainer.DerivedAmbiguous)}' - " + + "Name: 'Ambiguous.Name'"; + + // Act + var ex = Assert.Throws(() => selector.SelectComponent(name)); + + // Assert + Assert.Equal(expected, ex.Message); + } + [Fact] public void SelectComponent_FullNameToAvoidAmbiguity() { @@ -125,6 +145,19 @@ namespace Microsoft.AspNetCore.Mvc.ViewComponents Assert.Same(typeof(ViewComponentContainer.Ambiguous1).GetTypeInfo(), result.TypeInfo); } + [Fact] + public void SelectComponent_OverrideNameToAvoidAmbiguity() + { + // Arrange + var selector = CreateSelector(); + + // Act + var result = selector.SelectComponent("NonAmbiguousName"); + + // Assert + Assert.Same(typeof(ViewComponentContainer.DerivedAmbiguousWithOverriddenName).GetTypeInfo(), result.TypeInfo); + } + [Theory] [InlineData("FullNameInAttribute")] [InlineData("CoolNameSpace.FullNameInAttribute")] @@ -143,7 +176,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewComponents private IViewComponentSelector CreateSelector() { var provider = new DefaultViewComponentDescriptorCollectionProvider( - new FilteredViewComponentDescriptorProvider()); + new FilteredViewComponentDescriptorProvider()); + return new DefaultViewComponentSelector(provider); } @@ -187,6 +221,21 @@ namespace Microsoft.AspNetCore.Mvc.ViewComponents { public string Invoke() => "Hello"; } + + [ViewComponent(Name = "Ambiguous.Name")] + public class AmbiguousBase + { + public string Invoke() => "Hello"; + } + + public class DerivedAmbiguous : AmbiguousBase + { + } + + [ViewComponent(Name = "NonAmbiguousName")] + public class DerivedAmbiguousWithOverriddenName : AmbiguousBase + { + } } // This will only consider types nested inside this class as ViewComponent classes private class FilteredViewComponentDescriptorProvider : DefaultViewComponentDescriptorProvider @@ -196,12 +245,13 @@ namespace Microsoft.AspNetCore.Mvc.ViewComponents { } + // For error messages in tests above, ensure the TestApplicationPart returns types in a consistent order. public FilteredViewComponentDescriptorProvider(params Type[] allowedTypes) - : base(GetApplicationPartManager(allowedTypes.Select(t => t.GetTypeInfo()))) + : base(GetApplicationPartManager(allowedTypes.OrderBy(type => type.Name, StringComparer.Ordinal))) { } - private static ApplicationPartManager GetApplicationPartManager(IEnumerable types) + private static ApplicationPartManager GetApplicationPartManager(IEnumerable types) { var manager = new ApplicationPartManager(); manager.ApplicationParts.Add(new TestApplicationPart(types)); diff --git a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/ViewComponents/ViewComponentConventionsTest.cs b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/ViewComponents/ViewComponentConventionsTest.cs index cbcef0f599..dea32720ea 100644 --- a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/ViewComponents/ViewComponentConventionsTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/ViewComponents/ViewComponentConventionsTest.cs @@ -35,7 +35,10 @@ namespace Microsoft.AspNetCore.Mvc.ViewComponents // The Attribute does apply to derived classes. [InlineData(typeof(WithAttribute), true)] [InlineData(typeof(DerivedWithAttribute), true)] - + [InlineData(typeof(WithAttributeAndName), true)] + [InlineData(typeof(DerivedWithAttributeAndName), true)] + [InlineData(typeof(DerivedWithOverriddenAttributeName), true)] + // Value types cannot be view components [InlineData(typeof(int), false)] public void IsComponent(Type type, bool expected) @@ -47,6 +50,44 @@ namespace Microsoft.AspNetCore.Mvc.ViewComponents Assert.Equal(expected, result); } + [Theory] + [InlineData(typeof(PublicClass), "PublicClass")] + [InlineData(typeof(GenericViewComponent), "GenericViewComponent`1")] + [InlineData(typeof(NamingConventionViewComponent), "NamingConvention")] + [InlineData(typeof(CaseInsensitiveNamingConventionVIEWCOMPONENT), "CaseInsensitiveNamingConvention")] + [InlineData(typeof(WithAttribute), "WithAttribute")] + [InlineData(typeof(DerivedWithAttribute), "DerivedWithAttribute")] + [InlineData(typeof(WithAttributeAndName), "Name")] + [InlineData(typeof(DerivedWithAttributeAndName), "Name")] + [InlineData(typeof(DerivedWithOverriddenAttributeName), "Name")] + public void GetComponentName(Type type, string expected) + { + // Arrange & Act + var result = ViewComponentConventions.GetComponentName(type.GetTypeInfo()); + + // Assert + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(typeof(PublicClass), "Microsoft.AspNetCore.Mvc.ViewComponentConventionsTestClasses.PublicClass")] + [InlineData(typeof(GenericViewComponent), "Microsoft.AspNetCore.Mvc.ViewComponentConventionsTestClasses.GenericViewComponent`1")] + [InlineData(typeof(NamingConventionViewComponent), "Microsoft.AspNetCore.Mvc.ViewComponentConventionsTestClasses.NamingConvention")] + [InlineData(typeof(CaseInsensitiveNamingConventionVIEWCOMPONENT), "Microsoft.AspNetCore.Mvc.ViewComponentConventionsTestClasses.CaseInsensitiveNamingConvention")] + [InlineData(typeof(WithAttribute), "Microsoft.AspNetCore.Mvc.ViewComponentConventionsTestClasses.WithAttribute")] + [InlineData(typeof(DerivedWithAttribute), "Microsoft.AspNetCore.Mvc.ViewComponentConventionsTestClasses.DerivedWithAttribute")] + [InlineData(typeof(WithAttributeAndName), "Name")] + [InlineData(typeof(DerivedWithAttributeAndName), "Name")] + [InlineData(typeof(DerivedWithOverriddenAttributeName), "New.Name")] + public void GetComponentFullName(Type type, string expected) + { + // Arrange & Act + var result = ViewComponentConventions.GetComponentFullName(type.GetTypeInfo()); + + // Assert + Assert.Equal(expected, result); + } + public class PublicNestedClass : ViewComponent { } @@ -105,4 +146,18 @@ namespace Microsoft.AspNetCore.Mvc.ViewComponentConventionsTestClasses public class DerivedWithAttribute : WithAttribute { } + + [ViewComponent(Name = "Name")] + public class WithAttributeAndName + { + } + + public class DerivedWithAttributeAndName : WithAttributeAndName + { + } + + [ViewComponent(Name = "New.Name")] + public class DerivedWithOverriddenAttributeName : WithAttributeAndName + { + } } \ No newline at end of file