public void Execute_BindOnInputElementWithoutTypeAttribute_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Components; namespace Test { [BindInputElement(null, 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); Assert.Equal("myprop", bind.Metadata[ComponentMetadata.Bind.ValueAttribute]); Assert.Equal("myevent", bind.Metadata[ComponentMetadata.Bind.ChangeAttribute]); Assert.False(bind.Metadata.ContainsKey(ComponentMetadata.Bind.TypeAttribute)); Assert.True(bind.IsInputElementBindTagHelper()); Assert.True(bind.IsInputElementFallbackBindTagHelper()); var rule = Assert.Single(bind.TagMatchingRules); Assert.Equal("input", rule.TagName); Assert.Equal(TagStructure.Unspecified, rule.TagStructure); var requiredAttribute = Assert.Single(rule.Attributes); Assert.Equal("@bind", requiredAttribute.DisplayName); Assert.Equal("@bind", requiredAttribute.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); var parameter = Assert.Single(attribute.BoundAttributeParameters, a => a.Name.Equals("format")); Assert.Equal("format", parameter.Name); Assert.Equal("Format_myprop", parameter.GetPropertyName()); Assert.Equal(":format", parameter.DisplayName); }
public void Execute_DelegateProperty_CreatesDescriptor_Generic() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using System; using Microsoft.AspNetCore.Blazor.Components; namespace Test { public class MyComponent<T> : BlazorComponent { [Parameter] Action<T> OnClick { get; set; } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components); Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent<T>", component.Name); Assert.Collection( component.BoundAttributes.OrderBy(a => a.Name), a => { Assert.Equal("OnClick", a.Name); Assert.Equal("System.Action<T>", a.TypeName); Assert.False(a.HasIndexer); Assert.False(a.IsBooleanProperty); Assert.False(a.IsEnum); Assert.False(a.IsStringProperty); Assert.True(a.IsDelegateProperty()); Assert.False(a.IsChildContentProperty()); Assert.True(a.IsGenericTypedProperty()); }, a => { Assert.Equal("T", a.Name); Assert.Equal("T", a.GetPropertyName()); Assert.Equal("T", a.DisplayName); Assert.Equal("System.Type", a.TypeName); Assert.True(a.IsTypeParameterProperty()); }); }
public RazorCodeDocument Update(string filePath, string content) { var obj = FileSystem.GetItem(filePath, fileKind: FileKinds.Component); if (obj.Exists && obj is TestRazorProjectItem item) { Declarations.TryGetValue(filePath, out var existing); var declaration = Engine.ProcessDeclarationOnly(item); var declarationText = declaration.GetCSharpDocument().GeneratedCode; // Updating a declaration, create a new compilation if (!string.Equals(existing, declarationText, StringComparison.Ordinal)) { Declarations[filePath] = declarationText; // Yeet the old one. References.RemoveAt(References.Count - 1); var compilation = BaseCompilation.AddSyntaxTrees(Declarations.Select(kvp => { return(CSharpSyntaxTree.ParseText(kvp.Value, path: kvp.Key)); })); References.Add(compilation.ToMetadataReference()); } item.Content = content ?? string.Empty; var generated = Engine.Process(item); return(generated); } throw new InvalidOperationException($"Cannot find item '{filePath}'."); }
[Fact] // This component has lots of properties that don't become components. public void Execute_IgnoredProperties_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Blazor.Components; namespace Test { public abstract class MyBase : BlazorComponent { [Parameter] protected string Hidden { get; set; } } public class MyComponent : MyBase { [Parameter] string NoSetter { get; } [Parameter] static string StaticProperty { get; set; } public string NoParameterAttribute { get; set; } // No attribute here, hides base-class property of the same name. protected new int Hidden { get; set; } public string this[int i] { get { throw null; } set { throw null; } } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components); Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent", component.Name); Assert.Empty(component.BoundAttributes); }
public void Execute_RenderFragmentProperty_CreatesDescriptors() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Blazor; using Microsoft.AspNetCore.Blazor.Components; namespace Test { public class MyComponent : BlazorComponent { [Parameter] RenderFragment ChildContent2 { get; set; } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components, c => c.IsComponentTagHelper()); Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent", component.Name); var attribute = Assert.Single(component.BoundAttributes); Assert.Equal("ChildContent2", attribute.Name); Assert.Equal("Microsoft.AspNetCore.Blazor.RenderFragment", attribute.TypeName); Assert.False(attribute.HasIndexer); Assert.False(attribute.IsBooleanProperty); Assert.False(attribute.IsEnum); Assert.False(attribute.IsStringProperty); Assert.False(attribute.IsDelegateProperty()); // We treat RenderFragment as separate from generalized delegates Assert.True(attribute.IsChildContentProperty()); Assert.False(attribute.IsParameterizedChildContentProperty()); var childContent = Assert.Single(components, c => c.IsChildContentTagHelper()); Assert.Equal("TestAssembly", childContent.AssemblyName); Assert.Equal("Test.MyComponent.ChildContent2", childContent.Name); Assert.Empty(childContent.BoundAttributes); }
public void Execute_BindOnElementWithSuffix_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Components; namespace Test { [BindElement(""div"", ""myprop"", ""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[ComponentMetadata.Bind.ValueAttribute]); Assert.Equal("myevent", bind.Metadata[ComponentMetadata.Bind.ChangeAttribute]); Assert.False(bind.IsInputElementBindTagHelper()); Assert.False(bind.IsInputElementFallbackBindTagHelper()); var rule = Assert.Single(bind.TagMatchingRules); Assert.Equal("div", rule.TagName); Assert.Equal(TagStructure.Unspecified, rule.TagStructure); var requiredAttribute = Assert.Single(rule.Attributes); Assert.Equal("bind-myprop", requiredAttribute.DisplayName); Assert.Equal("bind-myprop", requiredAttribute.Name); var attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("bind")); Assert.Equal("bind-myprop", attribute.Name); Assert.Equal("Bind_myprop", attribute.GetPropertyName()); Assert.Equal("object Test.BindAttributes.Bind_myprop", 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); }
public void Execute_GenericProperty_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Components; namespace Test { public class MyComponent<T> : ComponentBase { [Parameter] T MyProperty { get; set; } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components); Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent<T>", component.Name); Assert.Collection( component.BoundAttributes.OrderBy(a => a.Name), a => { Assert.Equal("MyProperty", a.Name); Assert.Equal("MyProperty", a.GetPropertyName()); Assert.Equal("T Test.MyComponent<T>.MyProperty", a.DisplayName); Assert.Equal("T", a.TypeName); Assert.True(a.IsGenericTypedProperty()); }, a => { Assert.Equal("T", a.Name); Assert.Equal("T", a.GetPropertyName()); Assert.Equal("T", a.DisplayName); Assert.Equal("System.Type", a.TypeName); Assert.True(a.IsTypeParameterProperty()); }); }
private CSharpCompilation CreateCompilation() { var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTrees); var diagnostics = compilation.GetDiagnostics().Where(d => d.Severity >= DiagnosticSeverity.Warning).ToArray(); if (diagnostics.Length > 0) { throw new CompilationFailedException(compilation, diagnostics); } return(compilation); }
public void Execute_EventCallbackProperty_CreatesDescriptor_ClosedGeneric() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Components; namespace Test { public class MyComponent : ComponentBase { [Parameter] EventCallback<UIMouseEventArgs> OnClick { get; set; } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components); Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent", component.Name); Assert.Collection( component.BoundAttributes.OrderBy(a => a.Name), a => { Assert.Equal("OnClick", a.Name); Assert.Equal("Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIMouseEventArgs>", a.TypeName); Assert.False(a.HasIndexer); Assert.False(a.IsBooleanProperty); Assert.False(a.IsEnum); Assert.False(a.IsStringProperty); Assert.True(a.IsEventCallbackProperty()); Assert.False(a.IsDelegateProperty()); Assert.False(a.IsChildContentProperty()); Assert.False(a.IsGenericTypedProperty()); }); }
[Fact] // enum properties have some special intellisense behavior public void Excecute_EnumProperty_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Blazor.Components; namespace Test { public enum MyEnum { One, Two } public class MyComponent : BlazorComponent { [Parameter] MyEnum MyProperty { get; set; } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components); Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent", component.Name); var attribute = Assert.Single(component.BoundAttributes); Assert.Equal("MyProperty", attribute.Name); Assert.Equal("Test.MyEnum", attribute.TypeName); Assert.False(attribute.HasIndexer); Assert.False(attribute.IsBooleanProperty); Assert.True(attribute.IsEnum); Assert.False(attribute.IsStringProperty); }
public void Execute_DelegateProperty_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using System; using Microsoft.AspNetCore.Blazor; using Microsoft.AspNetCore.Blazor.Components; namespace Test { public class MyComponent : BlazorComponent { [Parameter] Action<UIMouseEventArgs> OnClick { get; set; } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components); Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent", component.Name); var attribute = Assert.Single(component.BoundAttributes); Assert.Equal("OnClick", attribute.Name); Assert.Equal("System.Action<Microsoft.AspNetCore.Blazor.UIMouseEventArgs>", attribute.TypeName); Assert.False(attribute.HasIndexer); Assert.False(attribute.IsBooleanProperty); Assert.False(attribute.IsEnum); Assert.False(attribute.IsStringProperty); Assert.True(attribute.IsDelegateProperty()); Assert.False(attribute.IsChildContentProperty()); }
public void Execute_NoMatchedPropertiesOnComponent_IgnoresComponent() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using System; 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; } public string MyProperty { get; set; } public Action<string> MyPropertyChangedNotMatch { 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: 0); Assert.Empty(matches); }
public void Execute_NoMatchedPropertiesOnComponent_IgnoresComponent() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using System; using Microsoft.AspNetCore.Components; namespace Test { public class MyComponent : IComponent { public void Init(RenderHandle renderHandle) { } public void SetParameters(ParameterCollection parameters) { } public string MyProperty { get; set; } public Action<string> MyPropertyChangedNotMatch { 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); Assert.Empty(matches); }
public void Excecute_FindsBlazorComponentType_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Blazor.Components; namespace Test { public class MyComponent : BlazorComponent { [Parameter] string MyProperty { get; set; } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components); Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent", component.Name); var attribute = Assert.Single(component.BoundAttributes); Assert.Equal("MyProperty", attribute.Name); Assert.Equal("System.String", attribute.TypeName); }
public void Execute_BindOnInputElementWithTypeAttributeAndSuffixAndInvariantCultureAndFormat_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Components; namespace Test { [BindInputElement(""number"", null, ""value"", ""onchange"", isInvariantCulture: true, format: ""0.00"")] 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("value", bind.Metadata[ComponentMetadata.Bind.ValueAttribute]); Assert.Equal("onchange", bind.Metadata[ComponentMetadata.Bind.ChangeAttribute]); Assert.Equal("number", bind.Metadata[ComponentMetadata.Bind.TypeAttribute]); Assert.True(bind.IsInputElementBindTagHelper()); Assert.False(bind.IsInputElementFallbackBindTagHelper()); Assert.True(bind.IsInvariantCultureBindTagHelper()); Assert.Equal("0.00", bind.GetFormat()); }
public void Excecute_FindsIComponentType_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Blazor.Components; namespace Test { public class MyComponent : IComponent { public void Init(RenderHandle renderHandle) { } public void SetParameters(ParameterCollection parameters) { } [Parameter] private string MyProperty { get; set; } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components); // These are features Components don't use. Verifying them once here and // then ignoring them. Assert.Empty(component.AllowedChildTags); Assert.Null(component.TagOutputHint); // These are features that are invariants of all Components. Verifying them once // here and then ignoring them. Assert.Empty(component.Diagnostics); Assert.False(component.HasErrors); Assert.Equal(BlazorMetadata.Component.TagHelperKind, component.Kind); Assert.False(component.IsDefaultKind()); Assert.False(component.KindUsesDefaultTagHelperRuntime()); // No documentation in this test Assert.Null(component.Documentation); // These are all trivially derived from the assembly/namespace/type name Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent", component.Name); Assert.Equal("Test.MyComponent", component.DisplayName); Assert.Equal("Test.MyComponent", component.GetTypeName()); // Our use of matching rules is also very simple, and derived from the name. Verifying // it once in detail here and then ignoring it. var rule = Assert.Single(component.TagMatchingRules); Assert.Empty(rule.Attributes); Assert.Empty(rule.Diagnostics); Assert.False(rule.HasErrors); Assert.Null(rule.ParentTag); Assert.Equal("MyComponent", rule.TagName); Assert.Equal(TagStructure.Unspecified, rule.TagStructure); // Our use of metadata is also (for now) an invariant for all Components - other than the type name // which is trivial. Verifying it once in detail and then ignoring it. Assert.Collection( component.Metadata.OrderBy(kvp => kvp.Key), kvp => { Assert.Equal(TagHelperMetadata.Common.TypeName, kvp.Key); Assert.Equal("Test.MyComponent", kvp.Value); }, kvp => { Assert.Equal(TagHelperMetadata.Runtime.Name, kvp.Key); Assert.Equal("Blazor.IComponent", kvp.Value); }); // Our use of bound attributes is what tests will focus on. As you might expect right now, this test // is going to cover a lot of trivial stuff that will be true for all components/component-properties. var attribute = Assert.Single(component.BoundAttributes); // Invariants Assert.Empty(attribute.Diagnostics); Assert.False(attribute.HasErrors); Assert.Equal("Blazor.Component", attribute.Kind); Assert.False(attribute.IsDefaultKind()); // Related to dictionaries/indexers, not supported currently, not sure if we ever will Assert.False(attribute.HasIndexer); Assert.Null(attribute.IndexerNamePrefix); Assert.Null(attribute.IndexerTypeName); Assert.False(attribute.IsIndexerBooleanProperty); Assert.False(attribute.IsIndexerStringProperty); // No documentation in this test Assert.Null(attribute.Documentation); // Names are trivially derived from the property name Assert.Equal("MyProperty", attribute.Name); Assert.Equal("MyProperty", attribute.GetPropertyName()); Assert.Equal("string Test.MyComponent.MyProperty", attribute.DisplayName); // Defined from the property type Assert.Equal("System.String", attribute.TypeName); Assert.True(attribute.IsStringProperty); Assert.False(attribute.IsBooleanProperty); Assert.False(attribute.IsEnum); // Our use of metadata is also (for now) an invariant for all Component properties - other than the type name // which is trivial. Verifying it once in detail and then ignoring it. Assert.Collection( attribute.Metadata.OrderBy(kvp => kvp.Key), kvp => { Assert.Equal(TagHelperMetadata.Common.PropertyName, kvp.Key); Assert.Equal("MyProperty", kvp.Value); }); }
public void Excecute_MultipleGenerics_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Blazor.Components; namespace Test { public class MyComponent<T, U, V> : BlazorComponent { [Parameter] T MyProperty1 { get; set; } [Parameter] U MyProperty2 { get; set; } [Parameter] V MyProperty3 { get; set; } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components); Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent<T, U, V>", component.Name); Assert.Collection( component.BoundAttributes.OrderBy(a => a.Name), a => { Assert.Equal("MyProperty1", a.Name); Assert.Equal("T", a.TypeName); Assert.True(a.IsGenericTypedProperty()); }, a => { Assert.Equal("MyProperty2", a.Name); Assert.Equal("U", a.TypeName); Assert.True(a.IsGenericTypedProperty()); }, a => { Assert.Equal("MyProperty3", a.Name); Assert.Equal("V", a.TypeName); Assert.True(a.IsGenericTypedProperty()); }, a => { Assert.Equal("T", a.Name); Assert.True(a.IsTypeParameterProperty()); }, a => { Assert.Equal("U", a.Name); Assert.True(a.IsTypeParameterProperty()); }, a => { Assert.Equal("V", a.Name); Assert.True(a.IsTypeParameterProperty()); }); }
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); 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.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")); // 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); attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("format")); // 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( "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>.", attribute.Documentation); Assert.Equal("format-myprop", attribute.Name); Assert.Equal("Format_myprop", attribute.GetPropertyName()); Assert.Equal("string Test.BindAttributes.Format_myprop", attribute.DisplayName); // Defined from the property type Assert.Equal("System.String", attribute.TypeName); Assert.True(attribute.IsStringProperty); Assert.False(attribute.IsBooleanProperty); Assert.False(attribute.IsEnum); }
protected CompileToCSharpResult CompileToCSharp(string cshtmlRelativePath, string cshtmlContent, bool throwOnFailure = true, string fileKind = null) { if (DeclarationOnly && DesignTime) { throw new InvalidOperationException($"{nameof(DeclarationOnly)} cannot be used with {nameof(DesignTime)}."); } if (DeclarationOnly && UseTwoPhaseCompilation) { throw new InvalidOperationException($"{nameof(DeclarationOnly)} cannot be used with {nameof(UseTwoPhaseCompilation)}."); } if (UseTwoPhaseCompilation) { // The first phase won't include any metadata references for component discovery. This mirrors // what the build does. var projectEngine = CreateProjectEngine(RazorConfiguration.Default, Array.Empty <MetadataReference>()); RazorCodeDocument codeDocument; foreach (var item in AdditionalRazorItems) { // Result of generating declarations codeDocument = projectEngine.ProcessDeclarationOnly(item); Assert.Empty(codeDocument.GetCSharpDocument().Diagnostics); var syntaxTree = Parse(codeDocument.GetCSharpDocument().GeneratedCode, path: item.FilePath); AdditionalSyntaxTrees.Add(syntaxTree); } // Result of generating declarations var projectItem = CreateProjectItem(cshtmlRelativePath, cshtmlContent, fileKind); codeDocument = projectEngine.ProcessDeclarationOnly(projectItem); var declaration = new CompileToCSharpResult { BaseCompilation = BaseCompilation.AddSyntaxTrees(AdditionalSyntaxTrees), CodeDocument = codeDocument, Code = codeDocument.GetCSharpDocument().GeneratedCode, Diagnostics = codeDocument.GetCSharpDocument().Diagnostics, }; // Result of doing 'temp' compilation var tempAssembly = CompileToAssembly(declaration, throwOnFailure); // Add the 'temp' compilation as a metadata reference var references = BaseCompilation.References.Concat(new[] { tempAssembly.Compilation.ToMetadataReference() }).ToArray(); projectEngine = CreateProjectEngine(RazorConfiguration.Default, references); // Now update the any additional files foreach (var item in AdditionalRazorItems) { // Result of generating definition codeDocument = DesignTime ? projectEngine.ProcessDesignTime(item) : projectEngine.Process(item); Assert.Empty(codeDocument.GetCSharpDocument().Diagnostics); // Replace the 'declaration' syntax tree var syntaxTree = Parse(codeDocument.GetCSharpDocument().GeneratedCode, path: item.FilePath); AdditionalSyntaxTrees.RemoveAll(st => st.FilePath == item.FilePath); AdditionalSyntaxTrees.Add(syntaxTree); } // Result of real code generation for the document under test codeDocument = DesignTime ? projectEngine.ProcessDesignTime(projectItem) : projectEngine.Process(projectItem); return(new CompileToCSharpResult { BaseCompilation = BaseCompilation.AddSyntaxTrees(AdditionalSyntaxTrees), CodeDocument = codeDocument, Code = codeDocument.GetCSharpDocument().GeneratedCode, Diagnostics = codeDocument.GetCSharpDocument().Diagnostics, }); } else { // For single phase compilation tests just use the base compilation's references. // This will include the built-in components. var projectEngine = CreateProjectEngine(Configuration, BaseCompilation.References.ToArray()); var projectItem = CreateProjectItem(cshtmlRelativePath, cshtmlContent, fileKind); RazorCodeDocument codeDocument; if (DeclarationOnly) { codeDocument = projectEngine.ProcessDeclarationOnly(projectItem); } else { codeDocument = DesignTime ? projectEngine.ProcessDesignTime(projectItem) : projectEngine.Process(projectItem); } return(new CompileToCSharpResult { BaseCompilation = BaseCompilation.AddSyntaxTrees(AdditionalSyntaxTrees), CodeDocument = codeDocument, Code = codeDocument.GetCSharpDocument().GeneratedCode, Diagnostics = codeDocument.GetCSharpDocument().Diagnostics, }); } }
public void Execute_FindsBindTagHelperOnComponentType_EventCallback_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Components; namespace Test { public class MyComponent : IComponent { public void Init(RenderHandle renderHandle) { } public void SetParameters(ParameterCollection parameters) { } [Parameter] string MyProperty { get; set; } [Parameter] EventCallback<string> MyPropertyChanged { 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); 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.Equal("MyProperty", bind.Metadata[ComponentMetadata.Bind.ValueAttribute]); Assert.Equal("MyPropertyChanged", bind.Metadata[ComponentMetadata.Bind.ChangeAttribute]); 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("string Test.MyComponent.MyProperty", attribute.DisplayName); // Defined from the property type Assert.Equal("System.String", attribute.TypeName); Assert.True(attribute.IsStringProperty); Assert.False(attribute.IsBooleanProperty); Assert.False(attribute.IsEnum); }
public void Execute_BindOnInputElementWithTypeAttributeAndSuffix_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Blazor.Components; namespace Test { [BindInputElement(""checkbox"", ""somevalue"", ""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-somevalue", a.DisplayName); Assert.Equal("bind-somevalue", a.Name); }); var attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("bind")); Assert.Equal("bind-somevalue", attribute.Name); Assert.Equal("Bind_somevalue", attribute.GetPropertyName()); Assert.Equal("object Test.BindAttributes.Bind_somevalue", attribute.DisplayName); attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("format")); Assert.Equal("format-somevalue", attribute.Name); Assert.Equal("Format_somevalue", attribute.GetPropertyName()); Assert.Equal("string Test.BindAttributes.Format_somevalue", attribute.DisplayName); }
public void Execute_MultipleRenderFragmentProperties_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Blazor; using Microsoft.AspNetCore.Blazor.Components; namespace Test { public class MyComponent : BlazorComponent { [Parameter] RenderFragment ChildContent { get; set; } [Parameter] RenderFragment<string> Header { get; set; } [Parameter] RenderFragment<string> Footer { get; set; } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components, c => c.IsComponentTagHelper()); Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent", component.Name); Assert.Collection( component.BoundAttributes.OrderBy(a => a.Name), a => { Assert.Equal("ChildContent", a.Name); Assert.Equal("Microsoft.AspNetCore.Blazor.RenderFragment", a.TypeName); Assert.True(a.IsChildContentProperty()); }, a => { Assert.Equal("Footer", a.Name); Assert.Equal("Microsoft.AspNetCore.Blazor.RenderFragment<System.String>", a.TypeName); Assert.True(a.IsChildContentProperty()); }, a => { Assert.Equal("Header", a.Name); Assert.Equal("Microsoft.AspNetCore.Blazor.RenderFragment<System.String>", a.TypeName); Assert.True(a.IsChildContentProperty()); }); var childContents = components.Where(c => c.IsChildContentTagHelper()).OrderBy(c => c.Name); Assert.Collection( childContents, c => Assert.Equal("Test.MyComponent.ChildContent", c.Name), c => Assert.Equal("Test.MyComponent.Footer", c.Name), c => Assert.Equal("Test.MyComponent.Header", c.Name)); }
public void Excecute_FindsIComponentType_CreatesDescriptor_Generic() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Blazor.Components; namespace Test { public class MyComponent<T> : IComponent { public void Init(RenderHandle renderHandle) { } public void SetParameters(ParameterCollection parameters) { } [Parameter] private string MyProperty { get; set; } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components); Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent<T>", component.Name); Assert.Equal("Test.MyComponent<T>", component.DisplayName); Assert.Equal("Test.MyComponent<T>", component.GetTypeName()); Assert.True(component.IsGenericTypedComponent()); var rule = Assert.Single(component.TagMatchingRules); Assert.Equal("MyComponent", rule.TagName); Assert.Collection( component.BoundAttributes.OrderBy(a => a.Name), a => { Assert.Equal("MyProperty", a.Name); Assert.Equal("MyProperty", a.GetPropertyName()); Assert.Equal("string Test.MyComponent<T>.MyProperty", a.DisplayName); Assert.Equal("System.String", a.TypeName); }, a => { Assert.Equal("T", a.Name); Assert.Equal("T", a.GetPropertyName()); Assert.Equal("T", a.DisplayName); Assert.Equal("System.Type", a.TypeName); Assert.True(a.IsTypeParameterProperty()); }); }
public void Execute_EventHandler_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using System; using Microsoft.AspNetCore.Components; namespace Test { [EventHandler(""onclick"", typeof(Action<UIMouseEventArgs>))] public class EventHandlers { } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new EventHandlerTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var matches = GetEventHandlerTagHelpers(context); var item = Assert.Single(matches); // These are features Event Handler Tag Helpers don't use. Verifying them once here and // then ignoring them. Assert.Empty(item.AllowedChildTags); Assert.Null(item.TagOutputHint); // These are features that are invariants of all Event Handler Helpers. Verifying them once // here and then ignoring them. Assert.Empty(item.Diagnostics); Assert.False(item.HasErrors); Assert.Equal(ComponentMetadata.EventHandler.TagHelperKind, item.Kind); Assert.Equal(bool.TrueString, item.Metadata[TagHelperMetadata.Common.ClassifyAttributesOnly]); Assert.Equal(ComponentMetadata.EventHandler.RuntimeName, item.Metadata[TagHelperMetadata.Runtime.Name]); Assert.False(item.IsDefaultKind()); Assert.False(item.KindUsesDefaultTagHelperRuntime()); Assert.False(item.IsComponentOrChildContentTagHelper()); Assert.Equal( "Sets the '@onclick' attribute to the provided string or delegate value. " + "A delegate value should be of type 'System.Action<Microsoft.AspNetCore.Components.UIMouseEventArgs>'.", item.Documentation); // These are all trivially derived from the assembly/namespace/type name Assert.Equal("Microsoft.AspNetCore.Components", item.AssemblyName); Assert.Equal("onclick", item.Name); Assert.Equal("Test.EventHandlers", item.DisplayName); Assert.Equal("Test.EventHandlers", item.GetTypeName()); // The tag matching rule for an event handler is just the attribute name var rule = Assert.Single(item.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("@onclick", requiredAttribute.DisplayName); Assert.Equal("@onclick", requiredAttribute.Name); Assert.Equal(RequiredAttributeDescriptor.NameComparisonMode.FullMatch, requiredAttribute.NameComparison); Assert.Null(requiredAttribute.Value); Assert.Equal(RequiredAttributeDescriptor.ValueComparisonMode.None, requiredAttribute.ValueComparison); var attribute = Assert.Single(item.BoundAttributes); // Invariants Assert.Empty(attribute.Diagnostics); Assert.False(attribute.HasErrors); Assert.Equal(ComponentMetadata.EventHandler.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.Collection( attribute.Metadata.OrderBy(kvp => kvp.Key), kvp => Assert.Equal(kvp, new KeyValuePair <string, string>(ComponentMetadata.Common.DirectiveAttribute, bool.TrueString)), kvp => Assert.Equal(kvp, new KeyValuePair <string, string>("Common.PropertyName", "onclick")), kvp => Assert.Equal(kvp, new KeyValuePair <string, string>(ComponentMetadata.Component.WeaklyTypedKey, bool.TrueString))); Assert.Equal( "Sets the '@onclick' attribute to the provided string or delegate value. " + "A delegate value should be of type 'System.Action<Microsoft.AspNetCore.Components.UIMouseEventArgs>'.", attribute.Documentation); Assert.Equal("@onclick", attribute.Name); Assert.Equal("onclick", attribute.GetPropertyName()); Assert.Equal("string Test.EventHandlers.onclick", attribute.DisplayName); // Defined from the property type Assert.Equal("System.String", attribute.TypeName); Assert.True(attribute.IsStringProperty); Assert.False(attribute.IsBooleanProperty); Assert.False(attribute.IsEnum); }
public void Execute_RenderFragmentGenericContextProperty_CreatesDescriptor() { // Arrange var compilation = BaseCompilation.AddSyntaxTrees(Parse(@" using Microsoft.AspNetCore.Blazor; using Microsoft.AspNetCore.Blazor.Components; namespace Test { public class MyComponent<T> : BlazorComponent { [Parameter] RenderFragment<Context> ChildContent2 { get; set; } public class Context { public T Item { get; set; } } } } ")); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); context.SetCompilation(compilation); var provider = new ComponentTagHelperDescriptorProvider(); // Act provider.Execute(context); // Assert var components = ExcludeBuiltInComponents(context); var component = Assert.Single(components, c => c.IsComponentTagHelper()); Assert.Equal("TestAssembly", component.AssemblyName); Assert.Equal("Test.MyComponent<T>", component.Name); Assert.Collection( component.BoundAttributes.OrderBy(a => a.Name), a => { Assert.Equal("ChildContent2", a.Name); Assert.Equal("Microsoft.AspNetCore.Blazor.RenderFragment<Test.MyComponent<T>.Context>", a.TypeName); Assert.False(a.HasIndexer); Assert.False(a.IsBooleanProperty); Assert.False(a.IsEnum); Assert.False(a.IsStringProperty); Assert.False(a.IsDelegateProperty()); // We treat RenderFragment as separate from generalized delegates Assert.True(a.IsChildContentProperty()); Assert.True(a.IsParameterizedChildContentProperty()); Assert.True(a.IsGenericTypedProperty()); }, a => { Assert.Equal("T", a.Name); Assert.Equal("T", a.GetPropertyName()); Assert.Equal("T", a.DisplayName); Assert.Equal("System.Type", a.TypeName); Assert.True(a.IsTypeParameterProperty()); }); var childContent = Assert.Single(components, c => c.IsChildContentTagHelper()); Assert.Equal("TestAssembly", childContent.AssemblyName); Assert.Equal("Test.MyComponent<T>.ChildContent2", childContent.Name); // A RenderFragment<T> tag helper has a parameter to allow you to set the lambda parameter name. var contextAttribute = Assert.Single(childContent.BoundAttributes); Assert.Equal("Context", contextAttribute.Name); Assert.Equal("System.String", contextAttribute.TypeName); Assert.Equal("Specifies the parameter name for the 'ChildContent2' lambda expression.", contextAttribute.Documentation); }