Add support for '*' at the end of required attributes.

- [TargetElement(Attributes ="prefix-*")] is now supported.
- Added '*' to the list of invalid non whitespace characters in TagHelperDescriptorFactory.
- Modified TagHelperDescriptorProvider to respect suffixed wildcards in TagHelperAttributeDescriptor.Attributes.
- Added tests to validate wildcard required attributes

#361
This commit is contained in:
N. Taylor Mullen 2015-05-20 17:42:29 -07:00
parent 60c47c8874
commit 0e61b49881
6 changed files with 222 additions and 62 deletions

View File

@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// https://github.com/aspnet/Razor/issues/165 // https://github.com/aspnet/Razor/issues/165
public static ICollection<char> InvalidNonWhitespaceNameCharacters { get; } = new HashSet<char>( public static ICollection<char> InvalidNonWhitespaceNameCharacters { get; } = new HashSet<char>(
new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'' }); new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' });
/// <summary> /// <summary>
/// Creates a <see cref="TagHelperDescriptor"/> from the given <paramref name="type"/>. /// Creates a <see cref="TagHelperDescriptor"/> from the given <paramref name="type"/>.
@ -169,6 +169,25 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
bool targetingAttributes, bool targetingAttributes,
ErrorSink errorSink) ErrorSink errorSink)
{ {
if (!targetingAttributes &&
string.Equals(
name,
TagHelperDescriptorProvider.ElementCatchAllTarget,
StringComparison.OrdinalIgnoreCase))
{
// '*' as the entire name is OK in the TargetElement catch-all case.
return true;
}
else if (targetingAttributes &&
name.EndsWith(
TagHelperDescriptorProvider.RequiredAttributeWildcardSuffix,
StringComparison.OrdinalIgnoreCase))
{
// A single '*' at the end of a required attribute is valid; everywhere else is invalid. Strip it from
// the end so we can validate the rest of the name.
name = name.Substring(0, name.Length - 1);
}
var targetName = targetingAttributes ? var targetName = targetingAttributes ?
Resources.TagHelperDescriptorFactory_Attribute : Resources.TagHelperDescriptorFactory_Attribute :
Resources.TagHelperDescriptorFactory_Tag; Resources.TagHelperDescriptorFactory_Tag;

View File

@ -12,16 +12,16 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public sealed class TargetElementAttribute : Attribute public sealed class TargetElementAttribute : Attribute
{ {
public const string CatchAllDescriptorTarget = TagHelperDescriptorProvider.CatchAllDescriptorTarget; public const string ElementCatchAllTarget = TagHelperDescriptorProvider.ElementCatchAllTarget;
/// <summary> /// <summary>
/// Instantiates a new instance of the <see cref="TargetElementAttribute"/> class with <see cref="Tag"/> /// Instantiates a new instance of the <see cref="TargetElementAttribute"/> class with <see cref="Tag"/>
/// set to <c>*</c>. /// set to <c>*</c>.
/// </summary> /// </summary>
/// <remarks>A <c>*</c> <see cref="Tag"/> value indicates an <see cref="ITagHelper"/> /// <remarks>A <c>*</c> <see cref="Tag"/> value indicates an <see cref="ITagHelper"/>
/// that targets all HTML elements with the required <see cref="Attributes"/>.</remarks> /// that targets all HTML elements with the required <see cref="Attributes"/>.</remarks>
public TargetElementAttribute() public TargetElementAttribute()
: this(CatchAllDescriptorTarget) : this(ElementCatchAllTarget)
{ {
} }
@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
/// <param name="tag"> /// <param name="tag">
/// The HTML tag the <see cref="ITagHelper"/> targets. /// The HTML tag the <see cref="ITagHelper"/> targets.
/// </param> /// </param>
/// <remarks>A <c>*</c> <paramref name="tag"/> value indicates an <see cref="ITagHelper"/> /// <remarks>A <c>*</c> <paramref name="tag"/> value indicates an <see cref="ITagHelper"/>
/// that targets all HTML elements with the required <see cref="Attributes"/>.</remarks> /// that targets all HTML elements with the required <see cref="Attributes"/>.</remarks>
public TargetElementAttribute(string tag) public TargetElementAttribute(string tag)
{ {
@ -41,14 +41,17 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
/// <summary> /// <summary>
/// The HTML tag the <see cref="ITagHelper"/> targets. /// The HTML tag the <see cref="ITagHelper"/> targets.
/// </summary> /// </summary>
/// <remarks>A <c>*</c> <see cref="Tag"/> value indicates an <see cref="ITagHelper"/> /// <remarks>A <c>*</c> <see cref="Tag"/> value indicates an <see cref="ITagHelper"/>
/// that targets all HTML elements with the required <see cref="Attributes"/>.</remarks> /// that targets all HTML elements with the required <see cref="Attributes"/>.</remarks>
public string Tag { get; } public string Tag { get; }
/// <summary> /// <summary>
/// A comma-separated <see cref="string"/> of attributes the HTML element must contain for the /// A comma-separated <see cref="string"/> of attribute names the HTML element must contain for the
/// <see cref="ITagHelper"/> to run. /// <see cref="ITagHelper"/> to run.
/// </summary> /// </summary>
/// <remarks>
/// <c>*</c> at the end of an attribute name acts as a prefix match.
/// </remarks>
public string Attributes { get; set; } public string Attributes { get; set; }
} }
} }

View File

@ -133,6 +133,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers
/// <summary> /// <summary>
/// The list of required attribute names the tag helper expects to target an element. /// The list of required attribute names the tag helper expects to target an element.
/// </summary> /// </summary>
/// <remarks>
/// <c>*</c> at the end of an attribute name acts as a prefix match.
/// </remarks>
public IReadOnlyList<string> RequiredAttributes { get; private set; } public IReadOnlyList<string> RequiredAttributes { get; private set; }
} }
} }

View File

@ -12,7 +12,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers
/// </summary> /// </summary>
public class TagHelperDescriptorProvider public class TagHelperDescriptorProvider
{ {
public const string CatchAllDescriptorTarget = "*"; public const string ElementCatchAllTarget = "*";
public static readonly string RequiredAttributeWildcardSuffix = "*";
private IDictionary<string, HashSet<TagHelperDescriptor>> _registrations; private IDictionary<string, HashSet<TagHelperDescriptor>> _registrations;
private string _tagHelperPrefix; private string _tagHelperPrefix;
@ -55,7 +57,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
IEnumerable<TagHelperDescriptor> descriptors; IEnumerable<TagHelperDescriptor> descriptors;
// Ensure there's a HashSet to use. // Ensure there's a HashSet to use.
if (!_registrations.TryGetValue(CatchAllDescriptorTarget, out catchAllDescriptors)) if (!_registrations.TryGetValue(ElementCatchAllTarget, out catchAllDescriptors))
{ {
descriptors = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default); descriptors = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
} }
@ -66,9 +68,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers
// If the requested tag name is the catch-all target, we shouldn't do the work of concatenating extra // If the requested tag name is the catch-all target, we shouldn't do the work of concatenating extra
// descriptors. // descriptors.
if (!tagName.Equals(CatchAllDescriptorTarget, StringComparison.OrdinalIgnoreCase)) if (!tagName.Equals(ElementCatchAllTarget, StringComparison.OrdinalIgnoreCase))
{ {
// If we have a tag name associated with the requested name, we need to combine matchingDescriptors // If we have a tag name associated with the requested name, we need to combine matchingDescriptors
// with all the catch-all descriptors. // with all the catch-all descriptors.
HashSet<TagHelperDescriptor> matchingDescriptors; HashSet<TagHelperDescriptor> matchingDescriptors;
if (_registrations.TryGetValue(tagName, out matchingDescriptors)) if (_registrations.TryGetValue(tagName, out matchingDescriptors))
@ -91,7 +93,23 @@ namespace Microsoft.AspNet.Razor.TagHelpers
{ {
foreach (var requiredAttribute in descriptor.RequiredAttributes) foreach (var requiredAttribute in descriptor.RequiredAttributes)
{ {
if (!attributeNames.Contains(requiredAttribute, StringComparer.OrdinalIgnoreCase)) // '*' at the end of a required attribute indicates: apply to attributes prefixed with the
// required attribute value.
if (requiredAttribute.EndsWith(
RequiredAttributeWildcardSuffix,
StringComparison.OrdinalIgnoreCase))
{
var prefix = requiredAttribute.Substring(0, requiredAttribute.Length - 1);
if (!attributeNames.Any(
attributeName =>
attributeName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(attributeName, prefix, StringComparison.OrdinalIgnoreCase)))
{
return false;
}
}
else if (!attributeNames.Contains(requiredAttribute, StringComparer.OrdinalIgnoreCase))
{ {
return false; return false;
} }
@ -111,8 +129,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
} }
var registrationKey = var registrationKey =
string.Equals(descriptor.TagName, CatchAllDescriptorTarget, StringComparison.Ordinal) ? string.Equals(descriptor.TagName, ElementCatchAllTarget, StringComparison.Ordinal) ?
CatchAllDescriptorTarget : ElementCatchAllTarget :
descriptor.FullTagName; descriptor.FullTagName;
// Ensure there's a HashSet to add the descriptor to. // Ensure there's a HashSet to add the descriptor to.

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.AspNet.Razor.TagHelpers;
@ -29,7 +30,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
new[] new[]
{ {
new TagHelperDescriptor( new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget, TagHelperDescriptorProvider.ElementCatchAllTarget,
typeof(AttributeTargetingTagHelper).FullName, typeof(AttributeTargetingTagHelper).FullName,
AssemblyName, AssemblyName,
attributes, attributes,
@ -41,7 +42,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
new[] new[]
{ {
new TagHelperDescriptor( new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget, TagHelperDescriptorProvider.ElementCatchAllTarget,
typeof(MultiAttributeTargetingTagHelper).FullName, typeof(MultiAttributeTargetingTagHelper).FullName,
AssemblyName, AssemblyName,
attributes, attributes,
@ -53,13 +54,13 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
new[] new[]
{ {
new TagHelperDescriptor( new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget, TagHelperDescriptorProvider.ElementCatchAllTarget,
typeof(MultiAttributeAttributeTargetingTagHelper).FullName, typeof(MultiAttributeAttributeTargetingTagHelper).FullName,
AssemblyName, AssemblyName,
attributes, attributes,
requiredAttributes: new[] { "custom" }), requiredAttributes: new[] { "custom" }),
new TagHelperDescriptor( new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget, TagHelperDescriptorProvider.ElementCatchAllTarget,
typeof(MultiAttributeAttributeTargetingTagHelper).FullName, typeof(MultiAttributeAttributeTargetingTagHelper).FullName,
AssemblyName, AssemblyName,
attributes, attributes,
@ -71,7 +72,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
new[] new[]
{ {
new TagHelperDescriptor( new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget, TagHelperDescriptorProvider.ElementCatchAllTarget,
typeof(InheritedAttributeTargetingTagHelper).FullName, typeof(InheritedAttributeTargetingTagHelper).FullName,
AssemblyName, AssemblyName,
attributes, attributes,
@ -168,6 +169,30 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
requiredAttributes: new[] { "class", "style" }), requiredAttributes: new[] { "class", "style" }),
} }
}, },
{
typeof(AttributeWildcardTargetingTagHelper),
new[]
{
new TagHelperDescriptor(
TagHelperDescriptorProvider.ElementCatchAllTarget,
typeof(AttributeWildcardTargetingTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class*" })
}
},
{
typeof(MultiAttributeWildcardTargetingTagHelper),
new[]
{
new TagHelperDescriptor(
TagHelperDescriptorProvider.ElementCatchAllTarget,
typeof(MultiAttributeWildcardTargetingTagHelper).FullName,
AssemblyName,
attributes,
requiredAttributes: new[] { "class*", "style*" })
}
},
}; };
} }
} }
@ -1381,51 +1406,65 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
onNameError("'he'lo'", "'"), onNameError("'he'lo'", "'"),
} }
}, },
{ "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*", "*"),
}
},
{ Environment.NewLine, new[] { whitespaceErrorString } }, { Environment.NewLine, new[] { whitespaceErrorString } },
{ "\t", new[] { whitespaceErrorString } }, { "\t", new[] { whitespaceErrorString } },
{ " \t ", new[] { whitespaceErrorString } }, { " \t ", new[] { whitespaceErrorString } },
{ " ", new[] { whitespaceErrorString } }, { " ", new[] { whitespaceErrorString } },
{ Environment.NewLine + " ", new[] { whitespaceErrorString } }, { Environment.NewLine + " ", new[] { whitespaceErrorString } },
{ {
"! \t\r\n@/<>?[]=\"'", "! \t\r\n@/<>?[]=\"'*",
new[] new[]
{ {
onNameError("! \t\r\n@/<>?[]=\"'", "!"), onNameError("! \t\r\n@/<>?[]=\"'*", "!"),
onNameError("! \t\r\n@/<>?[]=\"'", " "), onNameError("! \t\r\n@/<>?[]=\"'*", " "),
onNameError("! \t\r\n@/<>?[]=\"'", "\t"), onNameError("! \t\r\n@/<>?[]=\"'*", "\t"),
onNameError("! \t\r\n@/<>?[]=\"'", "\r"), onNameError("! \t\r\n@/<>?[]=\"'*", "\r"),
onNameError("! \t\r\n@/<>?[]=\"'", "\n"), 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@/<>?[]=\"'*", "?"),
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@/<>?[]=\"'*", "'"),
onNameError("! \t\r\n@/<>?[]=\"'*", "*"),
} }
}, },
{ {
"! \tv\ra\nl@i/d<>?[]=\"'", "! \tv\ra\nl@i/d<>?[]=\"'*",
new[] new[]
{ {
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<>?[]=\"'", "\t"), onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "\t"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "\r"), onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "\r"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "\n"), 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<>?[]=\"'*", "?"),
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<>?[]=\"'*", "'"),
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "*"),
} }
}, },
}; };
@ -1441,6 +1480,16 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
return data; return data;
} }
[TargetElement(Attributes = "class*")]
private class AttributeWildcardTargetingTagHelper : TagHelper
{
}
[TargetElement(Attributes = "class*,style*")]
private class MultiAttributeWildcardTargetingTagHelper : TagHelper
{
}
[TargetElement(Attributes = "class")] [TargetElement(Attributes = "class")]
private class AttributeTargetingTagHelper : TagHelper private class AttributeTargetingTagHelper : TagHelper
{ {

View File

@ -25,20 +25,34 @@ namespace Microsoft.AspNet.Razor.TagHelpers
assemblyName: "SomeAssembly", assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(), attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: new[] { "class", "style" }); requiredAttributes: new[] { "class", "style" });
var inputWildcardPrefixDescriptor = new TagHelperDescriptor(
tagName: "input",
typeName: "InputWildCardAttribute",
assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: new[] { "nodashprefix*" });
var catchAllDescriptor = new TagHelperDescriptor( var catchAllDescriptor = new TagHelperDescriptor(
tagName: TagHelperDescriptorProvider.CatchAllDescriptorTarget, tagName: TagHelperDescriptorProvider.ElementCatchAllTarget,
typeName: "CatchAllTagHelper", typeName: "CatchAllTagHelper",
assemblyName: "SomeAssembly", assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(), attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: new[] { "class" }); requiredAttributes: new[] { "class" });
var catchAllDescriptor2 = new TagHelperDescriptor( var catchAllDescriptor2 = new TagHelperDescriptor(
tagName: TagHelperDescriptorProvider.CatchAllDescriptorTarget, tagName: TagHelperDescriptorProvider.ElementCatchAllTarget,
typeName: "CatchAllTagHelper", typeName: "CatchAllTagHelper",
assemblyName: "SomeAssembly", assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(), attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: new[] { "custom", "class" }); requiredAttributes: new[] { "custom", "class" });
var catchAllWildcardPrefixDescriptor = new TagHelperDescriptor(
tagName: TagHelperDescriptorProvider.ElementCatchAllTarget,
typeName: "CatchAllWildCardAttribute",
assemblyName: "SomeAssembly",
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
requiredAttributes: new[] { "prefix-*" });
var defaultAvailableDescriptors = var defaultAvailableDescriptors =
new[] { divDescriptor, inputDescriptor, catchAllDescriptor, catchAllDescriptor2 }; new[] { divDescriptor, inputDescriptor, catchAllDescriptor, catchAllDescriptor2 };
var defaultWildcardDescriptors =
new[] { inputWildcardPrefixDescriptor, catchAllWildcardPrefixDescriptor };
return new TheoryData< return new TheoryData<
string, // tagName string, // tagName
@ -73,29 +87,83 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new[] { inputDescriptor, catchAllDescriptor } new[] { inputDescriptor, catchAllDescriptor }
}, },
{ {
TagHelperDescriptorProvider.CatchAllDescriptorTarget, TagHelperDescriptorProvider.ElementCatchAllTarget,
new[] { "custom" }, new[] { "custom" },
defaultAvailableDescriptors, defaultAvailableDescriptors,
Enumerable.Empty<TagHelperDescriptor>() Enumerable.Empty<TagHelperDescriptor>()
}, },
{ {
TagHelperDescriptorProvider.CatchAllDescriptorTarget, TagHelperDescriptorProvider.ElementCatchAllTarget,
new[] { "class" }, new[] { "class" },
defaultAvailableDescriptors, defaultAvailableDescriptors,
new[] { catchAllDescriptor } new[] { catchAllDescriptor }
}, },
{ {
TagHelperDescriptorProvider.CatchAllDescriptorTarget, TagHelperDescriptorProvider.ElementCatchAllTarget,
new[] { "class", "style" }, new[] { "class", "style" },
defaultAvailableDescriptors, defaultAvailableDescriptors,
new[] { catchAllDescriptor } new[] { catchAllDescriptor }
}, },
{ {
TagHelperDescriptorProvider.CatchAllDescriptorTarget, TagHelperDescriptorProvider.ElementCatchAllTarget,
new[] { "class", "custom" }, new[] { "class", "custom" },
defaultAvailableDescriptors, defaultAvailableDescriptors,
new[] { catchAllDescriptor, catchAllDescriptor2 } new[] { catchAllDescriptor, catchAllDescriptor2 }
}, },
{
"input",
new[] { "nodashprefixA" },
defaultWildcardDescriptors,
new[] { inputWildcardPrefixDescriptor }
},
{
"input",
new[] { "nodashprefix-ABC-DEF", "random" },
defaultWildcardDescriptors,
new[] { inputWildcardPrefixDescriptor }
},
{
"input",
new[] { "prefixABCnodashprefix" },
defaultWildcardDescriptors,
Enumerable.Empty<TagHelperDescriptor>()
},
{
"input",
new[] { "prefix-" },
defaultWildcardDescriptors,
Enumerable.Empty<TagHelperDescriptor>()
},
{
"input",
new[] { "nodashprefix" },
defaultWildcardDescriptors,
Enumerable.Empty<TagHelperDescriptor>()
},
{
"input",
new[] { "prefix-A" },
defaultWildcardDescriptors,
new[] { catchAllWildcardPrefixDescriptor }
},
{
"input",
new[] { "prefix-ABC-DEF", "random" },
defaultWildcardDescriptors,
new[] { catchAllWildcardPrefixDescriptor }
},
{
"input",
new[] { "prefix-abc", "nodashprefix-def" },
defaultWildcardDescriptors,
new[] { inputWildcardPrefixDescriptor, catchAllWildcardPrefixDescriptor }
},
{
"input",
new[] { "class", "prefix-abc", "onclick", "nodashprefix-def", "style" },
defaultWildcardDescriptors,
new[] { inputWildcardPrefixDescriptor, catchAllWildcardPrefixDescriptor }
},
}; };
} }
} }
@ -124,7 +192,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
// Arrange // Arrange
var catchAllDescriptor = CreatePrefixedDescriptor( var catchAllDescriptor = CreatePrefixedDescriptor(
"th", "th",
TagHelperDescriptorProvider.CatchAllDescriptorTarget, TagHelperDescriptorProvider.ElementCatchAllTarget,
"foo1"); "foo1");
var descriptors = new[] { catchAllDescriptor }; var descriptors = new[] { catchAllDescriptor };
var provider = new TagHelperDescriptorProvider(descriptors); var provider = new TagHelperDescriptorProvider(descriptors);
@ -159,7 +227,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
public void GetDescriptors_ReturnsCatchAllDescriptorsForPrefixedTags() public void GetDescriptors_ReturnsCatchAllDescriptorsForPrefixedTags()
{ {
// Arrange // Arrange
var catchAllDescriptor = CreatePrefixedDescriptor("th:", TagHelperDescriptorProvider.CatchAllDescriptorTarget, "foo1"); var catchAllDescriptor = CreatePrefixedDescriptor("th:", TagHelperDescriptorProvider.ElementCatchAllTarget, "foo1");
var descriptors = new[] { catchAllDescriptor }; var descriptors = new[] { catchAllDescriptor };
var provider = new TagHelperDescriptorProvider(descriptors); var provider = new TagHelperDescriptorProvider(descriptors);
@ -230,14 +298,14 @@ namespace Microsoft.AspNet.Razor.TagHelpers
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( var catchAllDescriptor = new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget, TagHelperDescriptorProvider.ElementCatchAllTarget,
"foo3", "foo3",
"SomeAssembly"); "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.GetDescriptors(TagHelperDescriptorProvider.CatchAllDescriptorTarget, attributeNames: Enumerable.Empty<string>()); var retrievedDescriptors = provider.GetDescriptors(TagHelperDescriptorProvider.ElementCatchAllTarget, attributeNames: Enumerable.Empty<string>());
// Assert // Assert
var descriptor = Assert.Single(retrievedDescriptors); var descriptor = Assert.Single(retrievedDescriptors);
@ -251,7 +319,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
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( var catchAllDescriptor = new TagHelperDescriptor(
TagHelperDescriptorProvider.CatchAllDescriptorTarget, TagHelperDescriptorProvider.ElementCatchAllTarget,
"foo3", "foo3",
"SomeAssembly"); "SomeAssembly");
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor }; var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };