public void Execute_BindFallback_CreatesDescriptor() { // Arrange var compilation = BaseCompilation; Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new BindTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var bind = Assert.Single(context.Results, r => r.IsFallbackBindTagHelper()); // These are features Bind Tags Helpers don't use. Verifying them once here and // then ignoring them. Assert.Empty(bind.AllowedChildTags); Assert.Null(bind.TagOutputHint); // These are features that are invariants of all Bind Tag Helpers. Verifying them once // here and then ignoring them. Assert.Empty(bind.Diagnostics); Assert.False(bind.HasErrors); Assert.Equal(ComponentMetadata.Bind.TagHelperKind, bind.Kind); Assert.Equal(bool.TrueString, bind.Metadata[TagHelperMetadata.Common.ClassifyAttributesOnly]); Assert.Equal(ComponentMetadata.Bind.RuntimeName, bind.Metadata[TagHelperMetadata.Runtime.Name]); Assert.False(bind.IsDefaultKind()); Assert.False(bind.KindUsesDefaultTagHelperRuntime()); Assert.False(bind.IsComponentOrChildContentTagHelper()); Assert.True(bind.CaseSensitive); Assert.False(bind.Metadata.ContainsKey(ComponentMetadata.Bind.ValueAttribute)); Assert.False(bind.Metadata.ContainsKey(ComponentMetadata.Bind.ChangeAttribute)); Assert.True(bind.IsFallbackBindTagHelper()); Assert.Equal( "Binds the provided expression to an attribute and a change event, based on the naming of " + "the bind attribute. For example: <code>@bind-value=\"...\"</code> and <code>@bind-value:event=\"onchange\"</code> will assign the " + "current value of the expression to the 'value' attribute, and assign a delegate that attempts " + "to set the value to the 'onchange' attribute.", bind.Documentation); // These are all trivially derived from the assembly/namespace/type name Assert.Equal("Microsoft.AspNetCore.Components", bind.AssemblyName); Assert.Equal("Bind", bind.Name); Assert.Equal("Microsoft.AspNetCore.Components.Bind", bind.DisplayName); Assert.Equal("Microsoft.AspNetCore.Components.Bind", bind.GetTypeName()); // The tag matching rule for a bind-Component is always the component name + the attribute name var rule = Assert.Single(bind.TagMatchingRules); Assert.Empty(rule.Diagnostics); Assert.False(rule.HasErrors); Assert.Null(rule.ParentTag); Assert.Equal("*", rule.TagName); Assert.Equal(TagStructure.Unspecified, rule.TagStructure); var requiredAttribute = Assert.Single(rule.Attributes); Assert.Empty(requiredAttribute.Diagnostics); Assert.Equal("@bind-...", requiredAttribute.DisplayName); Assert.Equal("@bind-", requiredAttribute.Name); Assert.Equal(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch, requiredAttribute.NameComparison); Assert.Null(requiredAttribute.Value); Assert.Equal(RequiredAttributeDescriptor.ValueComparisonMode.None, requiredAttribute.ValueComparison); var attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("@bind", StringComparison.Ordinal)); // Invariants Assert.Empty(attribute.Diagnostics); Assert.False(attribute.HasErrors); Assert.Equal(ComponentMetadata.Bind.TagHelperKind, attribute.Kind); Assert.False(attribute.IsDefaultKind()); Assert.False(attribute.IsIndexerBooleanProperty); Assert.False(attribute.IsIndexerStringProperty); Assert.True(attribute.HasIndexer); Assert.Equal("@bind-", attribute.IndexerNamePrefix); Assert.Equal("System.Object", attribute.IndexerTypeName); Assert.Equal( "Binds the provided expression to an attribute and a change event, based on the naming of " + "the bind attribute. For example: <code>@bind-value=\"...\"</code> and <code>@bind-value:event=\"onchange\"</code> will assign the " + "current value of the expression to the 'value' attribute, and assign a delegate that attempts " + "to set the value to the 'onchange' attribute.", attribute.Documentation); Assert.Equal("@bind-...", attribute.Name); Assert.Equal("Bind", attribute.GetPropertyName()); Assert.Equal( "System.Collections.Generic.Dictionary<string, object> Microsoft.AspNetCore.Components.Bind.Bind", attribute.DisplayName); // Defined from the property type Assert.Equal("System.Collections.Generic.Dictionary<string, object>", attribute.TypeName); Assert.False(attribute.IsStringProperty); Assert.False(attribute.IsBooleanProperty); Assert.False(attribute.IsEnum); var parameter = Assert.Single(attribute.BoundAttributeParameters, a => a.Name.Equals("format")); // Invariants Assert.Empty(parameter.Diagnostics); Assert.False(parameter.HasErrors); Assert.Equal(ComponentMetadata.Bind.TagHelperKind, parameter.Kind); Assert.False(parameter.IsDefaultKind()); Assert.Equal( "Specifies a format to convert the value specified by the corresponding bind attribute. " + "For example: <code>@bind-value:format=\"...\"</code> will apply a format string to the value " + "specified in <code>@bind-value=\"...\"</code>. The format string can currently only be used with " + "expressions of type <code>DateTime</code>.", parameter.Documentation); Assert.Equal("format", parameter.Name); Assert.Equal("Format", parameter.GetPropertyName()); Assert.Equal(":format", parameter.DisplayName); // Defined from the property type Assert.Equal("System.String", parameter.TypeName); Assert.True(parameter.IsStringProperty); Assert.False(parameter.IsBooleanProperty); Assert.False(parameter.IsEnum); parameter = Assert.Single(attribute.BoundAttributeParameters, a => a.Name.Equals("culture")); // Invariants Assert.Empty(parameter.Diagnostics); Assert.False(parameter.HasErrors); Assert.Equal(ComponentMetadata.Bind.TagHelperKind, parameter.Kind); Assert.False(parameter.IsDefaultKind()); Assert.Equal( "Specifies the culture to use for conversions.", parameter.Documentation); Assert.Equal("culture", parameter.Name); Assert.Equal("Culture", parameter.GetPropertyName()); Assert.Equal(":culture", parameter.DisplayName); // Defined from the property type Assert.Equal("System.Globalization.CultureInfo", parameter.TypeName); Assert.False(parameter.IsStringProperty); Assert.False(parameter.IsBooleanProperty); Assert.False(parameter.IsEnum); }
public void Execute_BindOnInputElementWithTypeAttributeAndSuffix_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Components; namespace Test { [BindInputElement(""checkbox"", ""somevalue"", ""myprop"", ""myevent"", false, null)] public class BindAttributes { } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new BindTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var matches = GetBindTagHelpers(context); matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 0); var bind = Assert.Single(matches); Assert.Equal("myprop", bind.Metadata[ComponentMetadata.Bind.ValueAttribute]); Assert.Equal("myevent", bind.Metadata[ComponentMetadata.Bind.ChangeAttribute]); Assert.Equal("checkbox", bind.Metadata[ComponentMetadata.Bind.TypeAttribute]); Assert.True(bind.IsInputElementBindTagHelper()); Assert.False(bind.IsInputElementFallbackBindTagHelper()); Assert.False(bind.IsInvariantCultureBindTagHelper()); Assert.Null(bind.GetFormat()); var rule = Assert.Single(bind.TagMatchingRules); Assert.Equal("input", rule.TagName); Assert.Equal(TagStructure.Unspecified, rule.TagStructure); Assert.Collection( rule.Attributes, a => { Assert.Equal("type", a.DisplayName); Assert.Equal("type", a.Name); Assert.Equal(RequiredAttributeDescriptor.NameComparisonMode.FullMatch, a.NameComparison); Assert.Equal("checkbox", a.Value); Assert.Equal(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch, a.ValueComparison); }, a => { Assert.Equal("@bind-somevalue", a.DisplayName); Assert.Equal("@bind-somevalue", a.Name); }); var attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("@bind", StringComparison.Ordinal)); Assert.Equal("@bind-somevalue", attribute.Name); Assert.Equal("Bind_somevalue", attribute.GetPropertyName()); Assert.Equal("object Test.BindAttributes.Bind_somevalue", attribute.DisplayName); var parameter = Assert.Single(attribute.BoundAttributeParameters, a => a.Name.Equals("format")); Assert.Equal("format", parameter.Name); Assert.Equal("Format_somevalue", parameter.GetPropertyName()); Assert.Equal(":format", parameter.DisplayName); }
public void Execute_FindsBindTagHelperOnComponentType_Delegate_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using System; using System.Linq.Expressions; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; namespace Test { public class MyComponent : IComponent { public void Attach(RenderHandle renderHandle) { } public Task SetParametersAsync(ParameterView parameters) { return Task.CompletedTask; } [Parameter] public string MyProperty { get; set; } [Parameter] public Action<string> MyPropertyChanged { get; set; } [Parameter] public Expression<Func<string>> MyPropertyExpression { get; set; } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); // We run after component discovery and depend on the results. var componentProvider = new ComponentTagHelperDescriptorProvider(); componentProvider.Execute(context); var provider = new BindTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var matches = GetBindTagHelpers(context); matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 1); var bind = Assert.Single(matches); // These are features Bind Tags Helpers don't use. Verifying them once here and // then ignoring them. Assert.Empty(bind.AllowedChildTags); Assert.Null(bind.TagOutputHint); // These are features that are invariants of all Bind Tag Helpers. Verifying them once // here and then ignoring them. Assert.Empty(bind.Diagnostics); Assert.False(bind.HasErrors); Assert.Equal(ComponentMetadata.Bind.TagHelperKind, bind.Kind); Assert.Equal(ComponentMetadata.Bind.RuntimeName, bind.Metadata[TagHelperMetadata.Runtime.Name]); Assert.False(bind.IsDefaultKind()); Assert.False(bind.KindUsesDefaultTagHelperRuntime()); Assert.False(bind.IsComponentOrChildContentTagHelper()); Assert.True(bind.CaseSensitive); Assert.Equal("MyProperty", bind.Metadata[ComponentMetadata.Bind.ValueAttribute]); Assert.Equal("MyPropertyChanged", bind.Metadata[ComponentMetadata.Bind.ChangeAttribute]); Assert.Equal("MyPropertyExpression", bind.Metadata[ComponentMetadata.Bind.ExpressionAttribute]); Assert.Equal( "Binds the provided expression to the 'MyProperty' property and a change event " + "delegate to the 'MyPropertyChanged' property of the component.", bind.Documentation); // These are all trivially derived from the assembly/namespace/type name Assert.Equal("TestAssembly", bind.AssemblyName); Assert.Equal("Test.MyComponent", bind.Name); Assert.Equal("Test.MyComponent", bind.DisplayName); Assert.Equal("Test.MyComponent", bind.GetTypeName()); var rule = Assert.Single(bind.TagMatchingRules); Assert.Empty(rule.Diagnostics); Assert.False(rule.HasErrors); Assert.Null(rule.ParentTag); Assert.Equal("MyComponent", rule.TagName); Assert.Equal(TagStructure.Unspecified, rule.TagStructure); var requiredAttribute = Assert.Single(rule.Attributes); Assert.Empty(requiredAttribute.Diagnostics); Assert.Equal("@bind-MyProperty", requiredAttribute.DisplayName); Assert.Equal("@bind-MyProperty", requiredAttribute.Name); Assert.Equal(RequiredAttributeDescriptor.NameComparisonMode.FullMatch, requiredAttribute.NameComparison); Assert.Null(requiredAttribute.Value); Assert.Equal(RequiredAttributeDescriptor.ValueComparisonMode.None, requiredAttribute.ValueComparison); var attribute = Assert.Single(bind.BoundAttributes); // Invariants Assert.Empty(attribute.Diagnostics); Assert.False(attribute.HasErrors); Assert.Equal(ComponentMetadata.Bind.TagHelperKind, attribute.Kind); Assert.False(attribute.IsDefaultKind()); Assert.False(attribute.HasIndexer); Assert.Null(attribute.IndexerNamePrefix); Assert.Null(attribute.IndexerTypeName); Assert.False(attribute.IsIndexerBooleanProperty); Assert.False(attribute.IsIndexerStringProperty); Assert.Equal( "Binds the provided expression to the 'MyProperty' property and a change event " + "delegate to the 'MyPropertyChanged' property of the component.", attribute.Documentation); Assert.Equal("@bind-MyProperty", attribute.Name); Assert.Equal("MyProperty", attribute.GetPropertyName()); Assert.Equal("System.Action<System.String> Test.MyComponent.MyProperty", attribute.DisplayName); // Defined from the property type Assert.Equal("System.Action<System.String>", attribute.TypeName); Assert.False(attribute.IsStringProperty); Assert.False(attribute.IsBooleanProperty); Assert.False(attribute.IsEnum); }
public void Execute_BindOnElement_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Components; namespace Test { [BindElement(""div"", null, ""myprop"", ""myevent"")] public class BindAttributes { } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new BindTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var matches = GetBindTagHelpers(context); matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 0); var bind = Assert.Single(matches); // These are features Bind Tags Helpers don't use. Verifying them once here and // then ignoring them. Assert.Empty(bind.AllowedChildTags); Assert.Null(bind.TagOutputHint); // These are features that are invariants of all Bind Tag Helpers. Verifying them once // here and then ignoring them. Assert.Empty(bind.Diagnostics); Assert.False(bind.HasErrors); Assert.Equal(ComponentMetadata.Bind.TagHelperKind, bind.Kind); Assert.Equal(bool.TrueString, bind.Metadata[TagHelperMetadata.Common.ClassifyAttributesOnly]); Assert.Equal(ComponentMetadata.Bind.RuntimeName, bind.Metadata[TagHelperMetadata.Runtime.Name]); Assert.False(bind.IsDefaultKind()); Assert.False(bind.KindUsesDefaultTagHelperRuntime()); Assert.False(bind.IsComponentOrChildContentTagHelper()); Assert.True(bind.CaseSensitive); Assert.Equal("myprop", bind.Metadata[ComponentMetadata.Bind.ValueAttribute]); Assert.Equal("myevent", bind.Metadata[ComponentMetadata.Bind.ChangeAttribute]); Assert.False(bind.IsInputElementBindTagHelper()); Assert.False(bind.IsInputElementFallbackBindTagHelper()); Assert.Equal( "Binds the provided expression to the 'myprop' attribute and a change event " + "delegate to the 'myevent' attribute.", bind.Documentation); // These are all trivially derived from the assembly/namespace/type name Assert.Equal("Microsoft.AspNetCore.Components", bind.AssemblyName); Assert.Equal("Bind", bind.Name); Assert.Equal("Test.BindAttributes", bind.DisplayName); Assert.Equal("Test.BindAttributes", bind.GetTypeName()); // The tag matching rule for a bind-Component is always the component name + the attribute name var rule = Assert.Single(bind.TagMatchingRules); Assert.Empty(rule.Diagnostics); Assert.False(rule.HasErrors); Assert.Null(rule.ParentTag); Assert.Equal("div", rule.TagName); Assert.Equal(TagStructure.Unspecified, rule.TagStructure); var requiredAttribute = Assert.Single(rule.Attributes); Assert.Empty(requiredAttribute.Diagnostics); Assert.Equal("@bind", requiredAttribute.DisplayName); Assert.Equal("@bind", requiredAttribute.Name); Assert.Equal(RequiredAttributeDescriptor.NameComparisonMode.FullMatch, requiredAttribute.NameComparison); Assert.Null(requiredAttribute.Value); Assert.Equal(RequiredAttributeDescriptor.ValueComparisonMode.None, requiredAttribute.ValueComparison); var attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("@bind", StringComparison.Ordinal)); // Invariants Assert.Empty(attribute.Diagnostics); Assert.False(attribute.HasErrors); Assert.Equal(ComponentMetadata.Bind.TagHelperKind, attribute.Kind); Assert.False(attribute.IsDefaultKind()); Assert.False(attribute.HasIndexer); Assert.Null(attribute.IndexerNamePrefix); Assert.Null(attribute.IndexerTypeName); Assert.False(attribute.IsIndexerBooleanProperty); Assert.False(attribute.IsIndexerStringProperty); Assert.Equal( "Binds the provided expression to the 'myprop' attribute and a change event " + "delegate to the 'myevent' attribute.", attribute.Documentation); Assert.Equal("@bind", attribute.Name); Assert.Equal("Bind", attribute.GetPropertyName()); Assert.Equal("object Test.BindAttributes.Bind", attribute.DisplayName); // Defined from the property type Assert.Equal("System.Object", attribute.TypeName); Assert.False(attribute.IsStringProperty); Assert.False(attribute.IsBooleanProperty); Assert.False(attribute.IsEnum); var parameter = Assert.Single(attribute.BoundAttributeParameters, a => a.Name.Equals("format")); // Invariants Assert.Empty(parameter.Diagnostics); Assert.False(parameter.HasErrors); Assert.Equal(ComponentMetadata.Bind.TagHelperKind, parameter.Kind); Assert.False(parameter.IsDefaultKind()); Assert.Equal( "Specifies a format to convert the value specified by the '@bind' attribute. " + "The format string can currently only be used with expressions of type <code>DateTime</code>.", parameter.Documentation); Assert.Equal("format", parameter.Name); Assert.Equal("Format_myprop", parameter.GetPropertyName()); Assert.Equal(":format", parameter.DisplayName); // Defined from the property type Assert.Equal("System.String", parameter.TypeName); Assert.True(parameter.IsStringProperty); Assert.False(parameter.IsBooleanProperty); Assert.False(parameter.IsEnum); parameter = Assert.Single(attribute.BoundAttributeParameters, a => a.Name.Equals("culture")); // Invariants Assert.Empty(parameter.Diagnostics); Assert.False(parameter.HasErrors); Assert.Equal(ComponentMetadata.Bind.TagHelperKind, parameter.Kind); Assert.False(parameter.IsDefaultKind()); Assert.Equal( "Specifies the culture to use for conversions.", parameter.Documentation); Assert.Equal("culture", parameter.Name); Assert.Equal("Culture", parameter.GetPropertyName()); Assert.Equal(":culture", parameter.DisplayName); // Defined from the property type Assert.Equal("System.Globalization.CultureInfo", parameter.TypeName); Assert.False(parameter.IsStringProperty); Assert.False(parameter.IsBooleanProperty); Assert.False(parameter.IsEnum); }
public void Execute_BindOnInputElementWithTypeAttribute_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(@" using Microsoft.AspNetCore.Blazor.Components; namespace Test { [BindInputElement(""checkbox"", null, ""myprop"", ""myevent"")] public class BindAttributes { } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new BindTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var matches = GetBindTagHelpers(context); var bind = Assert.Single(matches); Assert.Equal("myprop", bind.Metadata[BlazorMetadata.Bind.ValueAttribute]); Assert.Equal("myevent", bind.Metadata[BlazorMetadata.Bind.ChangeAttribute]); Assert.Equal("checkbox", bind.Metadata[BlazorMetadata.Bind.TypeAttribute]); Assert.True(bind.IsInputElementBindTagHelper()); Assert.False(bind.IsInputElementFallbackBindTagHelper()); var rule = Assert.Single(bind.TagMatchingRules); Assert.Equal("input", rule.TagName); Assert.Equal(TagStructure.Unspecified, rule.TagStructure); Assert.Collection( rule.Attributes, a => { Assert.Equal("type", a.DisplayName); Assert.Equal("type", a.Name); Assert.Equal(RequiredAttributeDescriptor.NameComparisonMode.FullMatch, a.NameComparison); Assert.Equal("checkbox", a.Value); Assert.Equal(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch, a.ValueComparison); }, a => { Assert.Equal("bind", a.DisplayName); Assert.Equal("bind", a.Name); }); var attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("bind")); Assert.Equal("bind", attribute.Name); Assert.Equal("Bind", attribute.GetPropertyName()); Assert.Equal("object Test.BindAttributes.Bind", attribute.DisplayName); attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("format")); Assert.Equal("format-myprop", attribute.Name); Assert.Equal("Format_myprop", attribute.GetPropertyName()); Assert.Equal("string Test.BindAttributes.Format_myprop", attribute.DisplayName); }