예제 #1
0
        [Fact] // bool properties support minimized attributes
        public void Execute_BoolProperty_CreatesDescriptor()
        {
            // Arrange

            var compilation = BaseCompilation.AddSyntaxTrees(Parse(@"
using Microsoft.AspNetCore.Components;

namespace Test
{
    public class MyComponent : ComponentBase
    {
        [Parameter]
        bool 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.Boolean", attribute.TypeName);

            Assert.False(attribute.HasIndexer);
            Assert.True(attribute.IsBooleanProperty);
            Assert.False(attribute.IsEnum);
            Assert.False(attribute.IsStringProperty);
        }
예제 #2
0
        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);

            matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 0);
            Assert.Empty(matches);
        }
예제 #3
0
        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);
        }
예제 #4
0
        public RazorCodeDocument Update(string filePath, string content)
        {
            //RazorCodeDocument razorCodeDocument;
            //if (RazorCodeDocumentCache.TryGetValue(content,out razorCodeDocument))
            //{
            //    return RazorCodeDocumentCache.GetValueOrDefault(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);
                //RazorCodeDocumentCache.Add(content, generated);
                return(generated);
            }

            throw new InvalidOperationException($"Cannot find item '{filePath}'.");
        }
        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 Execute_BindOnInputElementWithTypeAttributeAndSuffix_CreatesDescriptor()
        {
            // Arrange
            var compilation = BaseCompilation.AddSyntaxTrees(Parse(@"
using Microsoft.AspNetCore.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[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());

            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_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);
        }
        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);
        }
        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,
                });
            }
        }
예제 #10
0
        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);
        }
예제 #11
0
        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);
        }
예제 #12
0
        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());
            });
        }
예제 #13
0
        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); });
        }
예제 #14
0
        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());
            });
        }
예제 #15
0
        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));
        }