Add ability for `TagHelper`s to specify restricted children.
- Specifying the `RestrictChildrenAttribute` enables `TagHelper`s to only allow other `TagHelper`s targeting specified names to be in the children. - Used the `null` value to indicate that `AllowedChildren` was not specified and therefore everything is allowed. This is the default. - Added name verification to name values to ensure that no bad values pass through the system. - Added parsing tests to validate a mixture of content generates errors when expected. #255
This commit is contained in:
parent
3361507c29
commit
465ff9713d
|
|
@ -394,6 +394,38 @@ namespace Microsoft.AspNet.Razor.Runtime
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAttributeList_CannotAddAttribute"), p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(object p0, object p1, object p2, object p3)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName"), p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace"), p0, p1);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -189,4 +189,10 @@
|
|||
<data name="TagHelperAttributeList_CannotAddAttribute" xml:space="preserve">
|
||||
<value>Cannot add a {0} with inconsistent names. The {1} property '{2}' must match the location '{3}'.</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName" xml:space="preserve">
|
||||
<value>Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace" xml:space="preserve">
|
||||
<value>Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) .NET Foundation. 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;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Restricts children of the <see cref="ITagHelper"/>'s element.
|
||||
/// </summary>
|
||||
/// <remarks>Combining this attribute with a <see cref="TargetElementAttribute"/> that specifies its
|
||||
/// <see cref="TargetElementAttribute.TagStructure"/> as <see cref="TagStructure.WithoutEndTag"/> will result in
|
||||
/// this attribute being ignored.</remarks>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public class RestrictChildrenAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of the <see cref="RestrictChildrenAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="tagName">
|
||||
/// The tag name of an element allowed as a child. Tag helpers must target the element.
|
||||
/// </param>
|
||||
/// <param name="tagNames">
|
||||
/// Additional names of elements allowed as children. Tag helpers must target all such elements.
|
||||
/// </param>
|
||||
public RestrictChildrenAttribute(string tagName, params string[] tagNames)
|
||||
{
|
||||
var concatenatedNames = new string[1 + tagNames.Length];
|
||||
concatenatedNames[0] = tagName;
|
||||
|
||||
tagNames.CopyTo(concatenatedNames, 1);
|
||||
|
||||
ChildTagNames = concatenatedNames;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the names of elements allowed as children. Tag helpers must target all such elements.
|
||||
/// </summary>
|
||||
public IEnumerable<string> ChildTagNames { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -61,6 +61,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
|
||||
var attributeDescriptors = GetAttributeDescriptors(type, designTime, errorSink);
|
||||
var targetElementAttributes = GetValidTargetElementAttributes(typeInfo, errorSink);
|
||||
var allowedChildren = GetAllowedChildren(typeInfo, errorSink);
|
||||
|
||||
var tagHelperDescriptors =
|
||||
BuildTagHelperDescriptors(
|
||||
|
|
@ -68,6 +69,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName,
|
||||
attributeDescriptors,
|
||||
targetElementAttributes,
|
||||
allowedChildren,
|
||||
designTime);
|
||||
|
||||
return tagHelperDescriptors.Distinct(TagHelperDescriptorComparer.Default);
|
||||
|
|
@ -87,6 +89,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
string assemblyName,
|
||||
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
||||
IEnumerable<TargetElementAttribute> targetElementAttributes,
|
||||
IEnumerable<string> allowedChildren,
|
||||
bool designTime)
|
||||
{
|
||||
TagHelperDesignTimeDescriptor typeDesignTimeDescriptor = null;
|
||||
|
|
@ -118,6 +121,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName,
|
||||
attributeDescriptors,
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: allowedChildren,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: typeDesignTimeDescriptor)
|
||||
};
|
||||
|
|
@ -130,14 +134,70 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName,
|
||||
attributeDescriptors,
|
||||
attribute,
|
||||
allowedChildren,
|
||||
typeDesignTimeDescriptor));
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetAllowedChildren(TypeInfo typeInfo, ErrorSink errorSink)
|
||||
{
|
||||
var restrictChildrenAttribute = typeInfo.GetCustomAttribute<RestrictChildrenAttribute>(inherit: false);
|
||||
if (restrictChildrenAttribute == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var allowedChildren = restrictChildrenAttribute.ChildTagNames;
|
||||
var validAllowedChildren = GetValidAllowedChildren(allowedChildren, typeInfo.FullName, errorSink);
|
||||
|
||||
if (validAllowedChildren.Any())
|
||||
{
|
||||
return validAllowedChildren;
|
||||
}
|
||||
else
|
||||
{
|
||||
// All allowed children were invalid, return null to indicate that any child is acceptable.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for unit testing
|
||||
internal static IEnumerable<string> GetValidAllowedChildren(
|
||||
IEnumerable<string> allowedChildren,
|
||||
string tagHelperName,
|
||||
ErrorSink errorSink)
|
||||
{
|
||||
var validAllowedChildren = new List<string>();
|
||||
|
||||
foreach (var name in allowedChildren)
|
||||
{
|
||||
var valid = TryValidateName(
|
||||
name,
|
||||
whitespaceError: Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(
|
||||
nameof(RestrictChildrenAttribute),
|
||||
tagHelperName),
|
||||
characterErrorBuilder: (invalidCharacter) =>
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(
|
||||
nameof(RestrictChildrenAttribute),
|
||||
name,
|
||||
tagHelperName,
|
||||
invalidCharacter),
|
||||
errorSink: errorSink);
|
||||
|
||||
if (valid)
|
||||
{
|
||||
validAllowedChildren.Add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return validAllowedChildren;
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor BuildTagHelperDescriptor(
|
||||
string typeName,
|
||||
string assemblyName,
|
||||
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
||||
TargetElementAttribute targetElementAttribute,
|
||||
IEnumerable<string> allowedChildren,
|
||||
TagHelperDesignTimeDescriptor designTimeDescriptor)
|
||||
{
|
||||
var requiredAttributes = GetCommaSeparatedValues(targetElementAttribute.Attributes);
|
||||
|
|
@ -148,6 +208,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName,
|
||||
attributeDescriptors,
|
||||
requiredAttributes,
|
||||
allowedChildren,
|
||||
targetElementAttribute.TagStructure,
|
||||
designTimeDescriptor);
|
||||
}
|
||||
|
|
@ -158,6 +219,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
string assemblyName,
|
||||
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
||||
IEnumerable<string> requiredAttributes,
|
||||
IEnumerable<string> allowedChildren,
|
||||
TagStructure tagStructure,
|
||||
TagHelperDesignTimeDescriptor designTimeDescriptor)
|
||||
{
|
||||
|
|
@ -168,6 +230,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: assemblyName,
|
||||
attributes: attributeDescriptors,
|
||||
requiredAttributes: requiredAttributes,
|
||||
allowedChildren: allowedChildren,
|
||||
tagStructure: tagStructure,
|
||||
designTimeDescriptor: designTimeDescriptor);
|
||||
}
|
||||
|
|
@ -230,13 +293,28 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
var targetName = targetingAttributes ?
|
||||
Resources.TagHelperDescriptorFactory_Attribute :
|
||||
Resources.TagHelperDescriptorFactory_Tag;
|
||||
|
||||
var validName = TryValidateName(
|
||||
name,
|
||||
whitespaceError: Resources.FormatTargetElementAttribute_NameCannotBeNullOrWhitespace(targetName),
|
||||
characterErrorBuilder: (invalidCharacter) =>
|
||||
Resources.FormatTargetElementAttribute_InvalidName(targetName.ToLower(), name, invalidCharacter),
|
||||
errorSink: errorSink);
|
||||
|
||||
return validName;
|
||||
}
|
||||
|
||||
private static bool TryValidateName(
|
||||
string name,
|
||||
string whitespaceError,
|
||||
Func<char, string> characterErrorBuilder,
|
||||
ErrorSink errorSink)
|
||||
{
|
||||
var validName = true;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
errorSink.OnError(
|
||||
SourceLocation.Zero,
|
||||
Resources.FormatTargetElementAttribute_NameCannotBeNullOrWhitespace(targetName));
|
||||
errorSink.OnError(SourceLocation.Zero, whitespaceError);
|
||||
|
||||
validName = false;
|
||||
}
|
||||
|
|
@ -247,12 +325,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
if (char.IsWhiteSpace(character) ||
|
||||
InvalidNonWhitespaceNameCharacters.Contains(character))
|
||||
{
|
||||
errorSink.OnError(
|
||||
SourceLocation.Zero,
|
||||
Resources.FormatTargetElementAttribute_InvalidName(
|
||||
targetName.ToLower(),
|
||||
name,
|
||||
character));
|
||||
var error = characterErrorBuilder(character);
|
||||
errorSink.OnError(SourceLocation.Zero, error);
|
||||
|
||||
validName = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
descriptor.AssemblyName,
|
||||
descriptor.Attributes,
|
||||
descriptor.RequiredAttributes,
|
||||
descriptor.AllowedChildren,
|
||||
descriptor.TagStructure,
|
||||
descriptor.DesignTimeDescriptor));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,14 +26,19 @@ namespace Microsoft.AspNet.Razor.Test.Internal
|
|||
}
|
||||
|
||||
return base.Equals(descriptorX, descriptorY) &&
|
||||
// Normal comparer doesn't care about the case, required attribute order, attributes or prefixes.
|
||||
// In tests we do.
|
||||
// Normal comparer doesn't care about the case, required attribute order, allowed children 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.AllowedChildren == descriptorY.AllowedChildren ||
|
||||
Enumerable.SequenceEqual(
|
||||
descriptorX.AllowedChildren,
|
||||
descriptorY.AllowedChildren,
|
||||
StringComparer.Ordinal)) &&
|
||||
descriptorX.Attributes.SequenceEqual(
|
||||
descriptorY.Attributes,
|
||||
TagHelperAttributeDescriptorComparer.Default) &&
|
||||
|
|
@ -60,6 +65,14 @@ namespace Microsoft.AspNet.Razor.Test.Internal
|
|||
hashCodeCombiner.Add(requiredAttribute, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
if (descriptor.AllowedChildren != null)
|
||||
{
|
||||
foreach (var child in descriptor.AllowedChildren)
|
||||
{
|
||||
hashCodeCombiner.Add(child, StringComparer.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var attribute in descriptor.Attributes)
|
||||
{
|
||||
hashCodeCombiner.Add(TagHelperAttributeDescriptorComparer.Default.GetHashCode(attribute));
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
|
|
@ -16,6 +17,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
{
|
||||
private TagHelperDescriptorProvider _provider;
|
||||
private Stack<TagHelperBlockTracker> _trackerStack;
|
||||
private TagHelperBlockTracker _currentTagHelperTracker;
|
||||
private Stack<BlockBuilder> _blockStack;
|
||||
private BlockBuilder _currentBlock;
|
||||
|
||||
|
|
@ -56,6 +58,11 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-TagHelper tag.
|
||||
ValidateParentTagHelperAllowsPlainTag(childBlock, context.ErrorSink);
|
||||
}
|
||||
|
||||
// If we get to here it means that we're a normal html tag. No need to iterate any deeper into
|
||||
// the children of it because they wont be tag helpers.
|
||||
|
|
@ -67,6 +74,10 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ValidateParentTagHelperAllowsContent((Span)child, context.ErrorSink);
|
||||
}
|
||||
|
||||
// At this point the child is a Span or Block with Type BlockType.Tag that doesn't happen to be a
|
||||
// tag helper.
|
||||
|
|
@ -108,7 +119,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
return false;
|
||||
}
|
||||
|
||||
var tracker = _trackerStack.Count > 0 ? _trackerStack.Peek() : null;
|
||||
var tracker = _currentTagHelperTracker;
|
||||
var tagNameScope = tracker?.Builder.TagName ?? string.Empty;
|
||||
|
||||
if (!IsEndTag(tagBlock))
|
||||
|
|
@ -135,6 +146,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
return false;
|
||||
}
|
||||
|
||||
ValidateParentTagHelperAllowsTagHelper(tagName, tagBlock, context.ErrorSink);
|
||||
ValidateDescriptors(descriptors, tagName, tagBlock, context.ErrorSink);
|
||||
|
||||
// We're in a start TagHelper block.
|
||||
|
|
@ -211,6 +223,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
// can't recover it means there was no corresponding tag helper start tag.
|
||||
if (TryRecoverTagHelper(tagName, tagBlock, context))
|
||||
{
|
||||
ValidateParentTagHelperAllowsTagHelper(tagName, tagBlock, context.ErrorSink);
|
||||
ValidateTagSyntax(tagName, tagBlock, context);
|
||||
|
||||
// Successfully recovered, move onto the next element.
|
||||
|
|
@ -267,6 +280,63 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
return attributeNames;
|
||||
}
|
||||
|
||||
private void ValidateParentTagHelperAllowsContent(Span child, ErrorSink errorSink)
|
||||
{
|
||||
var allowedChildren = _currentTagHelperTracker?.AllowedChildren;
|
||||
if (allowedChildren != null)
|
||||
{
|
||||
var content = child.Content;
|
||||
if (!string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
var trimmedStart = content.TrimStart();
|
||||
var whitespace = content.Substring(0, content.Length - trimmedStart.Length);
|
||||
var errorStart = SourceLocation.Advance(child.Start, whitespace);
|
||||
var length = trimmedStart.TrimEnd().Length;
|
||||
var allowedChildrenString = string.Join(", ", allowedChildren);
|
||||
errorSink.OnError(
|
||||
errorStart,
|
||||
RazorResources.FormatTagHelperParseTreeRewriter_CannotHaveNonTagContent(
|
||||
_currentTagHelperTracker.Builder.TagName,
|
||||
allowedChildrenString),
|
||||
length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateParentTagHelperAllowsPlainTag(Block tagBlock, ErrorSink errorSink)
|
||||
{
|
||||
if (_currentTagHelperTracker?.AllowedChildren != null)
|
||||
{
|
||||
OnAllowedChildrenTagError(_currentTagHelperTracker, tagBlock, errorSink);
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateParentTagHelperAllowsTagHelper(string tagName, Block tagBlock, ErrorSink errorSink)
|
||||
{
|
||||
var currentlyAllowedChildren = _currentTagHelperTracker?.AllowedChildren;
|
||||
|
||||
if (currentlyAllowedChildren != null &&
|
||||
!currentlyAllowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
OnAllowedChildrenTagError(_currentTagHelperTracker, tagBlock, errorSink);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnAllowedChildrenTagError(
|
||||
TagHelperBlockTracker tracker,
|
||||
Block tagBlock,
|
||||
ErrorSink errorSink)
|
||||
{
|
||||
var tagName = GetTagName(tagBlock);
|
||||
var allowedChildrenString = string.Join(", ", tracker.AllowedChildren);
|
||||
var errorMessage = RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag(
|
||||
tagName,
|
||||
tracker.Builder.TagName,
|
||||
allowedChildrenString);
|
||||
|
||||
errorSink.OnError(tagBlock.Start, errorMessage, tagBlock.Length);
|
||||
}
|
||||
|
||||
private static void ValidateDescriptors(
|
||||
IEnumerable<TagHelperDescriptor> descriptors,
|
||||
string tagName,
|
||||
|
|
@ -361,6 +431,8 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
// for formatting.
|
||||
_trackerStack.Pop().Builder.SourceEndTag = endTag;
|
||||
|
||||
_currentTagHelperTracker = _trackerStack.Count > 0 ? _trackerStack.Peek() : null;
|
||||
|
||||
BuildCurrentlyTrackedBlock();
|
||||
}
|
||||
|
||||
|
|
@ -385,7 +457,8 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
|
||||
private void TrackTagHelperBlock(TagHelperBlockBuilder builder)
|
||||
{
|
||||
_trackerStack.Push(new TagHelperBlockTracker(builder));
|
||||
_currentTagHelperTracker = new TagHelperBlockTracker(builder);
|
||||
_trackerStack.Push(_currentTagHelperTracker);
|
||||
|
||||
TrackBlock(builder);
|
||||
}
|
||||
|
|
@ -478,11 +551,20 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
public TagHelperBlockTracker(TagHelperBlockBuilder builder)
|
||||
{
|
||||
Builder = builder;
|
||||
|
||||
if (Builder.Descriptors.Any(descriptor => descriptor.AllowedChildren != null))
|
||||
{
|
||||
AllowedChildren = Builder.Descriptors
|
||||
.SelectMany(descriptor => descriptor.AllowedChildren)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
public TagHelperBlockBuilder Builder { get; }
|
||||
|
||||
public uint OpenMatchingTags { get; set; }
|
||||
|
||||
public IEnumerable<string> AllowedChildren { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1514,6 +1514,38 @@ namespace Microsoft.AspNet.Razor
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag"), p0, p1, p2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The parent &lt;{0}&gt; tag helper does not allow non-tag content. Only child tag helper(s) targeting tag name(s) '{1}' are allowed.
|
||||
/// </summary>
|
||||
internal static string TagHelperParseTreeRewriter_CannotHaveNonTagContent
|
||||
{
|
||||
get { return GetString("TagHelperParseTreeRewriter_CannotHaveNonTagContent"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The parent &lt;{0}&gt; tag helper does not allow non-tag content. Only child tag helper(s) targeting tag name(s) '{1}' are allowed.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperParseTreeRewriter_CannotHaveNonTagContent(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_CannotHaveNonTagContent"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The &lt;{0}&gt; tag is not allowed by parent &lt;{1}&gt; tag helper. Only child tag helper(s) targeting tag name(s) '{2}' are allowed.
|
||||
/// </summary>
|
||||
internal static string TagHelperParseTreeRewriter_InvalidNestedTag
|
||||
{
|
||||
get { return GetString("TagHelperParseTreeRewriter_InvalidNestedTag"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The &lt;{0}&gt; tag is not allowed by parent &lt;{1}&gt; tag helper. Only child tag helper(s) targeting tag name(s) '{2}' are allowed.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperParseTreeRewriter_InvalidNestedTag(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_InvalidNestedTag"), p0, p1, p2);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -419,4 +419,10 @@ Instead, wrap the contents of the block in "{{}}":
|
|||
<data name="TagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag" xml:space="preserve">
|
||||
<value>Found an end tag (&lt;/{0}&gt;) for tag helper '{1}' with tag structure that disallows an end tag ('{2}').</value>
|
||||
</data>
|
||||
<data name="TagHelperParseTreeRewriter_CannotHaveNonTagContent" xml:space="preserve">
|
||||
<value>The parent &lt;{0}&gt; tag helper does not allow non-tag content. Only child tag helper(s) targeting tag name(s) '{1}' are allowed.</value>
|
||||
</data>
|
||||
<data name="TagHelperParseTreeRewriter_InvalidNestedTag" xml:space="preserve">
|
||||
<value>The &lt;{0}&gt; tag is not allowed by parent &lt;{1}&gt; tag helper. Only child tag helper(s) targeting tag name(s) '{2}' are allowed.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -61,6 +61,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
assemblyName: assemblyName,
|
||||
attributes: attributes,
|
||||
requiredAttributes: requiredAttributes,
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null)
|
||||
{
|
||||
|
|
@ -84,6 +85,10 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// <param name="requiredAttributes">
|
||||
/// The attribute names required for the tag helper to target the HTML tag.
|
||||
/// </param>
|
||||
/// <param name="allowedChildren">
|
||||
/// The names of elements allowed as children. Tag helpers must target all such elements.
|
||||
/// </param>
|
||||
/// <param name="tagStructure">The expected tag structure.</param>
|
||||
/// <param name="designTimeDescriptor">The <see cref="TagHelperDesignTimeDescriptor"/> that contains design
|
||||
/// time information about the tag helper.</param>
|
||||
public TagHelperDescriptor(
|
||||
|
|
@ -93,6 +98,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
[NotNull] string assemblyName,
|
||||
[NotNull] IEnumerable<TagHelperAttributeDescriptor> attributes,
|
||||
[NotNull] IEnumerable<string> requiredAttributes,
|
||||
IEnumerable<string> allowedChildren,
|
||||
TagStructure tagStructure,
|
||||
TagHelperDesignTimeDescriptor designTimeDescriptor)
|
||||
{
|
||||
|
|
@ -105,6 +111,11 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
RequiredAttributes = new List<string>(requiredAttributes);
|
||||
TagStructure = tagStructure;
|
||||
DesignTimeDescriptor = designTimeDescriptor;
|
||||
|
||||
if (allowedChildren != null)
|
||||
{
|
||||
AllowedChildren = new List<string>(allowedChildren);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -147,6 +158,12 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// </remarks>
|
||||
public IReadOnlyList<string> RequiredAttributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the names of elements allowed as children. Tag helpers must target all such elements.
|
||||
/// </summary>
|
||||
/// <remarks><c>null</c> indicates all children are allowed.</remarks>
|
||||
public IReadOnlyList<string> AllowedChildren { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The expected tag structure.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// <remarks>
|
||||
/// Determines equality based on <see cref="TagHelperDescriptor.TypeName"/>,
|
||||
/// <see cref="TagHelperDescriptor.AssemblyName"/>, <see cref="TagHelperDescriptor.TagName"/>,
|
||||
/// <see cref="TagHelperDescriptor.RequiredAttributes"/>, and <see cref="TagHelperDescriptor.TagStructure"/>.
|
||||
/// <see cref="TagHelperDescriptor.RequiredAttributes"/>, <see cref="TagHelperDescriptor.AllowedChildren"/>,
|
||||
/// and <see cref="TagHelperDescriptor.TagStructure"/>.
|
||||
/// Ignores <see cref="TagHelperDescriptor.DesignTimeDescriptor"/> because it can be inferred directly from
|
||||
/// <see cref="TagHelperDescriptor.TypeName"/> and <see cref="TagHelperDescriptor.AssemblyName"/>.
|
||||
/// </remarks>
|
||||
|
|
@ -49,6 +50,13 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
descriptorX.RequiredAttributes.OrderBy(attribute => attribute, StringComparer.OrdinalIgnoreCase),
|
||||
descriptorY.RequiredAttributes.OrderBy(attribute => attribute, StringComparer.OrdinalIgnoreCase),
|
||||
StringComparer.OrdinalIgnoreCase) &&
|
||||
(descriptorX.AllowedChildren == descriptorY.AllowedChildren ||
|
||||
(descriptorX.AllowedChildren != null &&
|
||||
descriptorY.AllowedChildren != null &&
|
||||
Enumerable.SequenceEqual(
|
||||
descriptorX.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase),
|
||||
descriptorY.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase),
|
||||
StringComparer.OrdinalIgnoreCase))) &&
|
||||
descriptorX.TagStructure == descriptorY.TagStructure;
|
||||
}
|
||||
|
||||
|
|
@ -69,6 +77,15 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
hashCodeCombiner.Add(attribute, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (descriptor.AllowedChildren != null)
|
||||
{
|
||||
var allowedChildren = descriptor.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var child in allowedChildren)
|
||||
{
|
||||
hashCodeCombiner.Add(child, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
return hashCodeCombiner.CombinedHash;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: AssemblyName,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: null)
|
||||
}
|
||||
|
|
@ -50,6 +51,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: AssemblyName,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
|
|
@ -59,6 +61,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: AssemblyName,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.NormalOrSelfClosing,
|
||||
designTimeDescriptor: null),
|
||||
}
|
||||
|
|
@ -74,6 +77,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: AssemblyName,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
|
|
@ -83,6 +87,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: AssemblyName,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null),
|
||||
}
|
||||
|
|
@ -137,6 +142,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: AssemblyName,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
||||
{
|
||||
|
|
@ -155,6 +161,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: AssemblyName,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
||||
{
|
||||
|
|
@ -167,6 +174,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: AssemblyName,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
||||
{
|
||||
|
|
@ -1734,6 +1742,42 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
}
|
||||
}
|
||||
|
||||
public static TheoryData<string, string[]> InvalidRestrictChildrenNameData
|
||||
{
|
||||
get
|
||||
{
|
||||
var nullOrWhiteSpaceError =
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(
|
||||
nameof(RestrictChildrenAttribute),
|
||||
"SomeTagHelper");
|
||||
|
||||
return GetInvalidNameOrPrefixData(
|
||||
onNameError: (invalidInput, invalidCharacter) =>
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(
|
||||
nameof(RestrictChildrenAttribute),
|
||||
invalidInput,
|
||||
"SomeTagHelper",
|
||||
invalidCharacter),
|
||||
whitespaceErrorString: nullOrWhiteSpaceError,
|
||||
onDataError: null);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(InvalidRestrictChildrenNameData))]
|
||||
public void GetValidAllowedChildren_AddsExpectedErrors(string name, string[] expectedErrorMessages)
|
||||
{
|
||||
// Arrange
|
||||
var errorSink = new ErrorSink();
|
||||
var expectedErrors = expectedErrorMessages.Select(message => new RazorError(message, SourceLocation.Zero));
|
||||
|
||||
// Act
|
||||
TagHelperDescriptorFactory.GetValidAllowedChildren(new[] { name }, "SomeTagHelper", errorSink);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedErrors, errorSink.Errors);
|
||||
}
|
||||
|
||||
private static TheoryData<string, string[]> GetInvalidNameOrPrefixData(
|
||||
Func<string, string, string> onNameError,
|
||||
string whitespaceErrorString,
|
||||
|
|
@ -1973,6 +2017,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: assemblyName,
|
||||
attributes: attributes ?? Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: requiredAttributes ?? Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: AssemblyName,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null);
|
||||
}
|
||||
|
|
@ -48,6 +49,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: AssemblyName,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null);
|
||||
}
|
||||
|
|
@ -609,6 +611,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: assemblyB,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null);
|
||||
|
||||
|
|
@ -1045,6 +1048,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName: assemblyB,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null);
|
||||
|
||||
|
|
@ -1444,6 +1448,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
assemblyName,
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1550,6 +1550,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
new TagHelperAttributeDescriptor("age", pAgePropertyInfo)
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.NormalOrSelfClosing,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
|
|
@ -1562,6 +1563,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
|
|
@ -1575,6 +1577,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
new TagHelperAttributeDescriptor("checked", checkedPropertyInfo)
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
|
||||
},
|
||||
requiredAttributes: new string[0],
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: null)
|
||||
};
|
||||
|
|
@ -189,6 +190,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: structure1,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
|
|
@ -198,6 +200,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: structure2,
|
||||
designTimeDescriptor: null)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -322,6 +322,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
assemblyName: "SomeAssembly",
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
assemblyName: "assembly name",
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: new[] { "required attribute one", "required attribute two" },
|
||||
allowedChildren: new[] { "allowed child one" },
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
||||
{
|
||||
|
|
@ -40,6 +41,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":" +
|
||||
"[\"required attribute one\",\"required attribute two\"]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":[\"allowed child one\"]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":0," +
|
||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":{{"+
|
||||
$"\"{ nameof(TagHelperDesignTimeDescriptor.Summary) }\":\"usage summary\"," +
|
||||
|
|
@ -78,6 +80,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
designTimeDescriptor: null),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.NormalOrSelfClosing,
|
||||
designTimeDescriptor: null);
|
||||
var expectedSerializedDescriptor =
|
||||
|
|
@ -100,6 +103,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," +
|
||||
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":1," +
|
||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
||||
|
||||
|
|
@ -135,6 +139,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
designTimeDescriptor: null),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: new[] { "allowed child one", "allowed child two" },
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null);
|
||||
var expectedSerializedDescriptor =
|
||||
|
|
@ -157,6 +162,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":[\"allowed child one\",\"allowed child two\"]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":0," +
|
||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
||||
|
||||
|
|
@ -180,6 +186,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
$"\"{nameof(TagHelperDescriptor.Attributes)}\":[]," +
|
||||
$"\"{nameof(TagHelperDescriptor.RequiredAttributes)}\":" +
|
||||
"[\"required attribute one\",\"required attribute two\"]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":[\"allowed child one\",\"allowed child two\"]," +
|
||||
$"\"{nameof(TagHelperDescriptor.TagStructure)}\":2," +
|
||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":{{" +
|
||||
$"\"{ nameof(TagHelperDesignTimeDescriptor.Summary) }\":\"usage summary\"," +
|
||||
|
|
@ -192,6 +199,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
assemblyName: "assembly name",
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: new[] { "required attribute one", "required attribute two" },
|
||||
allowedChildren: new[] { "allowed child one", "allowed child two" },
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
||||
{
|
||||
|
|
@ -242,6 +250,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," +
|
||||
$"\"{nameof(TagHelperDescriptor.TagStructure)}\":0," +
|
||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
||||
var expectedDescriptor = new TagHelperDescriptor(
|
||||
|
|
@ -265,6 +274,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
designTimeDescriptor: null),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null);
|
||||
|
||||
|
|
@ -331,6 +341,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," +
|
||||
$"\"{nameof(TagHelperDescriptor.TagStructure)}\":1," +
|
||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
||||
var expectedDescriptor = new TagHelperDescriptor(
|
||||
|
|
@ -354,6 +365,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
designTimeDescriptor: null),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.NormalOrSelfClosing,
|
||||
designTimeDescriptor: null);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,450 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
{
|
||||
public class TagHelperParseTreeRewriterTest : TagHelperRewritingTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void Rewrite_CanHandleInvalidChildrenWithWhitespace()
|
||||
{
|
||||
// Arrange
|
||||
var factory = CreateDefaultSpanFactory();
|
||||
var blockFactory = new BlockFactory(factory);
|
||||
var documentContent = $"<p>{Environment.NewLine} <strong>{Environment.NewLine} Hello" +
|
||||
$"{Environment.NewLine} </strong>{Environment.NewLine}</p>";
|
||||
var expectedErrors = new[] {
|
||||
new RazorError(
|
||||
RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag("strong", "p", "br"),
|
||||
absoluteIndex: 9,
|
||||
lineIndex: 1,
|
||||
columnIndex: 9,
|
||||
length: 8),
|
||||
new RazorError(
|
||||
RazorResources.FormatTagHelperParseTreeRewriter_CannotHaveNonTagContent("p", "br"),
|
||||
absoluteIndex: 27,
|
||||
lineIndex: 2,
|
||||
columnIndex: 27,
|
||||
length: 5),
|
||||
new RazorError(
|
||||
RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag("strong", "p", "br"),
|
||||
absoluteIndex: 38,
|
||||
lineIndex: 3,
|
||||
columnIndex: 38,
|
||||
length: 9),
|
||||
};
|
||||
var expectedOutput = new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
factory.Markup(Environment.NewLine + " "),
|
||||
blockFactory.MarkupTagBlock("<strong>"),
|
||||
factory.Markup(Environment.NewLine + " Hello" + Environment.NewLine + " "),
|
||||
blockFactory.MarkupTagBlock("</strong>"),
|
||||
factory.Markup(Environment.NewLine)));
|
||||
var descriptors = new TagHelperDescriptor[]
|
||||
{
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "p",
|
||||
typeName: "PTagHelper",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: new[] { "br" },
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null),
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rewrite_RecoversWhenRequiredAttributeMismatchAndRestrictedChildren()
|
||||
{
|
||||
// Arrange
|
||||
var factory = CreateDefaultSpanFactory();
|
||||
var blockFactory = new BlockFactory(factory);
|
||||
var documentContent = "<strong required><strong></strong></strong>";
|
||||
|
||||
var expectedErrors = new[] {
|
||||
new RazorError(
|
||||
RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag("strong", "strong", "br"),
|
||||
absoluteIndex: 17,
|
||||
lineIndex: 0,
|
||||
columnIndex: 17,
|
||||
length: 8),
|
||||
new RazorError(
|
||||
RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag("strong", "strong", "br"),
|
||||
absoluteIndex: 25,
|
||||
lineIndex: 0,
|
||||
columnIndex: 25,
|
||||
length: 9),
|
||||
};
|
||||
var expectedOutput = new MarkupBlock(
|
||||
new MarkupTagHelperBlock("strong",
|
||||
new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("required", null)
|
||||
},
|
||||
blockFactory.MarkupTagBlock("<strong>"),
|
||||
blockFactory.MarkupTagBlock("</strong>")));
|
||||
var descriptors = new TagHelperDescriptor[]
|
||||
{
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "strong",
|
||||
typeName: "StrongTagHelper",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: new[] { "required" },
|
||||
allowedChildren: new[] { "br" },
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null),
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rewrite_CanHandleMultipleTagHelpersWithAllowedChildren_OneNull()
|
||||
{
|
||||
// Arrange
|
||||
var factory = CreateDefaultSpanFactory();
|
||||
var documentContent = "<p><strong>Hello World</strong><br></p>";
|
||||
var expectedOutput = new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
new MarkupTagHelperBlock("strong",
|
||||
factory.Markup("Hello World")),
|
||||
new MarkupTagHelperBlock("br", TagMode.StartTagOnly)));
|
||||
var descriptors = new TagHelperDescriptor[]
|
||||
{
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "p",
|
||||
typeName: "PTagHelper1",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: new[] { "strong", "br" },
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "p",
|
||||
typeName: "PTagHelper2",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "strong",
|
||||
typeName: "StrongTagHelper",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "br",
|
||||
typeName: "BRTagHelper",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: null),
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new RazorError[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rewrite_CanHandleMultipleTagHelpersWithAllowedChildren()
|
||||
{
|
||||
// Arrange
|
||||
var factory = CreateDefaultSpanFactory();
|
||||
var documentContent = "<p><strong>Hello World</strong><br></p>";
|
||||
var expectedOutput = new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
new MarkupTagHelperBlock("strong",
|
||||
factory.Markup("Hello World")),
|
||||
new MarkupTagHelperBlock("br", TagMode.StartTagOnly)));
|
||||
var descriptors = new TagHelperDescriptor[]
|
||||
{
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "p",
|
||||
typeName: "PTagHelper1",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: new[] { "strong" },
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "p",
|
||||
typeName: "PTagHelper2",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: new[] { "br" },
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "strong",
|
||||
typeName: "StrongTagHelper",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "br",
|
||||
typeName: "BRTagHelper",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: null),
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new RazorError[0]);
|
||||
}
|
||||
|
||||
public static TheoryData AllowedChildrenData
|
||||
{
|
||||
get
|
||||
{
|
||||
var factory = CreateDefaultSpanFactory();
|
||||
var blockFactory = new BlockFactory(factory);
|
||||
Func<string, string, string, int, int, RazorError> nestedTagError =
|
||||
(childName, parentName, allowed, location, length) => new RazorError(
|
||||
RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag(
|
||||
childName,
|
||||
parentName,
|
||||
allowed),
|
||||
absoluteIndex: location,
|
||||
lineIndex: 0,
|
||||
columnIndex: location,
|
||||
length: length);
|
||||
Func<string, string, int, int, RazorError> nestedContentError =
|
||||
(parentName, allowed, location, length) => new RazorError(
|
||||
RazorResources.FormatTagHelperParseTreeRewriter_CannotHaveNonTagContent(parentName, allowed),
|
||||
absoluteIndex: location,
|
||||
lineIndex: 0,
|
||||
columnIndex: location,
|
||||
length: length);
|
||||
|
||||
return new TheoryData<string, IEnumerable<string>, MarkupBlock, RazorError[]>
|
||||
{
|
||||
{
|
||||
"<p><br /></p>",
|
||||
new[] { "br" },
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
new MarkupTagHelperBlock("br", TagMode.SelfClosing))),
|
||||
new RazorError[0]
|
||||
},
|
||||
{
|
||||
$"<p>{Environment.NewLine}<br />{Environment.NewLine}</p>",
|
||||
new[] { "br" },
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
factory.Markup(Environment.NewLine),
|
||||
new MarkupTagHelperBlock("br", TagMode.SelfClosing),
|
||||
factory.Markup(Environment.NewLine))),
|
||||
new RazorError[0]
|
||||
},
|
||||
{
|
||||
"<p><br></p>",
|
||||
new[] { "strong" },
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
new MarkupTagHelperBlock("br", TagMode.StartTagOnly))),
|
||||
new[] { nestedTagError("br", "p", "strong", 3, 4) }
|
||||
},
|
||||
{
|
||||
"<p>Hello</p>",
|
||||
new[] { "strong" },
|
||||
new MarkupBlock(new MarkupTagHelperBlock("p", factory.Markup("Hello"))),
|
||||
new[] { nestedContentError("p", "strong", 3, 5) }
|
||||
},
|
||||
{
|
||||
"<p><hr /></p>",
|
||||
new[] { "br", "strong" },
|
||||
new MarkupBlock(new MarkupTagHelperBlock("p", blockFactory.MarkupTagBlock("<hr />"))),
|
||||
new[] { nestedTagError("hr", "p", "br, strong", 3, 6) }
|
||||
},
|
||||
{
|
||||
"<p><br>Hello</p>",
|
||||
new[] { "strong" },
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
new MarkupTagHelperBlock("br", TagMode.StartTagOnly),
|
||||
factory.Markup("Hello"))),
|
||||
new[] { nestedTagError("br", "p", "strong", 3, 4), nestedContentError("p", "strong", 7, 5) }
|
||||
},
|
||||
{
|
||||
"<p><strong>Title:</strong><br />Something</p>",
|
||||
new[] { "strong" },
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
new MarkupTagHelperBlock("strong", factory.Markup("Title:")),
|
||||
new MarkupTagHelperBlock("br", TagMode.SelfClosing),
|
||||
factory.Markup("Something"))),
|
||||
new[]
|
||||
{
|
||||
nestedContentError("strong", "strong", 11, 6),
|
||||
nestedTagError("br", "p", "strong", 26, 6),
|
||||
nestedContentError("p", "strong", 32, 9),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<p><strong>Title:</strong><br />Something</p>",
|
||||
new[] { "strong", "br" },
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
new MarkupTagHelperBlock("strong", factory.Markup("Title:")),
|
||||
new MarkupTagHelperBlock("br", TagMode.SelfClosing),
|
||||
factory.Markup("Something"))),
|
||||
new[]
|
||||
{
|
||||
nestedContentError("strong", "strong, br", 11, 6),
|
||||
nestedContentError("p", "strong, br", 32, 9),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<p> <strong>Title:</strong> <br /> Something</p>",
|
||||
new[] { "strong", "br" },
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
factory.Markup(" "),
|
||||
new MarkupTagHelperBlock("strong", factory.Markup("Title:")),
|
||||
factory.Markup(" "),
|
||||
new MarkupTagHelperBlock("br", TagMode.SelfClosing),
|
||||
factory.Markup(" Something"))),
|
||||
new[]
|
||||
{
|
||||
nestedContentError("strong", "strong, br", 13, 6),
|
||||
nestedContentError("p", "strong, br", 38, 9),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<p><strong>Title:<br><em>A Very Cool</em></strong><br />Something</p>",
|
||||
new[] { "strong" },
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
new MarkupTagHelperBlock("strong",
|
||||
factory.Markup("Title:"),
|
||||
new MarkupTagHelperBlock("br", TagMode.StartTagOnly),
|
||||
blockFactory.MarkupTagBlock("<em>"),
|
||||
factory.Markup("A Very Cool"),
|
||||
blockFactory.MarkupTagBlock("</em>")),
|
||||
new MarkupTagHelperBlock("br", TagMode.SelfClosing),
|
||||
factory.Markup("Something"))),
|
||||
new[]
|
||||
{
|
||||
nestedContentError("strong", "strong", 11, 6),
|
||||
nestedTagError("br", "strong", "strong", 17, 4),
|
||||
nestedTagError("em", "strong", "strong", 21, 4),
|
||||
nestedContentError("strong", "strong", 25, 11),
|
||||
nestedTagError("em", "strong", "strong", 36, 5),
|
||||
nestedTagError("br", "p", "strong", 50, 6),
|
||||
nestedContentError("p", "strong", 56, 9)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<p><custom>Title:<br><em>A Very Cool</em></custom><br />Something</p>",
|
||||
new[] { "custom" },
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
blockFactory.MarkupTagBlock("<custom>"),
|
||||
factory.Markup("Title:"),
|
||||
new MarkupTagHelperBlock("br", TagMode.StartTagOnly),
|
||||
blockFactory.MarkupTagBlock("<em>"),
|
||||
factory.Markup("A Very Cool"),
|
||||
blockFactory.MarkupTagBlock("</em>"),
|
||||
blockFactory.MarkupTagBlock("</custom>"),
|
||||
new MarkupTagHelperBlock("br", TagMode.SelfClosing),
|
||||
factory.Markup("Something"))),
|
||||
new[]
|
||||
{
|
||||
nestedTagError("custom", "p", "custom", 3, 8),
|
||||
nestedContentError("p", "custom", 11, 6),
|
||||
nestedTagError("br", "p", "custom", 17, 4),
|
||||
nestedTagError("em", "p", "custom", 21, 4),
|
||||
nestedContentError("p", "custom", 25, 11),
|
||||
nestedTagError("em", "p", "custom", 36, 5),
|
||||
nestedTagError("custom", "p", "custom", 41, 9),
|
||||
nestedTagError("br", "p", "custom", 50, 6),
|
||||
nestedContentError("p", "custom", 56, 9)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(AllowedChildrenData))]
|
||||
public void Rewrite_UnderstandsAllowedChildren(
|
||||
string documentContent,
|
||||
IEnumerable<string> allowedChildren,
|
||||
MarkupBlock expectedOutput,
|
||||
RazorError[] expectedErrors)
|
||||
{
|
||||
// Arrange
|
||||
var descriptors = new TagHelperDescriptor[]
|
||||
{
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "p",
|
||||
typeName: "PTagHelper",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: allowedChildren,
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "strong",
|
||||
typeName: "StrongTagHelper",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: allowedChildren,
|
||||
tagStructure: TagStructure.Unspecified,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
tagName: "br",
|
||||
typeName: "BRTagHelper",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: null),
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rewrite_CanHandleStartTagOnlyTagTagMode()
|
||||
{
|
||||
|
|
@ -33,6 +477,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: null)
|
||||
};
|
||||
|
|
@ -68,6 +513,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: null)
|
||||
};
|
||||
|
|
@ -104,6 +550,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.WithoutEndTag,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
|
|
@ -113,6 +560,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
assemblyName: "SomeAssembly",
|
||||
attributes: new TagHelperAttributeDescriptor[0],
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: TagStructure.NormalOrSelfClosing,
|
||||
designTimeDescriptor: null)
|
||||
};
|
||||
|
|
@ -1022,6 +1470,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
assemblyName: "SomeAssembly",
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
|
|
@ -1039,6 +1488,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
designTimeDescriptor: null),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null)
|
||||
};
|
||||
|
|
@ -1051,6 +1501,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
assemblyName: "SomeAssembly",
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperDescriptor(
|
||||
|
|
@ -1068,6 +1519,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
designTimeDescriptor: null),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null)
|
||||
};
|
||||
|
|
@ -1080,6 +1532,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
assemblyName: "SomeAssembly",
|
||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
allowedChildren: null,
|
||||
tagStructure: default(TagStructure),
|
||||
designTimeDescriptor: null),
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue