Consume `EditorBrowsableAttribute` on `TagHelper`s.

- Changed `TagHelperDescriptorFactory` to not create individual descriptors when `EditorBrowsableAttribute` is present and set to `EditorBrowsableState.Never`.
- Added tests to validate the `TagHelperDescriptorFactory` creates the attribute correctly.
- Did not look down the inheritance chain for `EditorBrowsableAttribute` because `TargetElement` is not inherited.

#447
This commit is contained in:
N. Taylor Mullen 2015-07-13 16:21:20 -07:00
parent fe28081c34
commit b762830a43
2 changed files with 309 additions and 0 deletions

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
@ -52,6 +53,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
[NotNull] ErrorSink errorSink)
{
var typeInfo = type.GetTypeInfo();
if (ShouldSkipDescriptorCreation(designTime, typeInfo))
{
return Enumerable.Empty<TagHelperDescriptor>();
}
var attributeDescriptors = GetAttributeDescriptors(type, designTime, errorSink);
var targetElementAttributes = GetValidTargetElementAttributes(typeInfo, errorSink);
@ -264,6 +271,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
var accessibleProperties = type.GetRuntimeProperties().Where(IsAccessibleProperty);
foreach (var property in accessibleProperties)
{
if (ShouldSkipDescriptorCreation(designTime, property))
{
return Enumerable.Empty<TagHelperAttributeDescriptor>();
}
var attributeNameAttribute = property.GetCustomAttribute<HtmlAttributeNameAttribute>(inherit: false);
var hasExplicitName =
attributeNameAttribute != null && !string.IsNullOrEmpty(attributeNameAttribute.Name);
@ -364,6 +376,18 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
nameOrPrefix);
}
private static bool ShouldSkipDescriptorCreation(bool designTime, MemberInfo memberInfo)
{
if (designTime)
{
var editorBrowsableAttribute = memberInfo.GetCustomAttribute<EditorBrowsableAttribute>(inherit: false);
return editorBrowsableAttribute != null && editorBrowsableAttribute.State == EditorBrowsableState.Never;
}
return false;
}
private static bool ValidateTagHelperAttributeNameOrPrefix(
string attributeNameOrPrefix,
Type parentType,

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Razor.TagHelpers;
@ -15,6 +16,237 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
private static readonly string AssemblyName =
typeof(TagHelperDescriptorFactoryTest).GetTypeInfo().Assembly.GetName().Name;
public static TheoryData EditorBrowsableData
{
get
{
// tagHelperType, designTime, expectedDescriptors
return new TheoryData<Type, bool, TagHelperDescriptor[]>
{
{
typeof(InheritedEditorBrowsableTagHelper),
true,
new[]
{
new TagHelperDescriptor(
tagName: "inherited-editor-browsable",
typeName: typeof(InheritedEditorBrowsableTagHelper).FullName,
assemblyName: AssemblyName,
attributes: new[]
{
new TagHelperAttributeDescriptor(
name: "property",
propertyName: nameof(InheritedEditorBrowsableTagHelper.Property),
typeName: typeof(int).FullName,
isIndexer: false,
designTimeDescriptor: null)
})
}
},
{ typeof(EditorBrowsableTagHelper), true, new TagHelperDescriptor[0] },
{
typeof(EditorBrowsableTagHelper),
false,
new[]
{
new TagHelperDescriptor(
tagName: "editor-browsable",
typeName: typeof(EditorBrowsableTagHelper).FullName,
assemblyName: AssemblyName,
attributes: new[]
{
new TagHelperAttributeDescriptor(
name: "property",
propertyName: nameof(EditorBrowsableTagHelper.Property),
typeName: typeof(int).FullName,
isIndexer: false,
designTimeDescriptor: null)
})
}
},
{
typeof(HiddenPropertyEditorBrowsableTagHelper),
true,
new[]
{
new TagHelperDescriptor(
tagName: "hidden-property-editor-browsable",
typeName: typeof(HiddenPropertyEditorBrowsableTagHelper).FullName,
assemblyName: AssemblyName,
attributes: new TagHelperAttributeDescriptor[0])
}
},
{
typeof(HiddenPropertyEditorBrowsableTagHelper),
false,
new[]
{
new TagHelperDescriptor(
tagName: "hidden-property-editor-browsable",
typeName: typeof(HiddenPropertyEditorBrowsableTagHelper).FullName,
assemblyName: AssemblyName,
attributes: new[]
{
new TagHelperAttributeDescriptor(
name: "property",
propertyName: nameof(HiddenPropertyEditorBrowsableTagHelper.Property),
typeName: typeof(int).FullName,
isIndexer: false,
designTimeDescriptor: null)
})
}
},
{
typeof(OverriddenEditorBrowsableTagHelper),
true,
new[]
{
new TagHelperDescriptor(
tagName: "overridden-editor-browsable",
typeName: typeof(OverriddenEditorBrowsableTagHelper).FullName,
assemblyName: AssemblyName,
attributes: new[]
{
new TagHelperAttributeDescriptor(
name: "property",
propertyName: nameof(OverriddenEditorBrowsableTagHelper.Property),
typeName: typeof(int).FullName,
isIndexer: false,
designTimeDescriptor: null)
})
}
},
{
typeof(MultiPropertyEditorBrowsableTagHelper),
true,
new[]
{
new TagHelperDescriptor(
tagName: "multi-property-editor-browsable",
typeName: typeof(MultiPropertyEditorBrowsableTagHelper).FullName,
assemblyName: AssemblyName,
attributes: new[]
{
new TagHelperAttributeDescriptor(
name: "property2",
propertyName: nameof(MultiPropertyEditorBrowsableTagHelper.Property2),
typeName: typeof(int).FullName,
isIndexer: false,
designTimeDescriptor: null)
})
}
},
{
typeof(MultiPropertyEditorBrowsableTagHelper),
false,
new[]
{
new TagHelperDescriptor(
tagName: "multi-property-editor-browsable",
typeName: typeof(MultiPropertyEditorBrowsableTagHelper).FullName,
assemblyName: AssemblyName,
attributes: new[]
{
new TagHelperAttributeDescriptor(
name: "property",
propertyName: nameof(MultiPropertyEditorBrowsableTagHelper.Property),
typeName: typeof(int).FullName,
isIndexer: false,
designTimeDescriptor: null),
new TagHelperAttributeDescriptor(
name: "property2",
propertyName: nameof(MultiPropertyEditorBrowsableTagHelper.Property2),
typeName: typeof(int).FullName,
isIndexer: false,
designTimeDescriptor: null)
})
}
},
{
typeof(OverriddenPropertyEditorBrowsableTagHelper),
true,
new[]
{
new TagHelperDescriptor(
tagName: "overridden-property-editor-browsable",
typeName: typeof(OverriddenPropertyEditorBrowsableTagHelper).FullName,
assemblyName: AssemblyName,
attributes: new TagHelperAttributeDescriptor[0])
}
},
{
typeof(OverriddenPropertyEditorBrowsableTagHelper),
false,
new[]
{
new TagHelperDescriptor(
tagName: "overridden-property-editor-browsable",
typeName: typeof(OverriddenPropertyEditorBrowsableTagHelper).FullName,
assemblyName: AssemblyName,
attributes: new[]
{
new TagHelperAttributeDescriptor(
name: "property",
propertyName: nameof(OverriddenPropertyEditorBrowsableTagHelper.Property),
typeName: typeof(int).FullName,
isIndexer: false,
designTimeDescriptor: null),
new TagHelperAttributeDescriptor(
name: "property2",
propertyName: nameof(OverriddenPropertyEditorBrowsableTagHelper.Property2),
typeName: typeof(int).FullName,
isIndexer: false,
designTimeDescriptor: null)
})
}
},
{
typeof(DefaultEditorBrowsableTagHelper),
true,
new[]
{
new TagHelperDescriptor(
tagName: "default-editor-browsable",
typeName: typeof(DefaultEditorBrowsableTagHelper).FullName,
assemblyName: AssemblyName,
attributes: new[]
{
new TagHelperAttributeDescriptor(
name: "property",
propertyName: nameof(DefaultEditorBrowsableTagHelper.Property),
typeName: typeof(int).FullName,
isIndexer: false,
designTimeDescriptor: null)
})
}
},
{ typeof(MultiEditorBrowsableTagHelper), true, new TagHelperDescriptor[0] }
};
}
}
[Theory]
[MemberData(nameof(EditorBrowsableData))]
public void CreateDescriptors_UnderstandsEditorBrowsableAttribute(
Type tagHelperType,
bool designTime,
TagHelperDescriptor[] expectedDescriptors)
{
// Arrange
var errorSink = new ErrorSink();
// Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName,
tagHelperType,
designTime,
errorSink);
// Assert
Assert.Empty(errorSink.Errors);
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.Default);
}
public static TheoryData AttributeTargetData
{
get
@ -1564,6 +1796,59 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
return data;
}
[EditorBrowsable(EditorBrowsableState.Always)]
private class DefaultEditorBrowsableTagHelper : TagHelper
{
[EditorBrowsable(EditorBrowsableState.Always)]
public int Property { get; set; }
}
private class HiddenPropertyEditorBrowsableTagHelper : TagHelper
{
[EditorBrowsable(EditorBrowsableState.Never)]
public int Property { get; set; }
}
private class MultiPropertyEditorBrowsableTagHelper : TagHelper
{
[EditorBrowsable(EditorBrowsableState.Never)]
public int Property { get; set; }
public virtual int Property2 { get; set; }
}
private class OverriddenPropertyEditorBrowsableTagHelper : MultiPropertyEditorBrowsableTagHelper
{
[EditorBrowsable(EditorBrowsableState.Never)]
public override int Property2 { get; set; }
}
[EditorBrowsable(EditorBrowsableState.Never)]
private class EditorBrowsableTagHelper : TagHelper
{
[EditorBrowsable(EditorBrowsableState.Never)]
public virtual int Property { get; set; }
}
private class InheritedEditorBrowsableTagHelper : EditorBrowsableTagHelper
{
public override int Property { get; set; }
}
[EditorBrowsable(EditorBrowsableState.Advanced)]
private class OverriddenEditorBrowsableTagHelper : EditorBrowsableTagHelper
{
[EditorBrowsable(EditorBrowsableState.Advanced)]
public override int Property { get; set; }
}
[TargetElement("p")]
[TargetElement("div")]
[EditorBrowsable(EditorBrowsableState.Never)]
private class MultiEditorBrowsableTagHelper : TagHelper
{
}
[TargetElement(Attributes = "class*")]
private class AttributeWildcardTargetingTagHelper : TagHelper
{