File: IntegrationTests\TestTagHelperDescriptors.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.AspNetCore.Razor.Language\test\Microsoft.AspNetCore.Razor.Language.UnitTests.csproj (Microsoft.AspNetCore.Razor.Language.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Reflection;
using Xunit;
 
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests;
 
public static class TestTagHelperDescriptors
{
    public static TagHelperCollection SimpleTagHelperDescriptors
    {
        get
        {
            return
                [
                CreateTagHelperDescriptor(
                    tagName: "span",
                    typeName: "SpanTagHelper",
                    assemblyName: "TestAssembly"),
                CreateTagHelperDescriptor(
                    tagName: "div",
                    typeName: "DivTagHelper",
                    assemblyName: "TestAssembly"),
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "InputTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => builder
                            .Name("value")
                            .PropertyName("FooProp")
                            .TypeName("System.String"),
                        builder => builder
                            .Name("bound")
                            .PropertyName("BoundProp")
                            .TypeName("System.String"),
                        builder => builder
                            .Name("age")
                            .PropertyName("AgeProp")
                            .TypeName("System.Int32"),
                        builder => builder
                            .Name("alive")
                            .PropertyName("AliveProp")
                            .TypeName("System.Boolean"),
                        builder => builder
                            .Name("tag")
                            .PropertyName("TagProp")
                            .TypeName("System.Object"),
                        builder => builder
                            .Name("tuple-dictionary")
                            .PropertyName("DictionaryOfBoolAndStringTupleProperty")
                            .TypeName(typeof(IDictionary<string, int>).Namespace + ".IDictionary<System.String, (System.Boolean, System.String)>")
                            .AsDictionaryAttribute("tuple-prefix-", typeof((bool, string)).FullName)
                    ])
            ];
        }
    }
 
    public static TagHelperCollection MinimizedBooleanTagHelperDescriptors
    {
        get
        {
            return
            [
                CreateTagHelperDescriptor(
                    tagName: "span",
                    typeName: "SpanTagHelper",
                    assemblyName: "TestAssembly"),
                CreateTagHelperDescriptor(
                    tagName: "div",
                    typeName: "DivTagHelper",
                    assemblyName: "TestAssembly"),
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "InputTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => builder
                            .Name("value")
                            .PropertyName("FooProp")
                            .TypeName("System.String"),
                        builder => builder
                            .Name("bound")
                            .PropertyName("BoundProp")
                            .TypeName("System.Boolean"),
                        builder => builder
                            .Name("age")
                            .PropertyName("AgeProp")
                            .TypeName("System.Int32"),
                    ])
            ];
        }
    }
 
    public static TagHelperCollection CssSelectorTagHelperDescriptors
    {
        get
        {
            var inputTypePropertyInfo = GetTestTypeRuntimeProperty("Type");
            var inputCheckedPropertyInfo = GetTestTypeRuntimeProperty("Checked");
 
            return
            [
                CreateTagHelperDescriptor(
                    tagName: "a",
                    typeName: "TestNamespace.ATagHelper",
                    assemblyName: "TestAssembly",
                    ruleBuilders:
                    [
                        builder => builder
                            .RequireAttributeDescriptor(attribute => attribute
                                .Name("href", RequiredAttributeNameComparison.FullMatch)
                                .Value("~/", RequiredAttributeValueComparison.FullMatch)),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "a",
                    typeName: "TestNamespace.ATagHelperMultipleSelectors",
                    assemblyName: "TestAssembly",
                    ruleBuilders:
                    [
                        builder => builder
                            .RequireAttributeDescriptor(attribute => attribute
                                .Name("href", RequiredAttributeNameComparison.FullMatch)
                                .Value("~/", RequiredAttributeValueComparison.PrefixMatch))
                            .RequireAttributeDescriptor(attribute => attribute
                                .Name("href", RequiredAttributeNameComparison.FullMatch)
                                .Value("?hello=world", RequiredAttributeValueComparison.SuffixMatch)),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "TestNamespace.InputTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
                    ],
                    ruleBuilders:
                    [
                        builder => builder
                            .RequireAttributeDescriptor(attribute => attribute
                                .Name("type", RequiredAttributeNameComparison.FullMatch)
                                .Value("text", RequiredAttributeValueComparison.FullMatch)),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "TestNamespace.InputTagHelper2",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
                    ],
                    ruleBuilders:
                    [
                        builder => builder
                            .RequireAttributeDescriptor(attribute => attribute
                                .Name("ty", RequiredAttributeNameComparison.PrefixMatch)),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "*",
                    typeName: "TestNamespace.CatchAllTagHelper",
                    assemblyName: "TestAssembly",
                    ruleBuilders:
                    [
                        builder => builder
                            .RequireAttributeDescriptor(attribute => attribute
                                .Name("href", RequiredAttributeNameComparison.FullMatch)
                                .Value("~/", RequiredAttributeValueComparison.PrefixMatch)),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "*",
                    typeName: "TestNamespace.CatchAllTagHelper2",
                    assemblyName: "TestAssembly",
                    ruleBuilders:
                    [
                        builder => builder
                            .RequireAttributeDescriptor(attribute => attribute
                                .Name("type", RequiredAttributeNameComparison.FullMatch)),
                    ]),
            ];
        }
    }
 
    public static TagHelperCollection EnumTagHelperDescriptors
    {
        get
        {
            return
            [
                CreateTagHelperDescriptor(
                    tagName: "*",
                    typeName: "TestNamespace.CatchAllTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => builder
                            .Name("catch-all")
                            .PropertyName("CatchAll")
                            .AsEnum()
                            .TypeName("Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestTagHelperDescriptors.MyEnum"),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "TestNamespace.InputTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => builder
                            .Name("value")
                            .PropertyName("Value")
                            .AsEnum()
                            .TypeName("Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestTagHelperDescriptors.MyEnum"),
                    ]),
            ];
        }
    }
 
    public static TagHelperCollection SymbolBoundTagHelperDescriptors
    {
        get
        {
            return
            [
                CreateTagHelperDescriptor(
                    tagName: "*",
                    typeName: "TestNamespace.CatchAllTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => builder
                            .Name("[item]")
                            .PropertyName("ListItems")
                            .TypeName("System.Collections.Generic.List<string>"),
                        builder => builder
                            .Name("[(item)]")
                            .PropertyName("ArrayItems")
                            .TypeName(typeof(string[]).FullName),
                        builder => builder
                            .Name("(click)")
                            .PropertyName("Event1")
                            .TypeName(typeof(Action).FullName),
                        builder => builder
                            .Name("(^click)")
                            .PropertyName("Event2")
                            .TypeName(typeof(Action).FullName),
                        builder => builder
                            .Name("*something")
                            .PropertyName("StringProperty1")
                            .TypeName(typeof(string).FullName),
                        builder => builder
                            .Name("#local")
                            .PropertyName("StringProperty2")
                            .TypeName(typeof(string).FullName),
                    ],
                    ruleBuilders:
                    [
                        builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("bound")),
                    ]),
            ];
        }
    }
 
    public static TagHelperCollection MinimizedTagHelpers_Descriptors
    {
        get
        {
            return
            [
                CreateTagHelperDescriptor(
                    tagName: "*",
                    typeName: "TestNamespace.CatchAllTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => builder
                            .Name("catchall-bound-string")
                            .PropertyName("BoundRequiredString")
                            .TypeName(typeof(string).FullName),
                    ],
                    ruleBuilders:
                    [
                        builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("catchall-unbound-required")),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "TestNamespace.InputTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => builder
                            .Name("input-bound-required-string")
                            .PropertyName("BoundRequiredString")
                            .TypeName(typeof(string).FullName),
                        builder => builder
                            .Name("input-bound-string")
                            .PropertyName("BoundString")
                            .TypeName(typeof(string).FullName),
                    ],
                    ruleBuilders:
                    [
                        builder => builder
                            .RequireAttributeDescriptor(attribute => attribute.Name("input-bound-required-string"))
                            .RequireAttributeDescriptor(attribute => attribute.Name("input-unbound-required")),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "div",
                    typeName: "DivTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => builder
                            .Name("boundbool")
                            .PropertyName("BoundBoolProp")
                            .TypeName(typeof(bool).FullName),
                        builder => builder
                            .Name("booldict")
                            .PropertyName("BoolDictProp")
                            .TypeName("System.Collections.Generic.IDictionary<string, bool>")
                            .AsDictionaryAttribute("booldict-prefix-", typeof(bool).FullName),
                    ]),
            ];
        }
    }
 
    public static TagHelperCollection DynamicAttributeTagHelpers_Descriptors
    {
        get
        {
            return
            [
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "TestNamespace.InputTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => builder
                            .Name("bound")
                            .PropertyName("Bound")
                            .TypeName(typeof(string).FullName)
                    ]),
                ];
        }
    }
 
    public static TagHelperCollection DuplicateTargetTagHelperDescriptors
    {
        get
        {
            var typePropertyInfo = GetTestTypeRuntimeProperty("Type");
            var checkedPropertyInfo = GetTestTypeRuntimeProperty("Checked");
 
            return
            [
                CreateTagHelperDescriptor(
                    tagName: "*",
                    typeName: "TestNamespace.CatchAllTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", typePropertyInfo),
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", checkedPropertyInfo),
                    ],
                    ruleBuilders:
                    [
                        builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("type")),
                        builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("checked"))
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "TestNamespace.InputTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", typePropertyInfo),
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", checkedPropertyInfo),
                    ],
                    ruleBuilders:
                    [
                        builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("type")),
                        builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("checked"))
                    ])
            ];
        }
    }
 
    public static TagHelperCollection AttributeTargetingTagHelperDescriptors
    {
        get
        {
            var inputTypePropertyInfo = GetTestTypeRuntimeProperty("Type");
            var inputCheckedPropertyInfo = GetTestTypeRuntimeProperty("Checked");
 
            return
            [
                CreateTagHelperDescriptor(
                    tagName: "p",
                    typeName: "TestNamespace.PTagHelper",
                    assemblyName: "TestAssembly",
                    ruleBuilders:
                    [
                        builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("class")),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "TestNamespace.InputTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
                    ],
                    ruleBuilders:
                    [
                        builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("type")),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "TestNamespace.InputTagHelper2",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", inputCheckedPropertyInfo),
                    ],
                    ruleBuilders:
                    [
                        builder => builder
                            .RequireAttributeDescriptor(attribute => attribute.Name("type"))
                            .RequireAttributeDescriptor(attribute => attribute.Name("checked")),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "*",
                    typeName: "TestNamespace.CatchAllTagHelper",
                    assemblyName: "TestAssembly",
                    ruleBuilders:
                    [
                        builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("catchAll")),
                    ]),
            ];
        }
    }
 
    public static TagHelperCollection PrefixedAttributeTagHelperDescriptors
    {
        get
        {
            return
            [
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "TestNamespace.InputTagHelper1",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => builder
                            .Name("int-prefix-grabber")
                            .PropertyName("IntProperty")
                            .TypeName(typeof(int).FullName),
                        builder => builder
                            .Name("int-dictionary")
                            .PropertyName("IntDictionaryProperty")
                            .TypeName("System.Collections.Generic.IDictionary<string, int>")
                            .AsDictionaryAttribute("int-prefix-", typeof(int).FullName),
                        builder => builder
                            .Name("string-prefix-grabber")
                            .PropertyName("StringProperty")
                            .TypeName(typeof(string).FullName),
                        builder => builder
                            .Name("string-dictionary")
                            .PropertyName("StringDictionaryProperty")
                            .TypeName("Namespace.DictionaryWithoutParameterlessConstructor<string, string>")
                            .AsDictionaryAttribute("string-prefix-", typeof(string).FullName),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "TestNamespace.InputTagHelper2",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => builder
                            .Name("int-dictionary")
                            .PropertyName("IntDictionaryProperty")
                            .TypeName(typeof(int).FullName)
                            .AsDictionaryAttribute("int-prefix-", typeof(int).FullName),
                        builder => builder
                            .Name("string-dictionary")
                            .PropertyName("StringDictionaryProperty")
                            .TypeName("Namespace.DictionaryWithoutParameterlessConstructor<string, string>")
                            .AsDictionaryAttribute("string-prefix-", typeof(string).FullName),
                    ]),
            ];
        }
    }
 
    public static TagHelperCollection TagHelpersInSectionDescriptors
    {
        get
        {
            var propertyInfo = GetTestTypeRuntimeProperty("BoundProperty");
 
            return
            [
                CreateTagHelperDescriptor(
                    tagName: "MyTagHelper",
                    typeName: "TestNamespace.MyTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "BoundProperty", propertyInfo),
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "NestedTagHelper",
                    typeName: "TestNamespace.NestedTagHelper",
                    assemblyName: "TestAssembly"),
            ];
        }
    }
 
    public static TagHelperCollection DefaultPAndInputTagHelperDescriptors
    {
        get
        {
            var pAgePropertyInfo = GetTestTypeRuntimeProperty("Age");
            var inputTypePropertyInfo = GetTestTypeRuntimeProperty("Type");
            var checkedPropertyInfo = GetTestTypeRuntimeProperty("Checked");
 
            return
            [
                CreateTagHelperDescriptor(
                    tagName: "p",
                    typeName: "TestNamespace.PTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "age", pAgePropertyInfo),
                    ],
                    ruleBuilders:
                    [
                        builder => builder.RequireTagStructure(TagStructure.NormalOrSelfClosing)
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "TestNamespace.InputTagHelper",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
                    ],
                    ruleBuilders:
                    [
                        builder => builder.RequireTagStructure(TagStructure.WithoutEndTag)
                    ]),
                CreateTagHelperDescriptor(
                    tagName: "input",
                    typeName: "TestNamespace.InputTagHelper2",
                    assemblyName: "TestAssembly",
                    attributes:
                    [
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
                        builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", checkedPropertyInfo),
                    ]),
            ];
        }
    }
 
    private static TagHelperDescriptor CreateTagHelperDescriptor(
        string tagName,
        string typeName,
        string assemblyName,
        IEnumerable<Action<BoundAttributeDescriptorBuilder>>? attributes = null,
        IEnumerable<Action<TagMatchingRuleDescriptorBuilder>>? ruleBuilders = null)
    {
        var builder = TagHelperDescriptorBuilder.CreateTagHelper(typeName, assemblyName);
        builder.SetTypeName(typeName, typeNamespace: null, typeNameIdentifier: null);
 
        if (attributes != null)
        {
            foreach (var attributeBuilder in attributes)
            {
                builder.BoundAttributeDescriptor(attributeBuilder);
            }
        }
 
        if (ruleBuilders != null)
        {
            foreach (var ruleBuilder in ruleBuilders)
            {
                builder.TagMatchingRuleDescriptor(innerRuleBuilder =>
                {
                    innerRuleBuilder.RequireTagName(tagName);
                    ruleBuilder(innerRuleBuilder);
                });
            }
        }
        else
        {
            builder.TagMatchingRuleDescriptor(ruleBuilder => ruleBuilder.RequireTagName(tagName));
        }
 
        var descriptor = builder.Build();
 
        return descriptor;
    }
 
    private static PropertyInfo GetTestTypeRuntimeProperty(string name)
    {
        var result = typeof(TestType).GetRuntimeProperty(name);
        Assert.NotNull(result);
 
        return result;
    }
 
    private static void BuildBoundAttributeDescriptorFromPropertyInfo(
        BoundAttributeDescriptorBuilder builder,
        string name,
        PropertyInfo propertyInfo)
    {
        builder
            .Name(name)
            .PropertyName(propertyInfo.Name)
            .TypeName(propertyInfo.PropertyType.FullName);
 
        if (propertyInfo.PropertyType.GetTypeInfo().IsEnum)
        {
            builder.AsEnum();
        }
    }
 
    private class TestType
    {
        public int Age { get; set; }
 
        public string? Type { get; set; }
 
        public bool Checked { get; set; }
 
        public string? BoundProperty { get; set; }
    }
 
    public static readonly string Code = """
        namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
        {
            public class TestTagHelperDescriptors
            {
                public enum MyEnum
                {
                    MyValue,
                    MySecondValue
                }
            }
        }
        """;
}