From c67ec264ed24fd24a5a63bc38aa2265385fbdb95 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Fri, 31 Oct 2014 15:04:41 -0700 Subject: [PATCH] Modify tests to use new ITagHelperDescriptorResolver signature. - Updated all of the tests to use the new ITagHelperDescriptorResolver signature so instead of passing strings they now construct TagHelperDescriptorResolutionContexts. - Removed several tests from the AddOrRemoveTagHelperSpanVisitorTests. This was due to the change in responsibility of managing the found TagHelperDescriptors; the TagHelperDescriptorResolver now does this. - Added several new tests to verify the TagHelperDescriptorResolver manages resolved TagHelperDescriptors based on the given TagHelperDirectiveDescriptors. #214 --- .../TagHelpers/TagHelperTypeResolver.cs | 3 +- .../TagHelperDescriptorResolverTest.cs | 389 +++++++++++++++--- .../Generator/TagHelperTestBase.cs | 21 +- .../AddOrRemoveTagHelperSpanVisitorTest.cs | 238 +++++------ 4 files changed, 459 insertions(+), 192 deletions(-) diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs index 3ef04014fe..a33313b87a 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs @@ -61,7 +61,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } } - private static bool IsTagHelper(TypeInfo typeInfo) + // Internal for testing. + internal virtual bool IsTagHelper(TypeInfo typeInfo) { return typeInfo.IsPublic && !typeInfo.IsAbstract && diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorResolverTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorResolverTest.cs index e70c0c2f63..77672900cb 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorResolverTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorResolverTest.cs @@ -15,44 +15,322 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers private static readonly string AssemblyName = typeof(TagHelperDescriptorFactoryTest).GetTypeInfo().Assembly.GetName().Name; + private static readonly Type Valid_PlainTagHelperType = typeof(Valid_PlainTagHelper); + + private static readonly Type Valid_InheritedTagHelperType = typeof(Valid_InheritedTagHelper); + + private static TagHelperDescriptor Valid_PlainTagHelperDescriptor + { + get + { + return new TagHelperDescriptor("Valid_Plain", + Valid_PlainTagHelperType.FullName, + AssemblyName, + ContentBehavior.None); + } + } + + private static TagHelperDescriptor Valid_InheritedTagHelperDescriptor + { + get + { + return new TagHelperDescriptor("Valid_Inherited", + Valid_InheritedTagHelperType.FullName, + AssemblyName, + ContentBehavior.None); + } + } + [Theory] [InlineData("MyType, MyAssembly", "MyAssembly")] [InlineData("MyAssembly2", "MyAssembly2")] public void Resolve_AllowsOverridenResolveDescriptorsInAssembly(string lookupText, string expectedAssemblyName) { // Arrange - var tagHelperDescriptorResolver = new TestTagHelperDescriptorResolver(); + var tagHelperDescriptorResolver = new AssemblyCheckingTagHelperDescriptorResolver(); + var context = new TagHelperDescriptorResolutionContext( + new[] { new TagHelperDirectiveDescriptor(lookupText, TagHelperDirectiveType.AddTagHelper) }); // Act - tagHelperDescriptorResolver.Resolve(lookupText); + tagHelperDescriptorResolver.Resolve(context); // Assert Assert.Equal(expectedAssemblyName, tagHelperDescriptorResolver.CalledWithAssemblyName); } + public static TheoryData ResolveDirectiveDescriptorsData + { + get + { + var assemblyA = AssemblyName; + var stringType = typeof(string); + + var assemblyB = stringType.GetTypeInfo().Assembly.GetName().Name; + + // We're treating 'string' as a TagHelper so we can test TagHelpers in multiple assemblies without + // building a separate assembly with a single TagHelper. + var stringTagHelperDescriptor = + new TagHelperDescriptor("string", + "System.String", + assemblyB, + ContentBehavior.None); + + return new TheoryData>, // descriptorAssemblyLookups + IEnumerable, // directiveDescriptors + IEnumerable> // expectedDescriptors + { + { + new Dictionary> + { + { assemblyA, new [] { Valid_PlainTagHelperType } } + }, + new [] + { + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper) + }, + new [] { Valid_PlainTagHelperDescriptor } + }, + { + new Dictionary> + { + { assemblyA, new [] { Valid_PlainTagHelperType } }, + { assemblyB, new [] { stringType } } + }, + new [] + { + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor(assemblyB, TagHelperDirectiveType.AddTagHelper) + }, + new [] { Valid_PlainTagHelperDescriptor, stringTagHelperDescriptor } + }, + { + new Dictionary> + { + { assemblyA, new [] { Valid_PlainTagHelperType } }, + { assemblyB, new [] { stringType } } + }, + new [] + { + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor(assemblyB, TagHelperDirectiveType.RemoveTagHelper) + }, + new [] { Valid_PlainTagHelperDescriptor } + }, + { + new Dictionary> + { + { assemblyA, new [] { Valid_PlainTagHelperType, Valid_InheritedTagHelperType } }, + { assemblyB, new [] { stringType } } + }, + new [] + { + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor(assemblyB, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.RemoveTagHelper) + }, + new [] { stringTagHelperDescriptor } + }, + { + new Dictionary> + { + { assemblyA, new [] { Valid_PlainTagHelperType, Valid_InheritedTagHelperType } } + }, + new [] + { + new TagHelperDirectiveDescriptor( + Valid_PlainTagHelperType.FullName + ", " + assemblyA, + TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper) + }, + new [] { Valid_PlainTagHelperDescriptor, Valid_InheritedTagHelperDescriptor } + }, + { + new Dictionary> + { + { assemblyA, new [] { Valid_PlainTagHelperType, Valid_InheritedTagHelperType } } + }, + new [] + { + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor( + Valid_PlainTagHelperType.FullName + ", " + assemblyA, + TagHelperDirectiveType.RemoveTagHelper) + }, + new [] { Valid_InheritedTagHelperDescriptor } + }, + { + new Dictionary> + { + { assemblyA, new [] { Valid_PlainTagHelperType, Valid_InheritedTagHelperType } }, + }, + new [] + { + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor( + Valid_PlainTagHelperType.FullName + ", " + assemblyA, + TagHelperDirectiveType.RemoveTagHelper), + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper) + }, + new [] { Valid_InheritedTagHelperDescriptor, Valid_PlainTagHelperDescriptor } + }, + { + new Dictionary> + { + { assemblyA, new [] { Valid_PlainTagHelperType, Valid_InheritedTagHelperType } }, + }, + new [] + { + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper), + }, + new [] { Valid_InheritedTagHelperDescriptor, Valid_PlainTagHelperDescriptor } + } + }; + } + } + + [Theory] + [MemberData(nameof(ResolveDirectiveDescriptorsData))] + public void Resolve_ReturnsDescriptorsBasedOnDirectiveDescriptors( + Dictionary> descriptorAssemblyLookups, + IEnumerable directiveDescriptors, + IEnumerable expectedDescriptors) + { + // Arrange + var tagHelperDescriptorResolver = + new TestTagHelperDescriptorResolver( + new LookupBasedTagHelperTypeResolver(descriptorAssemblyLookups)); + var resolutionContext = new TagHelperDescriptorResolutionContext(directiveDescriptors); + + // Act + var descriptors = tagHelperDescriptorResolver.Resolve(resolutionContext); + + // Assert + Assert.Equal(expectedDescriptors.Count(), descriptors.Count()); + + foreach (var expectedDescriptor in expectedDescriptors) + { + Assert.Contains(expectedDescriptor, descriptors, TagHelperDescriptorComparer.Default); + } + } + + public static TheoryData ResolveDirectiveDescriptorsData_EmptyResult + { + get + { + var assemblyA = AssemblyName; + var stringType = typeof(string); + + var assemblyB = stringType.GetTypeInfo().Assembly.GetName().Name; + var stringTagHelperDescriptor = + new TagHelperDescriptor("string", + "System.String", + assemblyB, + ContentBehavior.None); + + return new TheoryData>, // descriptorAssemblyLookups + IEnumerable> // directiveDescriptors + { + { + new Dictionary> + { + { assemblyA, new [] { Valid_PlainTagHelperType } }, + }, + new [] + { + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.RemoveTagHelper), + } + }, + { + new Dictionary> + { + { assemblyA, new [] { Valid_PlainTagHelperType, Valid_InheritedTagHelperType } }, + }, + new [] + { + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor(Valid_PlainTagHelperType.FullName + ", " + assemblyA, TagHelperDirectiveType.RemoveTagHelper), + new TagHelperDirectiveDescriptor(Valid_InheritedTagHelperType.FullName + ", " + assemblyA, TagHelperDirectiveType.RemoveTagHelper) + } + }, + { + new Dictionary> + { + { assemblyA, new [] { Valid_PlainTagHelperType, Valid_InheritedTagHelperType } }, + { assemblyB, new [] { stringType } }, + }, + new [] + { + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor(assemblyB, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.RemoveTagHelper), + new TagHelperDirectiveDescriptor(assemblyB, TagHelperDirectiveType.RemoveTagHelper) + } + }, + { + new Dictionary> + { + { assemblyA, new [] { Valid_PlainTagHelperType, Valid_InheritedTagHelperType } }, + { assemblyB, new [] { stringType } }, + }, + new [] + { + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor(assemblyB, TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor(Valid_PlainTagHelperType.FullName + ", " + assemblyA, TagHelperDirectiveType.RemoveTagHelper), + new TagHelperDirectiveDescriptor(Valid_InheritedTagHelperType.FullName + ", " + assemblyA, TagHelperDirectiveType.RemoveTagHelper), + new TagHelperDirectiveDescriptor(stringType.FullName + ", " + assemblyB, TagHelperDirectiveType.RemoveTagHelper) + } + }, + { + new Dictionary>(), + new [] + { + new TagHelperDirectiveDescriptor(assemblyA, TagHelperDirectiveType.RemoveTagHelper), + new TagHelperDirectiveDescriptor(Valid_PlainTagHelperType.FullName + ", " + assemblyA, TagHelperDirectiveType.RemoveTagHelper), + } + } + }; + } + } + + [Theory] + [MemberData(nameof(ResolveDirectiveDescriptorsData_EmptyResult))] + public void Resolve_CanReturnEmptyDescriptorsBasedOnDirectiveDescriptors( + Dictionary> descriptorAssemblyLookups, + IEnumerable directiveDescriptors) + { + // Arrange + var tagHelperDescriptorResolver = + new TestTagHelperDescriptorResolver( + new LookupBasedTagHelperTypeResolver(descriptorAssemblyLookups)); + var resolutionContext = new TagHelperDescriptorResolutionContext(directiveDescriptors); + + // Act + var descriptors = tagHelperDescriptorResolver.Resolve(resolutionContext); + + // Assert + Assert.Empty(descriptors); + } + [Fact] public void DescriptorResolver_DoesNotReturnInvalidTagHelpersWhenSpecified() { // Arrange var tagHelperDescriptorResolver = - new TagHelperDescriptorResolver( + new TestTagHelperDescriptorResolver( new TestTagHelperTypeResolver(TestableTagHelpers)); // Act var descriptors = tagHelperDescriptorResolver.Resolve( - "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_AbstractTagHelper, MyAssembly"); - descriptors = descriptors.Concat(tagHelperDescriptorResolver.Resolve( - "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_GenericTagHelper`, MyAssembly")); - descriptors = descriptors.Concat(tagHelperDescriptorResolver.Resolve( - "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_NestedPublicTagHelper, MyAssembly")); - descriptors = descriptors.Concat(tagHelperDescriptorResolver.Resolve( - "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_NestedInternalTagHelper, MyAssembly")); - descriptors = descriptors.Concat(tagHelperDescriptorResolver.Resolve( - "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_PrivateTagHelper, MyAssembly")); - descriptors = descriptors.Concat(tagHelperDescriptorResolver.Resolve( - "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_ProtectedTagHelper, MyAssembly")); - descriptors = descriptors.Concat(tagHelperDescriptorResolver.Resolve( - "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_InternalTagHelper, MyAssembly")); + "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_AbstractTagHelper, " + AssemblyName, + "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_GenericTagHelper`, " + AssemblyName, + "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_NestedPublicTagHelper, " + AssemblyName, + "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_NestedInternalTagHelper, " + AssemblyName, + "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_PrivateTagHelper, " + AssemblyName, + "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_ProtectedTagHelper, " + AssemblyName, + "Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_InternalTagHelper, " + AssemblyName); // Assert Assert.Empty(descriptors); @@ -78,84 +356,75 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers Assert.Equal("MyAssembly", assemblyName.Name); } }; - var tagHelperDescriptorResolver = new TagHelperDescriptorResolver(tagHelperTypeResolver); - var expectedDescriptor = new TagHelperDescriptor("Valid_Plain", - typeof(Valid_PlainTagHelper).FullName, - AssemblyName, - ContentBehavior.None); + var tagHelperDescriptorResolver = new TestTagHelperDescriptorResolver(tagHelperTypeResolver); // Act var descriptors = tagHelperDescriptorResolver.Resolve(lookupText); // Assert - var descriptor = Assert.Single(descriptors); - Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default); + Assert.Empty(descriptors); } [Fact] public void DescriptorResolver_ResolvesOnlyTypeResolverProvidedTypes() { // Arrange - var resolver = new TagHelperDescriptorResolver( + var resolver = new TestTagHelperDescriptorResolver( new LookupBasedTagHelperTypeResolver( new Dictionary>(StringComparer.OrdinalIgnoreCase) { - { "lookupText1", ValidTestableTagHelpers }, - { "lookupText2", new Type[]{ typeof(Valid_PlainTagHelper) } } + { AssemblyName, ValidTestableTagHelpers }, + { + Valid_PlainTagHelperType.FullName + ", " + AssemblyName, + new Type[] { Valid_PlainTagHelperType } + } })); - var expectedDescriptor = new TagHelperDescriptor("Valid_Plain", - typeof(Valid_PlainTagHelper).FullName, - AssemblyName, - ContentBehavior.None); // Act - var descriptors = resolver.Resolve("lookupText2"); + var descriptors = resolver.Resolve(Valid_PlainTagHelperType + ", " + AssemblyName); // Assert var descriptor = Assert.Single(descriptors); - Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default); + Assert.Equal(Valid_PlainTagHelperDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default); } [Fact] public void DescriptorResolver_ResolvesMultipleTypes() { // Arrange - var resolver = new TagHelperDescriptorResolver( + var resolver = new TestTagHelperDescriptorResolver( new LookupBasedTagHelperTypeResolver( new Dictionary>(StringComparer.OrdinalIgnoreCase) { - { "lookupText", new Type[]{ typeof(Valid_PlainTagHelper), typeof(Valid_InheritedTagHelper) } }, + { AssemblyName, new Type[]{ Valid_PlainTagHelperType, Valid_InheritedTagHelperType } }, })); var expectedDescriptors = new TagHelperDescriptor[] { - new TagHelperDescriptor("Valid_Plain", - typeof(Valid_PlainTagHelper).FullName, - AssemblyName, - ContentBehavior.None), - new TagHelperDescriptor("Valid_Inherited", - typeof(Valid_InheritedTagHelper).FullName, - AssemblyName, - ContentBehavior.None) + Valid_PlainTagHelperDescriptor, + Valid_InheritedTagHelperDescriptor }; // Act - var descriptors = resolver.Resolve("lookupText").ToArray(); + var descriptors = resolver.Resolve(AssemblyName).ToArray(); // Assert Assert.Equal(descriptors.Length, 2); - Assert.Equal(descriptors, expectedDescriptors, CompleteTagHelperDescriptorComparer.Default); + Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default); } [Fact] public void DescriptorResolver_DoesNotResolveTypesForNoTypeResolvingLookupText() { // Arrange - var resolver = new TagHelperDescriptorResolver( + var resolver = new TestTagHelperDescriptorResolver( new LookupBasedTagHelperTypeResolver( new Dictionary>(StringComparer.OrdinalIgnoreCase) { - { "lookupText1", ValidTestableTagHelpers }, - { "lookupText2", new Type[]{ typeof(Valid_PlainTagHelper) } } + { AssemblyName, ValidTestableTagHelpers }, + { + Valid_PlainTagHelperType.FullName + ", " + AssemblyName, + new Type[]{ Valid_PlainTagHelperType } + } })); // Act @@ -172,7 +441,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { // Arrange var tagHelperDescriptorResolver = - new TagHelperDescriptorResolver( + new TestTagHelperDescriptorResolver( new TestTagHelperTypeResolver(InvalidTestableTagHelpers)); var expectedMessage = @@ -190,6 +459,23 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers Assert.Equal(expectedMessage, ex.Message); } + private class TestTagHelperDescriptorResolver : TagHelperDescriptorResolver + { + public TestTagHelperDescriptorResolver(TagHelperTypeResolver typeResolver) + : base(typeResolver) + { + } + + public IEnumerable Resolve(params string[] lookupTexts) + { + return Resolve( + new TagHelperDescriptorResolutionContext( + lookupTexts.Select( + lookupText => + new TagHelperDirectiveDescriptor(lookupText, TagHelperDirectiveType.AddTagHelper)))); + } + } + private class LookupBasedTagHelperTypeResolver : TagHelperTypeResolver { private Dictionary> _lookupValues; @@ -207,9 +493,14 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers return types?.Select(type => type.GetTypeInfo()) ?? Enumerable.Empty(); } + + internal override bool IsTagHelper(TypeInfo typeInfo) + { + return true; + } } - private class TestTagHelperDescriptorResolver : TagHelperDescriptorResolver + private class AssemblyCheckingTagHelperDescriptorResolver : TagHelperDescriptorResolver { public string CalledWithAssemblyName { get; set; } diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperTestBase.cs index 11113b2231..7b9f57626c 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperTestBase.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperTestBase.cs @@ -40,9 +40,26 @@ namespace Microsoft.AspNet.Razor.Test.Generator _tagHelperDescriptors = tagHelperDescriptors ?? Enumerable.Empty(); } - public IEnumerable Resolve(string lookupText) + public IEnumerable Resolve(TagHelperDescriptorResolutionContext resolutionContext) { - return _tagHelperDescriptors; + IEnumerable descriptors = null; + + foreach (var directiveDescriptor in resolutionContext.DirectiveDescriptors) + { + if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.RemoveTagHelper) + { + // We don't yet support "typeName, assemblyName" for @removetaghelper in this test class. Will + // add that ability and add the corresponding end-to-end test verification in: + // https://github.com/aspnet/Razor/issues/222 + descriptors = null; + } + else if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.AddTagHelper) + { + descriptors = _tagHelperDescriptors; + } + } + + return descriptors ?? Enumerable.Empty(); } } diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/AddOrRemoveTagHelperSpanVisitorTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/AddOrRemoveTagHelperSpanVisitorTest.cs index fce4083250..24c4d70750 100644 --- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/AddOrRemoveTagHelperSpanVisitorTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/AddOrRemoveTagHelperSpanVisitorTest.cs @@ -8,6 +8,7 @@ using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.TagHelpers; using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.Internal.Web.Utils; #if !ASPNETCORE50 using Moq; #endif @@ -19,29 +20,14 @@ namespace Microsoft.AspNet.Razor.TagHelpers { private static readonly SpanFactory Factory = SpanFactory.CreateCsHtml(); - private static TagHelperDescriptor PTagHelperDescriptor - { - get - { - return new TagHelperDescriptor("p", "PTagHelper", "SomeAssembly", ContentBehavior.None); - } - } - - private static TagHelperDescriptor DivTagHelperDescriptor - { - get - { - return new TagHelperDescriptor("div", "DivTagHelper", "SomeAssembly", ContentBehavior.None); - } - } - #if !ASPNETCORE50 [Fact] - public void GetDescriptors_InvokesResolveForEachLookup() + public void GetDescriptors_InvokesResolveOnceForAllDirectives() { // Arrange var resolver = new Mock(); - resolver.Setup(mock => mock.Resolve(It.IsAny())).Returns(Enumerable.Empty()); + resolver.Setup(mock => mock.Resolve(It.IsAny())) + .Returns(Enumerable.Empty()); var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(resolver.Object); var document = new MarkupBlock( Factory.Code("\"one\"").AsAddTagHelper("one"), @@ -49,23 +35,68 @@ namespace Microsoft.AspNet.Razor.TagHelpers Factory.Code("\"three\"").AsRemoveTagHelper("three")); // Act - var descriptors = addOrRemoveTagHelperSpanVisitor.GetDescriptors(document); + addOrRemoveTagHelperSpanVisitor.GetDescriptors(document); // Assert - Assert.Empty(descriptors); - resolver.Verify(mock => mock.Resolve(It.IsAny()), Times.Exactly(3)); + resolver.Verify(mock => mock.Resolve(It.IsAny()), Times.Once); } #endif + [Fact] + public void GetDescriptors_LocatesTagHelperCodeGenerator_CreatesDirectiveDescriptors() + { + // Arrange + var resolver = new TestTagHelperDescriptorResolver(); + var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(resolver); + var document = new MarkupBlock( + Factory.Code("\"one\"").AsAddTagHelper("one"), + Factory.Code("\"two\"").AsRemoveTagHelper("two"), + Factory.Code("\"three\"").AsRemoveTagHelper("three")); + var expectedRegistrations = new TagHelperDirectiveDescriptor[] + { + new TagHelperDirectiveDescriptor("one", TagHelperDirectiveType.AddTagHelper), + new TagHelperDirectiveDescriptor("two", TagHelperDirectiveType.RemoveTagHelper), + new TagHelperDirectiveDescriptor("three", TagHelperDirectiveType.RemoveTagHelper), + }; + + // Act + addOrRemoveTagHelperSpanVisitor.GetDescriptors(document); + + // Assert + Assert.Equal(expectedRegistrations, + resolver.DirectiveDescriptors, + TagHelperDirectiveDescriptorComparer.Default); + } + + [Fact] + public void GetDescriptors_LocatesAddTagHelperCodeGenerator() + { + // Arrange + var resolver = new TestTagHelperDescriptorResolver(); + var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(resolver); + var document = new MarkupBlock( + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") + .Accepts(AcceptedCharacters.None), + Factory.Code("\"something\"").AsAddTagHelper("something")) + ); + var expectedRegistration = + new TagHelperDirectiveDescriptor("something", TagHelperDirectiveType.AddTagHelper); + + // Act + addOrRemoveTagHelperSpanVisitor.GetDescriptors(document); + + // Assert + var directiveDescriptor = Assert.Single(resolver.DirectiveDescriptors); + Assert.Equal(expectedRegistration, directiveDescriptor, TagHelperDirectiveDescriptorComparer.Default); + } + [Fact] public void GetDescriptors_LocatesNestedRemoveTagHelperCodeGenerator() { // Arrange - var resolver = new TestTagHelperDescriptorResolver( - new Dictionary> - { - { "something", new[] { PTagHelperDescriptor } } - }); + var resolver = new TestTagHelperDescriptorResolver(); var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(resolver); var document = new MarkupBlock( new DirectiveBlock( @@ -74,146 +105,73 @@ namespace Microsoft.AspNet.Razor.TagHelpers .Accepts(AcceptedCharacters.None), Factory.Code("\"something\"").AsRemoveTagHelper("something")) ); + var expectedRegistration = + new TagHelperDirectiveDescriptor("something", TagHelperDirectiveType.RemoveTagHelper); // Act - var descriptors = addOrRemoveTagHelperSpanVisitor.GetDescriptors(document); + addOrRemoveTagHelperSpanVisitor.GetDescriptors(document); // Assert - Assert.Empty(descriptors); - var lookup = Assert.Single(resolver.Lookups); - Assert.Equal("something", lookup); - } - - [Fact] - public void GetDescriptors_RemovesSpecifiedTagHelper() - { - // Arrange - var resolver = new TestTagHelperDescriptorResolver( - new Dictionary> - { - { "twoTagHelpers", new[] { PTagHelperDescriptor, DivTagHelperDescriptor } }, - { "singleTagHelper", new [] { PTagHelperDescriptor } } - }); - var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(resolver); - var document = new MarkupBlock( - Factory.Code("\"twoTagHelpers\"").AsAddTagHelper("twoTagHelpers"), - Factory.Code("\"singleTagHelper\"").AsRemoveTagHelper("singleTagHelper")); - - // Act - var descriptors = addOrRemoveTagHelperSpanVisitor.GetDescriptors(document); - - // Assert - var descriptor = Assert.Single(descriptors); - Assert.Equal(DivTagHelperDescriptor, descriptor, TagHelperDescriptorComparer.Default); - Assert.Equal(2, resolver.Lookups.Count); - Assert.Contains("twoTagHelpers", resolver.Lookups); - Assert.Contains("singleTagHelper", resolver.Lookups); - } - - [Fact] - public void GetDescriptors_RemovesAddedTagHelpers() - { - // Arrange - var resolver = new TestTagHelperDescriptorResolver( - new Dictionary> - { - { "twoTagHelpers", new[] { PTagHelperDescriptor, DivTagHelperDescriptor } } - }); - var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(resolver); - var document = new MarkupBlock( - Factory.Code("\"twoTagHelpers\"").AsAddTagHelper("twoTagHelpers"), - Factory.Code("\"twoTagHelpers\"").AsRemoveTagHelper("twoTagHelpers")); - - // Act - var descriptors = addOrRemoveTagHelperSpanVisitor.GetDescriptors(document); - - // Assert - Assert.Empty(descriptors); - Assert.Equal(Enumerable.Repeat("twoTagHelpers", 2), resolver.Lookups); - } - - [Fact] - public void GetDescriptors_RemoveTagHelper_OrderMatters() - { - // Arrange - var expectedDescriptors = new[] { PTagHelperDescriptor, DivTagHelperDescriptor }; - var resolver = new TestTagHelperDescriptorResolver( - new Dictionary> - { - { "twoTagHelpers", expectedDescriptors } - }); - var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(resolver); - var document = new MarkupBlock( - Factory.Code("\"twoTagHelpers\"").AsRemoveTagHelper("twoTagHelpers"), - Factory.Code("\"twoTagHelpers\"").AsAddTagHelper("twoTagHelpers")); - - // Act - var descriptors = addOrRemoveTagHelperSpanVisitor.GetDescriptors(document); - - // Assert - Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.Default); - Assert.Equal(Enumerable.Repeat("twoTagHelpers", 2), resolver.Lookups); - } - - [Fact] - public void GetDescriptors_RemoveTagHelperInDocument_ThrowsIfNullResolver() - { - // Arrange - var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(descriptorResolver: null); - var document = new MarkupBlock( - Factory.Code("\"something\"").AsRemoveTagHelper("something")); - var expectedMessage = "Cannot use directive 'removetaghelper' when a Microsoft.AspNet.Razor.TagHelpers." + - "ITagHelperDescriptorResolver has not been provided to the Microsoft.AspNet.Razor." + - "Parser.RazorParser."; - - // Act & Assert - var ex = Assert.Throws(() => - { - addOrRemoveTagHelperSpanVisitor.GetDescriptors(document); - }); - - Assert.Equal(expectedMessage, ex.Message); + var directiveDescriptor = Assert.Single(resolver.DirectiveDescriptors); + Assert.Equal(expectedRegistration, directiveDescriptor, TagHelperDirectiveDescriptorComparer.Default); } [Fact] public void GetDescriptors_RemoveTagHelperNotInDocument_DoesNotThrow() { // Arrange - var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(descriptorResolver: null); + var addOrRemoveTagHelperSpanVisitor = + new AddOrRemoveTagHelperSpanVisitor( + new TestTagHelperDescriptorResolver()); var document = new MarkupBlock(Factory.Markup("Hello World")); // Act & Assert Assert.DoesNotThrow(() => addOrRemoveTagHelperSpanVisitor.GetDescriptors(document)); } - // TODO: Add @addtaghelper directive unit tests. Tracked by https://github.com/aspnet/Razor/issues/202. - private class TestTagHelperDescriptorResolver : ITagHelperDescriptorResolver { - private readonly IReadOnlyDictionary> _lookupTable; - - public TestTagHelperDescriptorResolver( - IReadOnlyDictionary> lookupTable) + public TestTagHelperDescriptorResolver() { - _lookupTable = lookupTable; - - Lookups = new List(); + DirectiveDescriptors = new List(); } - public List Lookups { get; private set; } + public List DirectiveDescriptors { get; } - public IEnumerable Resolve(string lookupText) + public IEnumerable Resolve(TagHelperDescriptorResolutionContext resolutionContext) { - Lookups.Add(lookupText); - - IEnumerable descriptors; - if (_lookupTable.TryGetValue(lookupText, out descriptors)) - { - return descriptors; - } + DirectiveDescriptors.AddRange(resolutionContext.DirectiveDescriptors); return Enumerable.Empty(); } } + + private class TagHelperDirectiveDescriptorComparer : IEqualityComparer + { + public static readonly TagHelperDirectiveDescriptorComparer Default = + new TagHelperDirectiveDescriptorComparer(); + + private TagHelperDirectiveDescriptorComparer() + { + } + + public bool Equals(TagHelperDirectiveDescriptor directiveDescriptorX, + TagHelperDirectiveDescriptor directiveDescriptorY) + { + return string.Equals(directiveDescriptorX.LookupText, + directiveDescriptorY.LookupText, + StringComparison.Ordinal) && + directiveDescriptorX.DirectiveType == directiveDescriptorY.DirectiveType; + } + + public int GetHashCode(TagHelperDirectiveDescriptor directiveDescriptor) + { + return HashCodeCombiner.Start() + .Add(base.GetHashCode()) + .Add(directiveDescriptor.LookupText) + .Add(directiveDescriptor.DirectiveType) + .CombinedHash; + } + } } } \ No newline at end of file