Add tests to validate TagHelper attribute targeting.

- Added tests to validate TargetElementAttribute, TagHelperDescriptorFactory, CSharpTagHelperCodeRenderer and TagHelperParseTreeRewriterTests.
- Renamed HtmlElementNameAttributeTest to TargetElementAttributeTest.

#311
This commit is contained in:
N. Taylor Mullen 2015-03-17 21:37:40 -07:00
parent d22246f636
commit 4345b06e88
11 changed files with 1991 additions and 251 deletions

View File

@ -0,0 +1,89 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
public class CaseSensitiveTagHelperDescriptorComparer : TagHelperDescriptorComparer, IEqualityComparer<TagHelperDescriptor>
{
public new static readonly CaseSensitiveTagHelperDescriptorComparer Default =
new CaseSensitiveTagHelperDescriptorComparer();
private CaseSensitiveTagHelperDescriptorComparer()
{
}
bool IEqualityComparer<TagHelperDescriptor>.Equals(
TagHelperDescriptor descriptorX,
TagHelperDescriptor descriptorY)
{
return base.Equals(descriptorX, descriptorY) &&
// Normal comparer doesn't care about the case, required attribute order, attributes or prefixes.
// In tests we do.
string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.Ordinal) &&
string.Equals(descriptorX.Prefix, descriptorY.Prefix, StringComparison.Ordinal) &&
Enumerable.SequenceEqual(
descriptorX.RequiredAttributes,
descriptorY.RequiredAttributes,
StringComparer.Ordinal) &&
descriptorX.Attributes.SequenceEqual(
descriptorY.Attributes,
CaseSensitiveAttributeDescriptorComparer.Default);
}
int IEqualityComparer<TagHelperDescriptor>.GetHashCode(TagHelperDescriptor descriptor)
{
var hashCodeCombiner = HashCodeCombiner
.Start()
.Add(base.GetHashCode())
.Add(descriptor.TagName, StringComparer.Ordinal)
.Add(descriptor.Prefix);
foreach (var requiredAttribute in descriptor.RequiredAttributes)
{
hashCodeCombiner.Add(requiredAttribute, StringComparer.Ordinal);
}
foreach (var attribute in descriptor.Attributes)
{
hashCodeCombiner.Add(CaseSensitiveAttributeDescriptorComparer.Default.GetHashCode(attribute));
}
return hashCodeCombiner.CombinedHash;
}
private class CaseSensitiveAttributeDescriptorComparer : IEqualityComparer<TagHelperAttributeDescriptor>
{
public static readonly CaseSensitiveAttributeDescriptorComparer Default =
new CaseSensitiveAttributeDescriptorComparer();
private CaseSensitiveAttributeDescriptorComparer()
{
}
public bool Equals(TagHelperAttributeDescriptor descriptorX, TagHelperAttributeDescriptor descriptorY)
{
return
// Normal comparer doesn't care about case, in tests we do.
string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.Ordinal) &&
string.Equals(descriptorX.PropertyName, descriptorY.PropertyName, StringComparison.Ordinal) &&
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal);
}
public int GetHashCode(TagHelperAttributeDescriptor descriptor)
{
return HashCodeCombiner
.Start()
.Add(descriptor.Name, StringComparer.Ordinal)
.Add(descriptor.PropertyName, StringComparer.Ordinal)
.Add(descriptor.TypeName, StringComparer.Ordinal)
.CombinedHash;
}
}
}
}

View File

@ -1,64 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
public class CompleteTagHelperDescriptorComparer : TagHelperDescriptorComparer, IEqualityComparer<TagHelperDescriptor>
{
public new static readonly CompleteTagHelperDescriptorComparer Default =
new CompleteTagHelperDescriptorComparer();
private CompleteTagHelperDescriptorComparer()
{
}
bool IEqualityComparer<TagHelperDescriptor>.Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
{
return base.Equals(descriptorX, descriptorY) &&
// Tests should be exact casing
string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.Ordinal) &&
descriptorX.Attributes.SequenceEqual(descriptorY.Attributes,
CompleteTagHelperAttributeDescriptorComparer.Default);
}
int IEqualityComparer<TagHelperDescriptor>.GetHashCode(TagHelperDescriptor descriptor)
{
return HashCodeCombiner.Start()
.Add(base.GetHashCode())
.Add(descriptor.Attributes)
.CombinedHash;
}
private class CompleteTagHelperAttributeDescriptorComparer : IEqualityComparer<TagHelperAttributeDescriptor>
{
public static readonly CompleteTagHelperAttributeDescriptorComparer Default =
new CompleteTagHelperAttributeDescriptorComparer();
private CompleteTagHelperAttributeDescriptorComparer()
{
}
public bool Equals(TagHelperAttributeDescriptor descriptorX, TagHelperAttributeDescriptor descriptorY)
{
return string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.Ordinal) &&
string.Equals(descriptorX.PropertyName, descriptorY.PropertyName, StringComparison.Ordinal) &&
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal);
}
public int GetHashCode(TagHelperAttributeDescriptor descriptor)
{
return HashCodeCombiner.Start()
.Add(descriptor.Name, StringComparer.Ordinal)
.Add(descriptor.PropertyName, StringComparer.Ordinal)
.Add(descriptor.TypeName, StringComparer.Ordinal)
.CombinedHash;
}
}
}
}

View File

@ -1,71 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Xunit;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
public class HtmlElementNameAttributeTest
{
public static TheoryData InvalidTagNameData
{
get
{
var invalidTagNameError =
"Tag helpers cannot target element name '{0}' because it contains a '{1}' character.";
var nullOrWhitespaceTagNameError =
"Tag name cannot be null or whitespace.";
// tagName, expectedExceptionMessage
return new TheoryData<string, string>
{
{ "!", string.Format(invalidTagNameError, "!", "!") },
{ "hello!", string.Format(invalidTagNameError, "hello!", "!") },
{ "!hello", string.Format(invalidTagNameError, "!hello", "!") },
{ "he!lo", string.Format(invalidTagNameError, "he!lo", "!") },
{ "!he!lo!", string.Format(invalidTagNameError, "!he!lo!", "!") },
{ string.Empty, nullOrWhitespaceTagNameError },
{ Environment.NewLine, nullOrWhitespaceTagNameError },
{ "\t", nullOrWhitespaceTagNameError },
{ " \t ", nullOrWhitespaceTagNameError },
{ " ", nullOrWhitespaceTagNameError },
{ Environment.NewLine + " ", nullOrWhitespaceTagNameError },
{ null, nullOrWhitespaceTagNameError },
};
}
}
[Theory]
[MemberData(nameof(InvalidTagNameData))]
public void SingleArgumentConstructor_ThrowsOnInvalidTagNames(
string tagName,
string expectedExceptionMessage)
{
// Arrange
expectedExceptionMessage += Environment.NewLine + "Parameter name: tag";
// Act & Assert
var exception = Assert.Throws<ArgumentException>(
"tag",
() => new HtmlElementNameAttribute(tagName));
Assert.Equal(exception.Message, expectedExceptionMessage);
}
[Theory]
[MemberData(nameof(InvalidTagNameData))]
public void MultipleArgumentConstructor_ThrowsOnInvalidTagNames(
string tagName,
string expectedExceptionMessage)
{
// Arrange
expectedExceptionMessage += Environment.NewLine + "Parameter name: additionalTags";
// Act & Assert
var exception = Assert.Throws<ArgumentException>(
"additionalTags",
() => new HtmlElementNameAttribute("p", "div", "span", tagName));
Assert.Equal(exception.Message, expectedExceptionMessage);
}
}
}

View File

@ -2,8 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.AspNet.Razor.Text;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
@ -13,6 +17,183 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
private static readonly string AssemblyName = private static readonly string AssemblyName =
typeof(TagHelperDescriptorFactoryTest).GetTypeInfo().Assembly.GetName().Name; typeof(TagHelperDescriptorFactoryTest).GetTypeInfo().Assembly.GetName().Name;
public static TheoryData AttributeTargetData
{
get
{
var attributes = Enumerable.Empty<TagHelperAttributeDescriptor>();
// tagHelperType, expectedDescriptors
return new TheoryData<Type, IEnumerable<TagHelperDescriptor>>
{
{
typeof(AttributeTargetingTagHelper),
new[]
{
new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
typeof(AttributeTargetingTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class" })
}
},
{
typeof(MultiAttributeTargetingTagHelper),
new[]
{
new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
typeof(MultiAttributeTargetingTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class", "style" })
}
},
{
typeof(MultiAttributeAttributeTargetingTagHelper),
new[]
{
new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
typeof(MultiAttributeAttributeTargetingTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "custom" }),
new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
typeof(MultiAttributeAttributeTargetingTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class", "style" })
}
},
{
typeof(InheritedAttributeTargetingTagHelper),
new[]
{
new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
typeof(InheritedAttributeTargetingTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "style" })
}
},
{
typeof(RequiredAttributeTagHelper),
new[]
{
new TagHelperDescriptor(
"input",
typeof(RequiredAttributeTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class" })
}
},
{
typeof(InheritedRequiredAttributeTagHelper),
new[]
{
new TagHelperDescriptor(
"div",
typeof(InheritedRequiredAttributeTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class" })
}
},
{
typeof(MultiAttributeRequiredAttributeTagHelper),
new[]
{
new TagHelperDescriptor(
"div",
typeof(MultiAttributeRequiredAttributeTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class" }),
new TagHelperDescriptor(
"input",
typeof(MultiAttributeRequiredAttributeTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class" })
}
},
{
typeof(MultiAttributeSameTagRequiredAttributeTagHelper),
new[]
{
new TagHelperDescriptor(
"input",
typeof(MultiAttributeSameTagRequiredAttributeTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "style" }),
new TagHelperDescriptor(
"input",
typeof(MultiAttributeSameTagRequiredAttributeTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class" })
}
},
{
typeof(MultiRequiredAttributeTagHelper),
new[]
{
new TagHelperDescriptor(
"input",
typeof(MultiRequiredAttributeTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class", "style" })
}
},
{
typeof(MultiTagMultiRequiredAttributeTagHelper),
new[]
{
new TagHelperDescriptor(
"div",
typeof(MultiTagMultiRequiredAttributeTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class", "style" }),
new TagHelperDescriptor(
"input",
typeof(MultiTagMultiRequiredAttributeTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class", "style" }),
}
},
};
}
}
[Theory]
[MemberData(nameof(AttributeTargetData))]
public void CreateDescriptors_ReturnsExpectedDescriptors(
Type tagHelperType,
IEnumerable<TagHelperDescriptor> expectedDescriptors)
{
// Arrange
var errorSink = new ParserErrorSink();
// Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName,
tagHelperType,
errorSink);
// Assert
Assert.Empty(errorSink.Errors);
Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
}
public static TheoryData HtmlCaseData public static TheoryData HtmlCaseData
{ {
get get
@ -39,10 +220,17 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
string expectedTagName, string expectedTagName,
string expectedAttributeName) string expectedAttributeName)
{ {
// Arrange & Act // Arrange
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, tagHelperType); var errorSink = new ParserErrorSink();
// Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName,
tagHelperType,
errorSink);
// Assert // Assert
Assert.Empty(errorSink.Errors);
var descriptor = Assert.Single(descriptors); var descriptor = Assert.Single(descriptors);
Assert.Equal(expectedTagName, descriptor.TagName, StringComparer.Ordinal); Assert.Equal(expectedTagName, descriptor.TagName, StringComparer.Ordinal);
var attributeDescriptor = Assert.Single(descriptor.Attributes); var attributeDescriptor = Assert.Single(descriptor.Attributes);
@ -53,6 +241,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
public void CreateDescriptor_OverridesAttributeNameFromAttribute() public void CreateDescriptor_OverridesAttributeNameFromAttribute()
{ {
// Arrange // Arrange
var errorSink = new ParserErrorSink();
var validProperty1 = typeof(OverriddenAttributeTagHelper).GetProperty( var validProperty1 = typeof(OverriddenAttributeTagHelper).GetProperty(
nameof(OverriddenAttributeTagHelper.ValidAttribute1)); nameof(OverriddenAttributeTagHelper.ValidAttribute1));
var validProperty2 = typeof(OverriddenAttributeTagHelper).GetProperty( var validProperty2 = typeof(OverriddenAttributeTagHelper).GetProperty(
@ -69,17 +258,21 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}; };
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
typeof(OverriddenAttributeTagHelper)); AssemblyName,
typeof(OverriddenAttributeTagHelper),
errorSink);
// Assert // Assert
Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default); Assert.Empty(errorSink.Errors);
Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
public void CreateDescriptor_DoesNotInheritOverridenAttributeName() public void CreateDescriptor_DoesNotInheritOverridenAttributeName()
{ {
// Arrange // Arrange
var errorSink = new ParserErrorSink();
var validProperty1 = typeof(InheritedOverriddenAttributeTagHelper).GetProperty( var validProperty1 = typeof(InheritedOverriddenAttributeTagHelper).GetProperty(
nameof(InheritedOverriddenAttributeTagHelper.ValidAttribute1)); nameof(InheritedOverriddenAttributeTagHelper.ValidAttribute1));
var validProperty2 = typeof(InheritedOverriddenAttributeTagHelper).GetProperty( var validProperty2 = typeof(InheritedOverriddenAttributeTagHelper).GetProperty(
@ -97,17 +290,21 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}; };
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
typeof(InheritedOverriddenAttributeTagHelper)); AssemblyName,
typeof(InheritedOverriddenAttributeTagHelper),
errorSink);
// Assert // Assert
Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default); Assert.Empty(errorSink.Errors);
Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
public void CreateDescriptor_AllowsOverridenAttributeNameOnUnimplementedVirtual() public void CreateDescriptor_AllowsOverridenAttributeNameOnUnimplementedVirtual()
{ {
// Arrange // Arrange
var errorSink = new ParserErrorSink();
var validProperty1 = typeof(InheritedNotOverriddenAttributeTagHelper).GetProperty( var validProperty1 = typeof(InheritedNotOverriddenAttributeTagHelper).GetProperty(
nameof(InheritedNotOverriddenAttributeTagHelper.ValidAttribute1)); nameof(InheritedNotOverriddenAttributeTagHelper.ValidAttribute1));
var validProperty2 = typeof(InheritedNotOverriddenAttributeTagHelper).GetProperty( var validProperty2 = typeof(InheritedNotOverriddenAttributeTagHelper).GetProperty(
@ -124,33 +321,42 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}; };
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
typeof(InheritedNotOverriddenAttributeTagHelper)); AssemblyName,
typeof(InheritedNotOverriddenAttributeTagHelper),
errorSink);
// Assert // Assert
Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default); Assert.Empty(errorSink.Errors);
Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
public void CreateDescriptor_BuildsDescriptorsFromSimpleTypes() public void CreateDescriptor_BuildsDescriptorsFromSimpleTypes()
{ {
// Arrange // Arrange
var errorSink = new ParserErrorSink();
var objectAssemblyName = typeof(object).GetTypeInfo().Assembly.GetName().Name; var objectAssemblyName = typeof(object).GetTypeInfo().Assembly.GetName().Name;
var expectedDescriptor = var expectedDescriptor =
new TagHelperDescriptor("object", "System.Object", objectAssemblyName); new TagHelperDescriptor("object", "System.Object", objectAssemblyName);
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(objectAssemblyName, typeof(object)); var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
objectAssemblyName,
typeof(object),
errorSink);
// Assert // Assert
Assert.Empty(errorSink.Errors);
var descriptor = Assert.Single(descriptors); var descriptor = Assert.Single(descriptors);
Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default); Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
public void CreateDescriptor_BuildsDescriptorsWithInheritedProperties() public void CreateDescriptor_BuildsDescriptorsWithInheritedProperties()
{ {
// Arrange // Arrange
var errorSink = new ParserErrorSink();
var intProperty = typeof(InheritedSingleAttributeTagHelper).GetProperty( var intProperty = typeof(InheritedSingleAttributeTagHelper).GetProperty(
nameof(InheritedSingleAttributeTagHelper.IntAttribute)); nameof(InheritedSingleAttributeTagHelper.IntAttribute));
var expectedDescriptor = new TagHelperDescriptor( var expectedDescriptor = new TagHelperDescriptor(
@ -162,18 +368,22 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}); });
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
typeof(InheritedSingleAttributeTagHelper)); AssemblyName,
typeof(InheritedSingleAttributeTagHelper),
errorSink);
// Assert // Assert
Assert.Empty(errorSink.Errors);
var descriptor = Assert.Single(descriptors); var descriptor = Assert.Single(descriptors);
Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default); Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
public void CreateDescriptor_BuildsDescriptorsWithConventionNames() public void CreateDescriptor_BuildsDescriptorsWithConventionNames()
{ {
// Arrange // Arrange
var errorSink = new ParserErrorSink();
var intProperty = typeof(SingleAttributeTagHelper).GetProperty(nameof(SingleAttributeTagHelper.IntAttribute)); var intProperty = typeof(SingleAttributeTagHelper).GetProperty(nameof(SingleAttributeTagHelper.IntAttribute));
var expectedDescriptor = new TagHelperDescriptor( var expectedDescriptor = new TagHelperDescriptor(
"single-attribute", "single-attribute",
@ -184,18 +394,22 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}); });
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
typeof(SingleAttributeTagHelper)); AssemblyName,
typeof(SingleAttributeTagHelper),
new ParserErrorSink());
// Assert // Assert
Assert.Empty(errorSink.Errors);
var descriptor = Assert.Single(descriptors); var descriptor = Assert.Single(descriptors);
Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default); Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
public void CreateDescriptor_OnlyAcceptsPropertiesWithGetAndSet() public void CreateDescriptor_OnlyAcceptsPropertiesWithGetAndSet()
{ {
// Arrange // Arrange
var errorSink = new ParserErrorSink();
var validProperty = typeof(MissingAccessorTagHelper).GetProperty( var validProperty = typeof(MissingAccessorTagHelper).GetProperty(
nameof(MissingAccessorTagHelper.ValidAttribute)); nameof(MissingAccessorTagHelper.ValidAttribute));
var expectedDescriptor = new TagHelperDescriptor( var expectedDescriptor = new TagHelperDescriptor(
@ -207,18 +421,22 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}); });
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
typeof(MissingAccessorTagHelper)); AssemblyName,
typeof(MissingAccessorTagHelper),
errorSink);
// Assert // Assert
Assert.Empty(errorSink.Errors);
var descriptor = Assert.Single(descriptors); var descriptor = Assert.Single(descriptors);
Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default); Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
public void CreateDescriptor_OnlyAcceptsPropertiesWithPublicGetAndSet() public void CreateDescriptor_OnlyAcceptsPropertiesWithPublicGetAndSet()
{ {
// Arrange // Arrange
var errorSink = new ParserErrorSink();
var validProperty = typeof(PrivateAccessorTagHelper).GetProperty( var validProperty = typeof(PrivateAccessorTagHelper).GetProperty(
nameof(PrivateAccessorTagHelper.ValidAttribute)); nameof(PrivateAccessorTagHelper.ValidAttribute));
var expectedDescriptor = new TagHelperDescriptor( var expectedDescriptor = new TagHelperDescriptor(
@ -231,29 +449,33 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}); });
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
typeof(PrivateAccessorTagHelper)); AssemblyName,
typeof(PrivateAccessorTagHelper),
errorSink);
// Assert // Assert
Assert.Empty(errorSink.Errors);
var descriptor = Assert.Single(descriptors); var descriptor = Assert.Single(descriptors);
Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default); Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
public void CreateDescriptor_ResolvesMultipleTagHelperDescriptorsFromSingleType() public void CreateDescriptor_ResolvesMultipleTagHelperDescriptorsFromSingleType()
{ {
// Arrange // Arrange
var errorSink = new ParserErrorSink();
var validProp = typeof(MultiTagTagHelper).GetProperty(nameof(MultiTagTagHelper.ValidAttribute)); var validProp = typeof(MultiTagTagHelper).GetProperty(nameof(MultiTagTagHelper.ValidAttribute));
var expectedDescriptors = new[] { var expectedDescriptors = new[] {
new TagHelperDescriptor( new TagHelperDescriptor(
"div", "p",
typeof(MultiTagTagHelper).FullName, typeof(MultiTagTagHelper).FullName,
AssemblyName, AssemblyName,
new[] { new[] {
new TagHelperAttributeDescriptor("valid-attribute", validProp) new TagHelperAttributeDescriptor("valid-attribute", validProp)
}), }),
new TagHelperDescriptor( new TagHelperDescriptor(
"p", "div",
typeof(MultiTagTagHelper).FullName, typeof(MultiTagTagHelper).FullName,
AssemblyName, AssemblyName,
new[] { new[] {
@ -262,16 +484,21 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}; };
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, typeof(MultiTagTagHelper)); var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName,
typeof(MultiTagTagHelper),
errorSink);
// Assert // Assert
Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default); Assert.Empty(errorSink.Errors);
Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
public void CreateDescriptor_DoesntResolveInheritedTagNames() public void CreateDescriptor_DoesntResolveInheritedTagNames()
{ {
// Arrange // Arrange
var errorSink = new ParserErrorSink();
var validProp = typeof(InheritedMultiTagTagHelper).GetProperty(nameof(InheritedMultiTagTagHelper.ValidAttribute)); var validProp = typeof(InheritedMultiTagTagHelper).GetProperty(nameof(InheritedMultiTagTagHelper.ValidAttribute));
var expectedDescriptor = new TagHelperDescriptor( var expectedDescriptor = new TagHelperDescriptor(
"inherited-multi-tag", "inherited-multi-tag",
@ -282,18 +509,22 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}); });
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
typeof(InheritedMultiTagTagHelper)); AssemblyName,
typeof(InheritedMultiTagTagHelper),
errorSink);
// Assert // Assert
Assert.Empty(errorSink.Errors);
var descriptor = Assert.Single(descriptors); var descriptor = Assert.Single(descriptors);
Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default); Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
public void CreateDescriptor_IgnoresDuplicateTagNamesFromAttribute() public void CreateDescriptor_IgnoresDuplicateTagNamesFromAttribute()
{ {
// Arrange // Arrange
var errorSink = new ParserErrorSink();
var expectedDescriptors = new[] { var expectedDescriptors = new[] {
new TagHelperDescriptor( new TagHelperDescriptor(
"p", "p",
@ -306,17 +537,21 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}; };
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
typeof(DuplicateTagNameTagHelper)); AssemblyName,
typeof(DuplicateTagNameTagHelper),
errorSink);
// Assert // Assert
Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default); Assert.Empty(errorSink.Errors);
Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
public void CreateDescriptor_OverridesTagNameFromAttribute() public void CreateDescriptor_OverridesTagNameFromAttribute()
{ {
// Arrange // Arrange
var errorSink = new ParserErrorSink();
var expectedDescriptors = new[] { var expectedDescriptors = new[] {
new TagHelperDescriptor("data-condition", new TagHelperDescriptor("data-condition",
typeof(OverrideNameTagHelper).FullName, typeof(OverrideNameTagHelper).FullName,
@ -324,41 +559,353 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}; };
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
typeof(OverrideNameTagHelper)); AssemblyName,
typeof(OverrideNameTagHelper),
errorSink);
// Assert // Assert
Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default); Assert.Empty(errorSink.Errors);
Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
}
public static TheoryData InvalidNameData
{
get
{
var invalidNameError =
"Tag helpers cannot target {0} name '{1}' because it contains a '{2}' character.";
var nullOrWhitespaceNameError =
"{0} name cannot be null or whitespace.";
Func<string, string, string> onNameError = (invalidText, invalidCharacter) =>
string.Format(invalidNameError, "tag", invalidText, invalidCharacter);
// name, expectedErrorMessages
return new TheoryData<string, string[]>
{
{ "!", new[] { onNameError("!", "!") } },
{ "hello!", new[] { onNameError("hello!", "!") } },
{ "!hello", new[] { onNameError("!hello", "!") } },
{ "he!lo", new[] { onNameError("he!lo", "!") } },
{
"!he!lo!",
new[]
{
onNameError("!he!lo!", "!"),
onNameError("!he!lo!", "!"),
onNameError("!he!lo!", "!")
}
},
{ "@", new[] { onNameError("@", "@") } },
{ "hello@", new[] { onNameError("hello@", "@") } },
{ "@hello", new[] { onNameError("@hello", "@") } },
{ "he@lo", new[] { onNameError("he@lo", "@") } },
{
"@he@lo@",
new[]
{
onNameError("@he@lo@", "@"),
onNameError("@he@lo@", "@"),
onNameError("@he@lo@", "@")
}
},
{ "/", new[] { onNameError("/", "/") } },
{ "hello/", new[] { onNameError("hello/", "/") } },
{ "/hello", new[] { onNameError("/hello", "/") } },
{ "he/lo", new[] { onNameError("he/lo", "/") } },
{
"/he/lo/",
new[] {
onNameError("/he/lo/", "/"),
onNameError("/he/lo/", "/"),
onNameError("/he/lo/", "/")
}
},
{ "<", new[] { onNameError("<", "<") } },
{ "hello<", new[] { onNameError("hello<", "<") } },
{ "<hello", new[] { onNameError("<hello", "<") } },
{ "he<lo", new[] { onNameError("he<lo", "<") } },
{
"<he<lo<",
new[]
{
onNameError("<he<lo<", "<"),
onNameError("<he<lo<", "<"),
onNameError("<he<lo<", "<")
}
},
{ "?", new[] { onNameError("?", "?") } },
{ "hello?", new[] { onNameError("hello?", "?") } },
{ "?hello", new[] { onNameError("?hello", "?") } },
{ "he?lo", new[] { onNameError("he?lo", "?") } },
{
"?he?lo?",
new[]
{
onNameError("?he?lo?", "?"),
onNameError("?he?lo?", "?"),
onNameError("?he?lo?", "?")
}
},
{ "[", new[] { onNameError("[", "[") } },
{ "hello[", new[] { onNameError("hello[", "[") } },
{ "[hello", new[] { onNameError("[hello", "[") } },
{ "he[lo", new[] { onNameError("he[lo", "[") } },
{
"[he[lo[",
new[]
{
onNameError("[he[lo[", "["),
onNameError("[he[lo[", "["),
onNameError("[he[lo[", "[")
}
},
{ ">", new[] { onNameError(">", ">") } },
{ "hello>", new[] { onNameError("hello>", ">") } },
{ ">hello", new[] { onNameError(">hello", ">") } },
{ "he>lo", new[] { onNameError("he>lo", ">") } },
{
">he>lo>",
new[]
{
onNameError(">he>lo>", ">"),
onNameError(">he>lo>", ">"),
onNameError(">he>lo>", ">")
}
},
{ "]", new[] { onNameError("]", "]") } },
{ "hello]", new[] { onNameError("hello]", "]") } },
{ "]hello", new[] { onNameError("]hello", "]") } },
{ "he]lo", new[] { onNameError("he]lo", "]") } },
{
"]he]lo]",
new[]
{
onNameError("]he]lo]", "]"),
onNameError("]he]lo]", "]"),
onNameError("]he]lo]", "]")
}
},
{ "=", new[] { onNameError("=", "=") } },
{ "hello=", new[] { onNameError("hello=", "=") } },
{ "=hello", new[] { onNameError("=hello", "=") } },
{ "he=lo", new[] { onNameError("he=lo", "=") } },
{
"=he=lo=",
new[]
{
onNameError("=he=lo=", "="),
onNameError("=he=lo=", "="),
onNameError("=he=lo=", "=")
}
},
{ "\"", new[] { onNameError("\"", "\"") } },
{ "hello\"", new[] { onNameError("hello\"", "\"") } },
{ "\"hello", new[] { onNameError("\"hello", "\"") } },
{ "he\"lo", new[] { onNameError("he\"lo", "\"") } },
{
"\"he\"lo\"",
new[]
{
onNameError("\"he\"lo\"", "\""),
onNameError("\"he\"lo\"", "\""),
onNameError("\"he\"lo\"", "\"")
}
},
{ "'", new[] { onNameError("'", "'") } },
{ "hello'", new[] { onNameError("hello'", "'") } },
{ "'hello", new[] { onNameError("'hello", "'") } },
{ "he'lo", new[] { onNameError("he'lo", "'") } },
{
"'he'lo'",
new[]
{
onNameError("'he'lo'", "'"),
onNameError("'he'lo'", "'"),
onNameError("'he'lo'", "'")
}
},
{ string.Empty, new[] { string.Format(nullOrWhitespaceNameError, "Tag") } },
{ Environment.NewLine, new[] { string.Format(nullOrWhitespaceNameError, "Tag") } },
{ "\t", new[] { string.Format(nullOrWhitespaceNameError, "Tag") } },
{ " \t ", new[] { string.Format(nullOrWhitespaceNameError, "Tag") } },
{ " ", new[] { string.Format(nullOrWhitespaceNameError, "Tag") } },
{ Environment.NewLine + " ", new[] { string.Format(nullOrWhitespaceNameError, "Tag") } },
{
"! \t\r\n@/<>?[]=\"'",
new[]
{
onNameError("! \t\r\n@/<>?[]=\"'", "!"),
onNameError("! \t\r\n@/<>?[]=\"'", " "),
onNameError("! \t\r\n@/<>?[]=\"'", "\t"),
onNameError("! \t\r\n@/<>?[]=\"'", "\r"),
onNameError("! \t\r\n@/<>?[]=\"'", "\n"),
onNameError("! \t\r\n@/<>?[]=\"'", "@"),
onNameError("! \t\r\n@/<>?[]=\"'", "/"),
onNameError("! \t\r\n@/<>?[]=\"'", "<"),
onNameError("! \t\r\n@/<>?[]=\"'", ">"),
onNameError("! \t\r\n@/<>?[]=\"'", "?"),
onNameError("! \t\r\n@/<>?[]=\"'", "["),
onNameError("! \t\r\n@/<>?[]=\"'", "]"),
onNameError("! \t\r\n@/<>?[]=\"'", "="),
onNameError("! \t\r\n@/<>?[]=\"'", "\""),
onNameError("! \t\r\n@/<>?[]=\"'", "'"),
}
},
{
"! \tv\ra\nl@i/d<>?[]=\"'",
new[]
{
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "!"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", " "),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "\t"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "\r"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "\n"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "@"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "/"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "<"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", ">"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "?"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "["),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "]"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "="),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "\""),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "'"),
}
},
};
}
}
[Theory]
[MemberData(nameof(InvalidNameData))]
public void ValidTargetElementAttributeNames_CreatesErrorOnInvalidNames(
string name, string[] expectedErrorMessages)
{
// Arrange
var errorSink = new ParserErrorSink();
var attribute = new TargetElementAttribute(name);
// Act
TagHelperDescriptorFactory.ValidTargetElementAttributeNames(attribute, errorSink);
// Assert
var errors = errorSink.Errors.ToArray();
for (var i = 0; i < errors.Length; i++)
{
Assert.Equal(expectedErrorMessages[i], errors[i].Message);
Assert.Equal(SourceLocation.Zero, errors[i].Location);
}
}
public static TheoryData ValidNameData
{
get
{
// name, expectedNames
return new TheoryData<string, IEnumerable<string>>
{
{ "p", new[] { "p" } },
{ " p", new[] { "p" } },
{ "p ", new[] { "p" } },
{ " p ", new[] { "p" } },
{ "p,div", new[] { "p", "div" } },
{ " p,div", new[] { "p", "div" } },
{ "p ,div", new[] { "p", "div" } },
{ " p ,div", new[] { "p", "div" } },
{ "p, div", new[] { "p", "div" } },
{ "p,div ", new[] { "p", "div" } },
{ "p, div ", new[] { "p", "div" } },
{ " p, div ", new[] { "p", "div" } },
{ " p , div ", new[] { "p", "div" } },
};
}
}
[Theory]
[MemberData(nameof(ValidNameData))]
public void GetCommaSeparatedValues_OutputsCommaSeparatedListOfNames(
string name,
IEnumerable<string> expectedNames)
{
// Act
var result = TagHelperDescriptorFactory.GetCommaSeparatedValues(name);
// Assert
Assert.Equal(expectedNames, result);
} }
[Fact] [Fact]
public void CreateDescriptor_GetsTagNamesFromMultipleAttributes() public void GetCommaSeparatedValues_OutputsEmptyArrayForNullValue()
{ {
// Arrange
var expectedDescriptors = new[] {
new TagHelperDescriptor(
"span",
typeof(MultipleAttributeTagHelper).FullName,
AssemblyName),
new TagHelperDescriptor(
"p",
typeof(MultipleAttributeTagHelper).FullName,
AssemblyName),
new TagHelperDescriptor(
"div",
typeof(MultipleAttributeTagHelper).FullName,
AssemblyName)
};
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(AssemblyName, var result = TagHelperDescriptorFactory.GetCommaSeparatedValues(text: null);
typeof(MultipleAttributeTagHelper));
// Assert // Assert
Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default); Assert.Empty(result);
} }
[HtmlElementName("p", "div")] [TargetElement(Attributes = "class")]
private class AttributeTargetingTagHelper : TagHelper
{
}
[TargetElement(Attributes = "class,style")]
private class MultiAttributeTargetingTagHelper : TagHelper
{
}
[TargetElement(Attributes = "custom")]
[TargetElement(Attributes = "class,style")]
private class MultiAttributeAttributeTargetingTagHelper : TagHelper
{
}
[TargetElement(Attributes = "style")]
private class InheritedAttributeTargetingTagHelper : AttributeTargetingTagHelper
{
}
[TargetElement("input", Attributes = "class")]
private class RequiredAttributeTagHelper : TagHelper
{
}
[TargetElement("div", Attributes = "class")]
private class InheritedRequiredAttributeTagHelper : RequiredAttributeTagHelper
{
}
[TargetElement("div", Attributes = "class")]
[TargetElement("input", Attributes = "class")]
private class MultiAttributeRequiredAttributeTagHelper : TagHelper
{
}
[TargetElement("input", Attributes = "style")]
[TargetElement("input", Attributes = "class")]
private class MultiAttributeSameTagRequiredAttributeTagHelper : TagHelper
{
}
[TargetElement("input", Attributes = "class,style")]
private class MultiRequiredAttributeTagHelper : TagHelper
{
}
[TargetElement("div", Attributes = "style")]
private class InheritedMultiRequiredAttributeTagHelper : MultiRequiredAttributeTagHelper
{
}
[TargetElement("div", Attributes = "class,style")]
[TargetElement("input", Attributes = "class,style")]
private class MultiTagMultiRequiredAttributeTagHelper : TagHelper
{
}
[TargetElement("p")]
[TargetElement("div")]
private class MultiTagTagHelper private class MultiTagTagHelper
{ {
public string ValidAttribute { get; set; } public string ValidAttribute { get; set; }
@ -368,22 +915,19 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {
} }
[HtmlElementName("p", "p", "div", "div")] [TargetElement("p")]
[TargetElement("p")]
[TargetElement("div")]
[TargetElement("div")]
private class DuplicateTagNameTagHelper private class DuplicateTagNameTagHelper
{ {
} }
[HtmlElementName("data-condition")] [TargetElement("data-condition")]
private class OverrideNameTagHelper private class OverrideNameTagHelper
{ {
} }
[HtmlElementName("span")]
[HtmlElementName("div", "p")]
private class MultipleAttributeTagHelper
{
}
private class InheritedSingleAttributeTagHelper : SingleAttributeTagHelper private class InheritedSingleAttributeTagHelper : SingleAttributeTagHelper
{ {
} }

View File

@ -1241,7 +1241,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Assert // Assert
var descriptor = Assert.Single(descriptors); var descriptor = Assert.Single(descriptors);
Assert.Equal(Valid_PlainTagHelperDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default); Assert.Equal(Valid_PlainTagHelperDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
@ -1265,7 +1265,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Assert // Assert
Assert.Equal(descriptors.Length, 2); Assert.Equal(descriptors.Length, 2);
Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default); Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
} }
[Fact] [Fact]
@ -1369,7 +1369,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
tagName, tagName,
typeName, typeName,
assemblyName, assemblyName,
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>()); attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: Enumerable.Empty<string>());
} }
private static TagHelperDescriptor CreatePrefixedValidPlainDescriptor(string prefix) private static TagHelperDescriptor CreatePrefixedValidPlainDescriptor(string prefix)

View File

@ -19,6 +19,49 @@ namespace Microsoft.AspNet.Razor.Test.Generator
private static IEnumerable<TagHelperDescriptor> PrefixedPAndInputTagHelperDescriptors private static IEnumerable<TagHelperDescriptor> PrefixedPAndInputTagHelperDescriptors
=> BuildPAndInputTagHelperDescriptors("THS"); => BuildPAndInputTagHelperDescriptors("THS");
private static IEnumerable<TagHelperDescriptor> AttributeTargetingTagHelperDescriptors
{
get
{
var inputTypePropertyInfo = typeof(TestType).GetProperty("Type");
var inputCheckedPropertyInfo = typeof(TestType).GetProperty("Checked");
return new[]
{
new TagHelperDescriptor(
tagName: "p",
typeName: "PTagHelper",
assemblyName: "SomeAssembly",
attributes: new TagHelperAttributeDescriptor[0],
requiredAttributes: new[] { "class" }),
new TagHelperDescriptor(
tagName: "input",
typeName: "InputTagHelper",
assemblyName: "SomeAssembly",
attributes: new TagHelperAttributeDescriptor[]
{
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
},
requiredAttributes: new[] { "type" }),
new TagHelperDescriptor(
tagName: "input",
typeName: "InputTagHelper2",
assemblyName: "SomeAssembly",
attributes: new TagHelperAttributeDescriptor[]
{
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
new TagHelperAttributeDescriptor("checked", inputCheckedPropertyInfo)
},
requiredAttributes: new[] { "type", "checked" }),
new TagHelperDescriptor(
tagName: "*",
typeName: "CatchAllTagHelper",
assemblyName: "SomeAssembly",
attributes: new TagHelperAttributeDescriptor[0],
requiredAttributes: new[] { "catchAll" })
};
}
}
public static TheoryData TagHelperDescriptorFlowTestData public static TheoryData TagHelperDescriptorFlowTestData
{ {
get get
@ -91,6 +134,20 @@ namespace Microsoft.AspNet.Razor.Test.Generator
DefaultPAndInputTagHelperDescriptors, DefaultPAndInputTagHelperDescriptors,
DefaultPAndInputTagHelperDescriptors, DefaultPAndInputTagHelperDescriptors,
true true
},
{
"AttributeTargetingTagHelpers",
"AttributeTargetingTagHelpers",
AttributeTargetingTagHelperDescriptors,
AttributeTargetingTagHelperDescriptors,
false
},
{
"AttributeTargetingTagHelpers",
"AttributeTargetingTagHelpers.DesignTime",
AttributeTargetingTagHelperDescriptors,
AttributeTargetingTagHelperDescriptors,
true
} }
}; };
} }
@ -294,6 +351,33 @@ namespace Microsoft.AspNet.Razor.Test.Generator
contentLength: 4) contentLength: 4)
} }
}, },
{
"AttributeTargetingTagHelpers",
"AttributeTargetingTagHelpers.DesignTime",
AttributeTargetingTagHelperDescriptors,
new List<LineMapping>
{
BuildLineMapping(documentAbsoluteIndex: 14,
documentLineIndex: 0,
generatedAbsoluteIndex: 501,
generatedLineIndex: 15,
characterOffsetIndex: 14,
contentLength: 14),
BuildLineMapping(documentAbsoluteIndex: 186,
documentLineIndex: 5,
generatedAbsoluteIndex: 1460,
generatedLineIndex: 41,
characterOffsetIndex: 36,
contentLength: 4),
BuildLineMapping(documentAbsoluteIndex: 232,
documentLineIndex: 6,
documentCharacterOffsetIndex: 36,
generatedAbsoluteIndex: 1827,
generatedLineIndex: 50,
generatedCharacterOffsetIndex: 36,
contentLength: 4)
}
},
}; };
} }
} }
@ -328,6 +412,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
{ "ComplexTagHelpers", DefaultPAndInputTagHelperDescriptors }, { "ComplexTagHelpers", DefaultPAndInputTagHelperDescriptors },
{ "EmptyAttributeTagHelpers", DefaultPAndInputTagHelperDescriptors }, { "EmptyAttributeTagHelpers", DefaultPAndInputTagHelperDescriptors },
{ "EscapedTagHelpers", DefaultPAndInputTagHelperDescriptors }, { "EscapedTagHelpers", DefaultPAndInputTagHelperDescriptors },
{ "AttributeTargetingTagHelpers", AttributeTargetingTagHelperDescriptors },
}; };
} }
} }
@ -421,7 +506,8 @@ namespace Microsoft.AspNet.Razor.Test.Generator
assemblyName: "SomeAssembly", assemblyName: "SomeAssembly",
attributes: new [] { attributes: new [] {
new TagHelperAttributeDescriptor("age", pAgePropertyInfo) new TagHelperAttributeDescriptor("age", pAgePropertyInfo)
}), },
requiredAttributes: Enumerable.Empty<string>()),
new TagHelperDescriptor( new TagHelperDescriptor(
prefix, prefix,
tagName: "input", tagName: "input",
@ -429,7 +515,8 @@ namespace Microsoft.AspNet.Razor.Test.Generator
assemblyName: "SomeAssembly", assemblyName: "SomeAssembly",
attributes: new TagHelperAttributeDescriptor[] { attributes: new TagHelperAttributeDescriptor[] {
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo) new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
}), },
requiredAttributes: Enumerable.Empty<string>()),
new TagHelperDescriptor( new TagHelperDescriptor(
prefix, prefix,
tagName: "input", tagName: "input",
@ -438,7 +525,8 @@ namespace Microsoft.AspNet.Razor.Test.Generator
attributes: new TagHelperAttributeDescriptor[] { attributes: new TagHelperAttributeDescriptor[] {
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo), new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
new TagHelperAttributeDescriptor("checked", checkedPropertyInfo) new TagHelperAttributeDescriptor("checked", checkedPropertyInfo)
}) },
requiredAttributes: Enumerable.Empty<string>())
}; };
} }

View File

@ -1,31 +1,143 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNet.Razor.TagHelpers;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Razor.Test.TagHelpers namespace Microsoft.AspNet.Razor.TagHelpers
{ {
public class TagHelperDescriptorProviderTest public class TagHelperDescriptorProviderTest
{ {
[Fact] public static TheoryData RequiredAttributeData
public void GetTagHelpers_ReturnsEmptyDescriptorsWithPrefixAsTagName() {
get
{
var divDescriptor = new TagHelperDescriptor(
tagName: "div",
typeName: "DivTagHelper",
assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: new[] { "style" });
var inputDescriptor = new TagHelperDescriptor(
tagName: "input",
typeName: "InputTagHelper",
assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: new[] { "class", "style" });
var catchAllDescriptor = new TagHelperDescriptor(
tagName: TagHelperDescriptorProvider.CatchAllDescriptorTarget,
typeName: "CatchAllTagHelper",
assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: new[] { "class" });
var catchAllDescriptor2 = new TagHelperDescriptor(
tagName: TagHelperDescriptorProvider.CatchAllDescriptorTarget,
typeName: "CatchAllTagHelper",
assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: new[] { "custom", "class" });
var defaultAvailableDescriptors =
new[] { divDescriptor, inputDescriptor, catchAllDescriptor, catchAllDescriptor2 };
return new TheoryData<
string, // tagName
IEnumerable<string>, // providedAttributes
IEnumerable<TagHelperDescriptor>, // availableDescriptors
IEnumerable<TagHelperDescriptor>> // expectedDescriptors
{
{
"div",
new[] { "custom" },
defaultAvailableDescriptors,
Enumerable.Empty<TagHelperDescriptor>()
},
{ "div", new[] { "style" }, defaultAvailableDescriptors, new[] { divDescriptor } },
{ "div", new[] { "class" }, defaultAvailableDescriptors, new[] { catchAllDescriptor } },
{
"div",
new[] { "class", "style" },
defaultAvailableDescriptors,
new[] { divDescriptor, catchAllDescriptor }
},
{
"div",
new[] { "class", "style", "custom" },
defaultAvailableDescriptors,
new[] { divDescriptor, catchAllDescriptor, catchAllDescriptor2 }
},
{
"input",
new[] { "class", "style" },
defaultAvailableDescriptors,
new[] { inputDescriptor, catchAllDescriptor }
},
{
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
new[] { "custom" },
defaultAvailableDescriptors,
Enumerable.Empty<TagHelperDescriptor>()
},
{
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
new[] { "class" },
defaultAvailableDescriptors,
new[] { catchAllDescriptor }
},
{
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
new[] { "class", "style" },
defaultAvailableDescriptors,
new[] { catchAllDescriptor }
},
{
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
new[] { "class", "custom" },
defaultAvailableDescriptors,
new[] { catchAllDescriptor, catchAllDescriptor2 }
},
};
}
}
[Theory]
[MemberData(nameof(RequiredAttributeData))]
public void GetDescriptors_ReturnsDescriptorsWithRequiredAttributes(
string tagName,
IEnumerable<string> providedAttributes,
IEnumerable<TagHelperDescriptor> availableDescriptors,
IEnumerable<TagHelperDescriptor> expectedDescriptors)
{ {
// Arrange // Arrange
var catchAllDescriptor = CreatePrefixedDescriptor("th", "*", "foo1"); var provider = new TagHelperDescriptorProvider(availableDescriptors);
// Act
var resolvedDescriptors = provider.GetDescriptors(tagName, providedAttributes);
// Assert
Assert.Equal(expectedDescriptors, resolvedDescriptors, TagHelperDescriptorComparer.Default);
}
[Fact]
public void GetDescriptors_ReturnsEmptyDescriptorsWithPrefixAsTagName()
{
// Arrange
var catchAllDescriptor = CreatePrefixedDescriptor(
"th",
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
"foo1");
var descriptors = new[] { catchAllDescriptor }; var descriptors = new[] { catchAllDescriptor };
var provider = new TagHelperDescriptorProvider(descriptors); var provider = new TagHelperDescriptorProvider(descriptors);
// Act // Act
var resolvedDescriptors = provider.GetTagHelpers("th"); var resolvedDescriptors = provider.GetDescriptors("th", attributeNames: Enumerable.Empty<string>());
// Assert // Assert
Assert.Empty(resolvedDescriptors); Assert.Empty(resolvedDescriptors);
} }
[Fact] [Fact]
public void GetTagHelpers_OnlyUnderstandsSinglePrefix() public void GetDescriptors_OnlyUnderstandsSinglePrefix()
{ {
// Arrange // Arrange
var divDescriptor = CreatePrefixedDescriptor("th:", "div", "foo1"); var divDescriptor = CreatePrefixedDescriptor("th:", "div", "foo1");
@ -34,8 +146,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
var provider = new TagHelperDescriptorProvider(descriptors); var provider = new TagHelperDescriptorProvider(descriptors);
// Act // Act
var retrievedDescriptorsDiv = provider.GetTagHelpers("th:div"); var retrievedDescriptorsDiv = provider.GetDescriptors("th:div", attributeNames: Enumerable.Empty<string>());
var retrievedDescriptorsSpan = provider.GetTagHelpers("th2:span"); var retrievedDescriptorsSpan = provider.GetDescriptors("th2:span", attributeNames: Enumerable.Empty<string>());
// Assert // Assert
var descriptor = Assert.Single(retrievedDescriptorsDiv); var descriptor = Assert.Single(retrievedDescriptorsDiv);
@ -44,16 +156,16 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
} }
[Fact] [Fact]
public void GetTagHelpers_ReturnsCatchAllDescriptorsForPrefixedTags() public void GetDescriptors_ReturnsCatchAllDescriptorsForPrefixedTags()
{ {
// Arrange // Arrange
var catchAllDescriptor = CreatePrefixedDescriptor("th:", "*", "foo1"); var catchAllDescriptor = CreatePrefixedDescriptor("th:", TagHelperDescriptorProvider.CatchAllDescriptorTarget, "foo1");
var descriptors = new[] { catchAllDescriptor }; var descriptors = new[] { catchAllDescriptor };
var provider = new TagHelperDescriptorProvider(descriptors); var provider = new TagHelperDescriptorProvider(descriptors);
// Act // Act
var retrievedDescriptorsDiv = provider.GetTagHelpers("th:div"); var retrievedDescriptorsDiv = provider.GetDescriptors("th:div", attributeNames: Enumerable.Empty<string>());
var retrievedDescriptorsSpan = provider.GetTagHelpers("th:span"); var retrievedDescriptorsSpan = provider.GetDescriptors("th:span", attributeNames: Enumerable.Empty<string>());
// Assert // Assert
var descriptor = Assert.Single(retrievedDescriptorsDiv); var descriptor = Assert.Single(retrievedDescriptorsDiv);
@ -63,7 +175,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
} }
[Fact] [Fact]
public void GetTagHelpers_ReturnsDescriptorsForPrefixedTags() public void GetDescriptors_ReturnsDescriptorsForPrefixedTags()
{ {
// Arrange // Arrange
var divDescriptor = CreatePrefixedDescriptor("th:", "div", "foo1"); var divDescriptor = CreatePrefixedDescriptor("th:", "div", "foo1");
@ -71,7 +183,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
var provider = new TagHelperDescriptorProvider(descriptors); var provider = new TagHelperDescriptorProvider(descriptors);
// Act // Act
var retrievedDescriptors = provider.GetTagHelpers("th:div"); var retrievedDescriptors = provider.GetDescriptors("th:div", attributeNames: Enumerable.Empty<string>());
// Assert // Assert
var descriptor = Assert.Single(retrievedDescriptors); var descriptor = Assert.Single(retrievedDescriptors);
@ -81,7 +193,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
[Theory] [Theory]
[InlineData("*")] [InlineData("*")]
[InlineData("div")] [InlineData("div")]
public void GetTagHelpers_ReturnsNothingForUnprefixedTags(string tagName) public void GetDescriptors_ReturnsNothingForUnprefixedTags(string tagName)
{ {
// Arrange // Arrange
var divDescriptor = CreatePrefixedDescriptor("th:", tagName, "foo1"); var divDescriptor = CreatePrefixedDescriptor("th:", tagName, "foo1");
@ -89,14 +201,14 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
var provider = new TagHelperDescriptorProvider(descriptors); var provider = new TagHelperDescriptorProvider(descriptors);
// Act // Act
var retrievedDescriptorsDiv = provider.GetTagHelpers("div"); var retrievedDescriptorsDiv = provider.GetDescriptors("div", attributeNames: Enumerable.Empty<string>());
// Assert // Assert
Assert.Empty(retrievedDescriptorsDiv); Assert.Empty(retrievedDescriptorsDiv);
} }
[Fact] [Fact]
public void GetTagHelpers_ReturnsNothingForUnregisteredTags() public void GetDescriptors_ReturnsNothingForUnregisteredTags()
{ {
// Arrange // Arrange
var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly"); var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly");
@ -105,24 +217,27 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
var provider = new TagHelperDescriptorProvider(descriptors); var provider = new TagHelperDescriptorProvider(descriptors);
// Act // Act
var retrievedDescriptors = provider.GetTagHelpers("foo"); var retrievedDescriptors = provider.GetDescriptors("foo", attributeNames: Enumerable.Empty<string>());
// Assert // Assert
Assert.Empty(retrievedDescriptors); Assert.Empty(retrievedDescriptors);
} }
[Fact] [Fact]
public void GetTagHelpers_DoesNotReturnNonCatchAllTagsForCatchAll() public void GetDescriptors_DoesNotReturnNonCatchAllTagsForCatchAll()
{ {
// Arrange // Arrange
var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly"); var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly");
var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly"); var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly");
var catchAllDescriptor = new TagHelperDescriptor("*", "foo3", "SomeAssembly"); var catchAllDescriptor = new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
"foo3",
"SomeAssembly");
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor }; var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };
var provider = new TagHelperDescriptorProvider(descriptors); var provider = new TagHelperDescriptorProvider(descriptors);
// Act // Act
var retrievedDescriptors = provider.GetTagHelpers("*"); var retrievedDescriptors = provider.GetDescriptors(TagHelperDescriptorProvider.CatchAllDescriptorTarget, attributeNames: Enumerable.Empty<string>());
// Assert // Assert
var descriptor = Assert.Single(retrievedDescriptors); var descriptor = Assert.Single(retrievedDescriptors);
@ -130,18 +245,21 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
} }
[Fact] [Fact]
public void GetTagHelpers_ReturnsCatchAllsWithEveryTagName() public void GetDescriptors_ReturnsCatchAllsWithEveryTagName()
{ {
// Arrange // Arrange
var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly"); var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly");
var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly"); var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly");
var catchAllDescriptor = new TagHelperDescriptor("*", "foo3", "SomeAssembly"); var catchAllDescriptor = new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
"foo3",
"SomeAssembly");
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor }; var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };
var provider = new TagHelperDescriptorProvider(descriptors); var provider = new TagHelperDescriptorProvider(descriptors);
// Act // Act
var divDescriptors = provider.GetTagHelpers("div"); var divDescriptors = provider.GetDescriptors("div", attributeNames: Enumerable.Empty<string>());
var spanDescriptors = provider.GetTagHelpers("span"); var spanDescriptors = provider.GetDescriptors("span", attributeNames: Enumerable.Empty<string>());
// Assert // Assert
// For divs // For divs
@ -156,7 +274,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
} }
[Fact] [Fact]
public void GetTagHelpers_DuplicateDescriptorsAreNotPartOfTagHelperDescriptorPool() public void GetDescriptors_DuplicateDescriptorsAreNotPartOfTagHelperDescriptorPool()
{ {
// Arrange // Arrange
var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly"); var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly");
@ -164,7 +282,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
var provider = new TagHelperDescriptorProvider(descriptors); var provider = new TagHelperDescriptorProvider(descriptors);
// Act // Act
var retrievedDescriptors = provider.GetTagHelpers("div"); var retrievedDescriptors = provider.GetDescriptors("div", attributeNames: Enumerable.Empty<string>());
// Assert // Assert
var descriptor = Assert.Single(retrievedDescriptors); var descriptor = Assert.Single(retrievedDescriptors);
@ -174,11 +292,12 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
private static TagHelperDescriptor CreatePrefixedDescriptor(string prefix, string tagName, string typeName) private static TagHelperDescriptor CreatePrefixedDescriptor(string prefix, string tagName, string typeName)
{ {
return new TagHelperDescriptor( return new TagHelperDescriptor(
prefix, prefix,
tagName, tagName,
typeName, typeName,
assemblyName: "SomeAssembly", assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>()); attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: Enumerable.Empty<string>());
} }
} }
} }

View File

@ -19,6 +19,867 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
{ {
public class TagHelperParseTreeRewriterTest : CsHtmlMarkupParserTestBase public class TagHelperParseTreeRewriterTest : CsHtmlMarkupParserTestBase
{ {
public static TheoryData RequiredAttributeData
{
get
{
var factory = CreateDefaultSpanFactory();
var blockFactory = new BlockFactory(factory);
var dateTimeNow = new MarkupBlock(
new MarkupBlock(
new ExpressionBlock(
factory.CodeTransition(),
factory.Code("DateTime.Now")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace))));
// documentContent, expectedOutput
return new TheoryData<string, MarkupBlock>
{
{
"<p />",
new MarkupBlock(blockFactory.MarkupTagBlock("<p />"))
},
{
"<p></p>",
new MarkupBlock(
blockFactory.MarkupTagBlock("<p>"),
blockFactory.MarkupTagBlock("</p>"))
},
{
"<div />",
new MarkupBlock(blockFactory.MarkupTagBlock("<div />"))
},
{
"<div></div>",
new MarkupBlock(
blockFactory.MarkupTagBlock("<div>"),
blockFactory.MarkupTagBlock("</div>"))
},
{
"<p class=\"btn\" />",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
selfClosing: true,
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
}))
},
{
"<p class=\"@DateTime.Now\" />",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
selfClosing: true,
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = dateTimeNow
}))
},
{
"<p class=\"btn\">words and spaces</p>",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
},
children: factory.Markup("words and spaces")))
},
{
"<p class=\"@DateTime.Now\">words and spaces</p>",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = dateTimeNow
},
children: factory.Markup("words and spaces")))
},
{
"<p class=\"btn\">words<strong>and</strong>spaces</p>",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
},
children: new SyntaxTreeNode[]
{
factory.Markup("words"),
blockFactory.MarkupTagBlock("<strong>"),
factory.Markup("and"),
blockFactory.MarkupTagBlock("</strong>"),
factory.Markup("spaces")
}))
},
{
"<strong catchAll=\"hi\" />",
new MarkupBlock(
new MarkupTagHelperBlock(
"strong",
selfClosing: true,
attributes: new Dictionary<string, SyntaxTreeNode>
{
["catchAll"] = factory.Markup("hi")
}))
},
{
"<strong catchAll=\"@DateTime.Now\" />",
new MarkupBlock(
new MarkupTagHelperBlock(
"strong",
selfClosing: true,
attributes: new Dictionary<string, SyntaxTreeNode>
{
["catchAll"] = dateTimeNow
}))
},
{
"<strong catchAll=\"hi\">words and spaces</strong>",
new MarkupBlock(
new MarkupTagHelperBlock(
"strong",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["catchAll"] = factory.Markup("hi")
},
children: factory.Markup("words and spaces")))
},
{
"<strong catchAll=\"@DateTime.Now\">words and spaces</strong>",
new MarkupBlock(
new MarkupTagHelperBlock(
"strong",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["catchAll"] = dateTimeNow
},
children: factory.Markup("words and spaces")))
},
{
"<div class=\"btn\" />",
new MarkupBlock(
new MarkupTagBlock(
factory.Markup("<div"),
new MarkupBlock(
new AttributeBlockCodeGenerator(
name: "class",
prefix: new LocationTagged<string>(" class=\"", 4, 0, 4),
suffix: new LocationTagged<string>("\"", 15, 0, 15)),
factory.Markup(" class=\"").With(SpanCodeGenerator.Null),
factory.Markup("btn").With(
new LiteralAttributeCodeGenerator(
prefix: new LocationTagged<string>(string.Empty, 12, 0, 12),
value: new LocationTagged<string>("btn", 12, 0, 12))),
factory.Markup("\"").With(SpanCodeGenerator.Null)),
factory.Markup(" />")))
},
{
"<div class=\"btn\"></div>",
new MarkupBlock(
new MarkupTagBlock(
factory.Markup("<div"),
new MarkupBlock(
new AttributeBlockCodeGenerator(
name: "class",
prefix: new LocationTagged<string>(" class=\"", 4, 0, 4),
suffix: new LocationTagged<string>("\"", 15, 0, 15)),
factory.Markup(" class=\"").With(SpanCodeGenerator.Null),
factory.Markup("btn").With(
new LiteralAttributeCodeGenerator(
prefix: new LocationTagged<string>(string.Empty, 12, 0, 12),
value: new LocationTagged<string>("btn", 12, 0, 12))),
factory.Markup("\"").With(SpanCodeGenerator.Null)),
factory.Markup(">")),
blockFactory.MarkupTagBlock("</div>"))
},
{
"<p notRequired=\"a\" class=\"btn\" />",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
selfClosing: true,
attributes: new Dictionary<string, SyntaxTreeNode>
{
["notRequired"] = factory.Markup("a"),
["class"] = factory.Markup("btn")
}))
},
{
"<p notRequired=\"@DateTime.Now\" class=\"btn\" />",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
selfClosing: true,
attributes: new Dictionary<string, SyntaxTreeNode>
{
["notRequired"] = dateTimeNow,
["class"] = factory.Markup("btn")
}))
},
{
"<p notRequired=\"a\" class=\"btn\">words and spaces</p>",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["notRequired"] = factory.Markup("a"),
["class"] = factory.Markup("btn")
},
children: factory.Markup("words and spaces")))
},
{
"<div style=\"\" class=\"btn\" />",
new MarkupBlock(
new MarkupTagHelperBlock(
"div",
selfClosing: true,
attributes: new Dictionary<string, SyntaxTreeNode>
{
["style"] = new MarkupBlock(),
["class"] = factory.Markup("btn")
}))
},
{
"<div style=\"@DateTime.Now\" class=\"btn\" />",
new MarkupBlock(
new MarkupTagHelperBlock(
"div",
selfClosing: true,
attributes: new Dictionary<string, SyntaxTreeNode>
{
["style"] = dateTimeNow,
["class"] = factory.Markup("btn")
}))
},
{
"<div style=\"\" class=\"btn\">words and spaces</div>",
new MarkupBlock(
new MarkupTagHelperBlock(
"div",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["style"] = new MarkupBlock(),
["class"] = factory.Markup("btn")
},
children: factory.Markup("words and spaces")))
},
{
"<div style=\"@DateTime.Now\" class=\"@DateTime.Now\">words and spaces</div>",
new MarkupBlock(
new MarkupTagHelperBlock(
"div",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["style"] = dateTimeNow,
["class"] = dateTimeNow
},
children: factory.Markup("words and spaces")))
},
{
"<div style=\"\" class=\"btn\">words<strong>and</strong>spaces</div>",
new MarkupBlock(
new MarkupTagHelperBlock(
"div",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["style"] = new MarkupBlock(),
["class"] = factory.Markup("btn")
},
children: new SyntaxTreeNode[]
{
factory.Markup("words"),
blockFactory.MarkupTagBlock("<strong>"),
factory.Markup("and"),
blockFactory.MarkupTagBlock("</strong>"),
factory.Markup("spaces")
}))
},
{
"<p class=\"btn\" catchAll=\"hi\" />",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
selfClosing: true,
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn"),
["catchAll"] = factory.Markup("hi")
}))
},
{
"<p class=\"btn\" catchAll=\"hi\">words and spaces</p>",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn"),
["catchAll"] = factory.Markup("hi")
},
children: factory.Markup("words and spaces")))
},
{
"<div style=\"\" class=\"btn\" catchAll=\"hi\" />",
new MarkupBlock(
new MarkupTagHelperBlock(
"div",
selfClosing: true,
attributes: new Dictionary<string, SyntaxTreeNode>
{
["style"] = new MarkupBlock(),
["class"] = factory.Markup("btn"),
["catchAll"] = factory.Markup("hi")
}))
},
{
"<div style=\"\" class=\"btn\" catchAll=\"hi\" >words and spaces</div>",
new MarkupBlock(
new MarkupTagHelperBlock(
"div",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["style"] = new MarkupBlock(),
["class"] = factory.Markup("btn"),
["catchAll"] = factory.Markup("hi")
},
children: factory.Markup("words and spaces")))
},
{
"<div style=\"@DateTime.Now\" class=\"@DateTime.Now\" catchAll=\"@DateTime.Now\" >words and " +
"spaces</div>",
new MarkupBlock(
new MarkupTagHelperBlock(
"div",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["style"] = dateTimeNow,
["class"] = dateTimeNow,
["catchAll"] = dateTimeNow
},
children: factory.Markup("words and spaces")))
},
{
"<div style=\"\" class=\"btn\" catchAll=\"hi\" >words<strong>and</strong>spaces</div>",
new MarkupBlock(
new MarkupTagHelperBlock(
"div",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["style"] = new MarkupBlock(),
["class"] = factory.Markup("btn"),
["catchAll"] = factory.Markup("hi")
},
children: new SyntaxTreeNode[]
{
factory.Markup("words"),
blockFactory.MarkupTagBlock("<strong>"),
factory.Markup("and"),
blockFactory.MarkupTagBlock("</strong>"),
factory.Markup("spaces")
}))
},
};
}
}
[Theory]
[MemberData(nameof(RequiredAttributeData))]
public void Rewrite_RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly(
string documentContent,
MarkupBlock expectedOutput)
{
// Arrange
var descriptors = new TagHelperDescriptor[]
{
new TagHelperDescriptor(
tagName: "p",
typeName: "pTagHelper",
assemblyName: "SomeAssembly",
attributes: new TagHelperAttributeDescriptor[0],
requiredAttributes: new[] { "class" }),
new TagHelperDescriptor(
tagName: "div",
typeName: "divTagHelper",
assemblyName: "SomeAssembly",
attributes: new TagHelperAttributeDescriptor[0],
requiredAttributes: new[] { "class", "style" }),
new TagHelperDescriptor(
tagName: "*",
typeName: "catchAllTagHelper",
assemblyName: "SomeAssembly",
attributes: new TagHelperAttributeDescriptor[0],
requiredAttributes: new[] { "catchAll" })
};
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new RazorError[0]);
}
public static TheoryData NestedRequiredAttributeData
{
get
{
var factory = CreateDefaultSpanFactory();
var blockFactory = new BlockFactory(factory);
var dateTimeNow = new MarkupBlock(
new MarkupBlock(
new ExpressionBlock(
factory.CodeTransition(),
factory.Code("DateTime.Now")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace))));
// documentContent, expectedOutput
return new TheoryData<string, MarkupBlock>
{
{
"<p class=\"btn\"><p></p></p>",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
},
children: new[]
{
blockFactory.MarkupTagBlock("<p>"),
blockFactory.MarkupTagBlock("</p>")
}))
},
{
"<strong catchAll=\"hi\"><strong></strong></strong>",
new MarkupBlock(
new MarkupTagHelperBlock(
"strong",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["catchAll"] = factory.Markup("hi")
},
children: new SyntaxTreeNode[]
{
blockFactory.MarkupTagBlock("<strong>"),
blockFactory.MarkupTagBlock("</strong>"),
}))
},
{
"<p class=\"btn\"><strong><p></p></strong></p>",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
},
children: new[]
{
blockFactory.MarkupTagBlock("<strong>"),
blockFactory.MarkupTagBlock("<p>"),
blockFactory.MarkupTagBlock("</p>"),
blockFactory.MarkupTagBlock("</strong>"),
}))
},
{
"<strong catchAll=\"hi\"><p><strong></strong></p></strong>",
new MarkupBlock(
new MarkupTagHelperBlock(
"strong",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["catchAll"] = factory.Markup("hi")
},
children: new SyntaxTreeNode[]
{
blockFactory.MarkupTagBlock("<p>"),
blockFactory.MarkupTagBlock("<strong>"),
blockFactory.MarkupTagBlock("</strong>"),
blockFactory.MarkupTagBlock("</p>"),
}))
},
{
"<p class=\"btn\"><strong catchAll=\"hi\"><p></p></strong></p>",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
},
children: new MarkupTagHelperBlock(
"strong",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["catchAll"] = factory.Markup("hi")
},
children: new[]
{
blockFactory.MarkupTagBlock("<p>"),
blockFactory.MarkupTagBlock("</p>")
})))
},
{
"<strong catchAll=\"hi\"><p class=\"btn\"><strong></strong></p></strong>",
new MarkupBlock(
new MarkupTagHelperBlock(
"strong",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["catchAll"] = factory.Markup("hi")
},
children: new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
},
children: new[]
{
blockFactory.MarkupTagBlock("<strong>"),
blockFactory.MarkupTagBlock("</strong>"),
})))
},
{
"<p class=\"btn\"><p class=\"btn\"><p></p></p></p>",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
},
children: new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
},
children: new[]
{
blockFactory.MarkupTagBlock("<p>"),
blockFactory.MarkupTagBlock("</p>")
})))
},
{
"<strong catchAll=\"hi\"><strong catchAll=\"hi\"><strong></strong></strong></strong>",
new MarkupBlock(
new MarkupTagHelperBlock(
"strong",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["catchAll"] = factory.Markup("hi")
},
children: new MarkupTagHelperBlock(
"strong",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["catchAll"] = factory.Markup("hi")
},
children: new[]
{
blockFactory.MarkupTagBlock("<strong>"),
blockFactory.MarkupTagBlock("</strong>"),
})))
},
{
"<p class=\"btn\"><p><p><p class=\"btn\"><p></p></p></p></p></p>",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
},
children: new[]
{
blockFactory.MarkupTagBlock("<p>"),
blockFactory.MarkupTagBlock("<p>"),
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
},
children: new[]
{
blockFactory.MarkupTagBlock("<p>"),
blockFactory.MarkupTagBlock("</p>")
}),
blockFactory.MarkupTagBlock("</p>"),
blockFactory.MarkupTagBlock("</p>"),
}))
},
{
"<strong catchAll=\"hi\"><strong><strong><strong catchAll=\"hi\"><strong></strong></strong>" +
"</strong></strong></strong>",
new MarkupBlock(
new MarkupTagHelperBlock(
"strong",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["catchAll"] = factory.Markup("hi")
},
children: new[]
{
blockFactory.MarkupTagBlock("<strong>"),
blockFactory.MarkupTagBlock("<strong>"),
new MarkupTagHelperBlock(
"strong",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["catchAll"] = factory.Markup("hi")
},
children: new[]
{
blockFactory.MarkupTagBlock("<strong>"),
blockFactory.MarkupTagBlock("</strong>"),
}),
blockFactory.MarkupTagBlock("</strong>"),
blockFactory.MarkupTagBlock("</strong>"),
}))
},
};
}
}
[Theory]
[MemberData(nameof(NestedRequiredAttributeData))]
public void Rewrite_NestedRequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly(
string documentContent,
MarkupBlock expectedOutput)
{
// Arrange
var descriptors = new TagHelperDescriptor[]
{
new TagHelperDescriptor(
tagName: "p",
typeName: "pTagHelper",
assemblyName: "SomeAssembly",
attributes: new TagHelperAttributeDescriptor[0],
requiredAttributes: new[] { "class" }),
new TagHelperDescriptor(
tagName: "*",
typeName: "catchAllTagHelper",
assemblyName: "SomeAssembly",
attributes: new TagHelperAttributeDescriptor[0],
requiredAttributes: new[] { "catchAll" })
};
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new RazorError[0]);
}
public static TheoryData<string, MarkupBlock, RazorError[]> MalformedRequiredAttributeData
{
get
{
var factory = CreateDefaultSpanFactory();
var blockFactory = new BlockFactory(factory);
var errorFormatUnclosed = "Found a malformed '{0}' tag helper. Tag helpers must have a start and " +
"end tag or be self closing.";
var errorFormatNoCloseAngle = "Missing close angle for tag helper '{0}'.";
// documentContent, expectedOutput, expectedErrors
return new TheoryData<string, MarkupBlock, RazorError[]>
{
{
"<p",
new MarkupBlock(blockFactory.MarkupTagBlock("<p")),
new RazorError[0]
},
{
"<p class=\"btn\"",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
})),
new[]
{
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"),
SourceLocation.Zero),
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"),
SourceLocation.Zero)
}
},
{
"<p notRequired=\"hi\" class=\"btn\"",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["notRequired"] = factory.Markup("hi"),
["class"] = factory.Markup("btn")
})),
new[]
{
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"),
SourceLocation.Zero),
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"),
SourceLocation.Zero)
}
},
{
"<p></p",
new MarkupBlock(
blockFactory.MarkupTagBlock("<p>"),
blockFactory.MarkupTagBlock("</p")),
new RazorError[0]
},
{
"<p class=\"btn\"></p",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
})),
new[]
{
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"),
absoluteIndex: 15, lineIndex: 0, columnIndex: 15)
}
},
{
"<p notRequired=\"hi\" class=\"btn\"></p",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["notRequired"] = factory.Markup("hi"),
["class"] = factory.Markup("btn")
})),
new[]
{
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"),
absoluteIndex: 32, lineIndex: 0, columnIndex: 32)
}
},
{
"<p class=\"btn\" <p>",
new MarkupBlock(
new MarkupTagHelperBlock("p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
},
children: blockFactory.MarkupTagBlock("<p>"))),
new[]
{
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"),
SourceLocation.Zero),
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"),
SourceLocation.Zero),
}
},
{
"<p notRequired=\"hi\" class=\"btn\" <p>",
new MarkupBlock(
new MarkupTagHelperBlock("p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["notRequired"] = factory.Markup("hi"),
["class"] = factory.Markup("btn")
},
children: blockFactory.MarkupTagBlock("<p>"))),
new[]
{
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"),
SourceLocation.Zero),
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"),
SourceLocation.Zero),
}
},
{
"<p class=\"btn\" </p",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["class"] = factory.Markup("btn")
})),
new[]
{
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"),
SourceLocation.Zero),
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"),
absoluteIndex: 15, lineIndex: 0, columnIndex: 15)
}
},
{
"<p notRequired=\"hi\" class=\"btn\" </p",
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new Dictionary<string, SyntaxTreeNode>
{
["notRequired"] = factory.Markup("hi"),
["class"] = factory.Markup("btn")
})),
new[]
{
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"),
SourceLocation.Zero),
new RazorError(
string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"),
absoluteIndex: 32, lineIndex: 0, columnIndex: 32)
}
},
};
}
}
[Theory]
[MemberData(nameof(MalformedRequiredAttributeData))]
public void Rewrite_RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly(
string documentContent,
MarkupBlock expectedOutput,
RazorError[] expectedErrors)
{
// Arrange
var descriptors = new TagHelperDescriptor[]
{
new TagHelperDescriptor(
tagName: "p",
typeName: "pTagHelper",
assemblyName: "SomeAssembly",
attributes: new TagHelperAttributeDescriptor[0],
requiredAttributes: new[] { "class" })
};
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors);
}
public static TheoryData PrefixedTagHelperBoundData public static TheoryData PrefixedTagHelperBoundData
{ {
get get
@ -32,7 +893,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
tagName: "myth", tagName: "myth",
typeName: "mythTagHelper", typeName: "mythTagHelper",
assemblyName: "SomeAssembly", assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>()), attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: Enumerable.Empty<string>()),
new TagHelperDescriptor( new TagHelperDescriptor(
prefix: "th:", prefix: "th:",
tagName: "myth2", tagName: "myth2",
@ -44,7 +906,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
name: "bound", name: "bound",
propertyName: "Bound", propertyName: "Bound",
typeName: typeof(bool).FullName), typeName: typeof(bool).FullName),
}) },
requiredAttributes: Enumerable.Empty<string>())
}; };
var availableDescriptorsText = new TagHelperDescriptor[] var availableDescriptorsText = new TagHelperDescriptor[]
{ {
@ -53,7 +916,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
tagName: "myth", tagName: "myth",
typeName: "mythTagHelper", typeName: "mythTagHelper",
assemblyName: "SomeAssembly", assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>()), attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: Enumerable.Empty<string>()),
new TagHelperDescriptor( new TagHelperDescriptor(
prefix: "PREFIX", prefix: "PREFIX",
tagName: "myth2", tagName: "myth2",
@ -65,7 +929,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
name: "bound", name: "bound",
propertyName: "Bound", propertyName: "Bound",
typeName: typeof(bool).FullName), typeName: typeof(bool).FullName),
}) },
requiredAttributes: Enumerable.Empty<string>())
}; };
var availableDescriptorsCatchAll = new TagHelperDescriptor[] var availableDescriptorsCatchAll = new TagHelperDescriptor[]
{ {
@ -74,7 +939,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
tagName: "*", tagName: "*",
typeName: "mythTagHelper", typeName: "mythTagHelper",
assemblyName: "SomeAssembly", assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>()), attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: Enumerable.Empty<string>()),
}; };
// documentContent, expectedOutput, availableDescriptors // documentContent, expectedOutput, availableDescriptors
@ -280,8 +1146,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
[Theory] [Theory]
[MemberData(nameof(PrefixedTagHelperBoundData))] [MemberData(nameof(PrefixedTagHelperBoundData))]
public void Rewrite_AllowsPrefixedTagHelpers( public void Rewrite_AllowsPrefixedTagHelpers(
string documentContent, string documentContent,
MarkupBlock expectedOutput, MarkupBlock expectedOutput,
IEnumerable<TagHelperDescriptor> availableDescriptors) IEnumerable<TagHelperDescriptor> availableDescriptors)
{ {
// Arrange // Arrange
@ -289,9 +1155,9 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
// Act & Assert // Act & Assert
EvaluateData( EvaluateData(
descriptorProvider, descriptorProvider,
documentContent, documentContent,
expectedOutput, expectedOutput,
expectedErrors: Enumerable.Empty<RazorError>()); expectedErrors: Enumerable.Empty<RazorError>());
} }

View File

@ -0,0 +1,60 @@
namespace TestOutput
{
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using System;
using System.Threading.Tasks;
public class AttributeTargetingTagHelpers
{
private static object @__o;
private void @__RazorDesignTimeHelpers__()
{
#pragma warning disable 219
string __tagHelperDirectiveSyntaxHelper = null;
__tagHelperDirectiveSyntaxHelper =
#line 1 "AttributeTargetingTagHelpers.cshtml"
"*, something"
#line default
#line hidden
;
#pragma warning restore 219
}
#line hidden
private PTagHelper __PTagHelper = null;
private CatchAllTagHelper __CatchAllTagHelper = null;
private InputTagHelper __InputTagHelper = null;
private InputTagHelper2 __InputTagHelper2 = null;
#line hidden
public AttributeTargetingTagHelpers()
{
}
#pragma warning disable 1998
public override async Task ExecuteAsync()
{
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__InputTagHelper.Type = "checkbox";
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__InputTagHelper2.Type = __InputTagHelper.Type;
#line 6 "AttributeTargetingTagHelpers.cshtml"
__InputTagHelper2.Checked = true;
#line default
#line hidden
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__InputTagHelper.Type = "checkbox";
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__InputTagHelper2.Type = __InputTagHelper.Type;
#line 7 "AttributeTargetingTagHelpers.cshtml"
__InputTagHelper2.Checked = true;
#line default
#line hidden
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
__PTagHelper = CreateTagHelper<PTagHelper>();
}
#pragma warning restore 1998
}
}

View File

@ -0,0 +1,100 @@
#pragma checksum "AttributeTargetingTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "e5aa16869aaf5543b30289e98ee5733b08bfe423"
namespace TestOutput
{
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using System;
using System.Threading.Tasks;
public class AttributeTargetingTagHelpers
{
#line hidden
#pragma warning disable 0414
private TagHelperContent __tagHelperStringValueBuffer = null;
#pragma warning restore 0414
private TagHelperExecutionContext __tagHelperExecutionContext = null;
private TagHelperRunner __tagHelperRunner = null;
private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager();
private PTagHelper __PTagHelper = null;
private CatchAllTagHelper __CatchAllTagHelper = null;
private InputTagHelper __InputTagHelper = null;
private InputTagHelper2 __InputTagHelper2 = null;
#line hidden
public AttributeTargetingTagHelpers()
{
}
#pragma warning disable 1998
public override async Task ExecuteAsync()
{
__tagHelperRunner = __tagHelperRunner ?? new TagHelperRunner();
Instrumentation.BeginContext(30, 2, true);
WriteLiteral("\r\n");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", false, "test", async() => {
WriteLiteral("\r\n <p>");
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("strong", false, "test", async() => {
WriteLiteral("Hello");
}
, StartTagHelperWritingScope, EndTagHelperWritingScope);
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
__tagHelperExecutionContext.Add(__CatchAllTagHelper);
__tagHelperExecutionContext.AddHtmlAttribute("catchAll", "hi");
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteTagHelperAsync(__tagHelperExecutionContext).Wait();
__tagHelperExecutionContext = __tagHelperScopeManager.End();
WriteLiteral("<strong>World</strong></p>\r\n <input checked=\"true\" />\r\n ");
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
}
, StartTagHelperWritingScope, EndTagHelperWritingScope);
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__tagHelperExecutionContext.Add(__InputTagHelper);
__InputTagHelper.Type = "checkbox";
__tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type);
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
__InputTagHelper2.Type = __InputTagHelper.Type;
#line 6 "AttributeTargetingTagHelpers.cshtml"
__InputTagHelper2.Checked = true;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteTagHelperAsync(__tagHelperExecutionContext).Wait();
__tagHelperExecutionContext = __tagHelperScopeManager.End();
WriteLiteral("\r\n ");
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
}
, StartTagHelperWritingScope, EndTagHelperWritingScope);
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__tagHelperExecutionContext.Add(__InputTagHelper);
__InputTagHelper.Type = "checkbox";
__tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type);
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
__InputTagHelper2.Type = __InputTagHelper.Type;
#line 7 "AttributeTargetingTagHelpers.cshtml"
__InputTagHelper2.Checked = true;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
__tagHelperExecutionContext.Add(__CatchAllTagHelper);
__tagHelperExecutionContext.AddHtmlAttribute("catchAll", "hi");
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteTagHelperAsync(__tagHelperExecutionContext).Wait();
__tagHelperExecutionContext = __tagHelperScopeManager.End();
WriteLiteral("\r\n");
}
, StartTagHelperWritingScope, EndTagHelperWritingScope);
__PTagHelper = CreateTagHelper<PTagHelper>();
__tagHelperExecutionContext.Add(__PTagHelper);
__tagHelperExecutionContext.AddHtmlAttribute("class", "btn");
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteTagHelperAsync(__tagHelperExecutionContext).Wait();
__tagHelperExecutionContext = __tagHelperScopeManager.End();
}
#pragma warning restore 1998
}
}

View File

@ -0,0 +1,8 @@
@addTagHelper "*, something"
<p class="btn">
<p><strong catchAll="hi">Hello</strong><strong>World</strong></p>
<input checked="true" />
<input type="checkbox" checked="true" />
<input type="checkbox" checked="true" catchAll="hi" />
</p>