File: Legacy\TagHelperParseTreeRewriterTest.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.AspNetCore.Razor.Language\legacyTest\Microsoft.AspNetCore.Razor.Language.Legacy.UnitTests.csproj (Microsoft.AspNetCore.Razor.Language.Legacy.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.Linq;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Xunit;
 
namespace Microsoft.AspNetCore.Razor.Language.Legacy;
 
public class TagHelperParseTreeRewriterTest : TagHelperRewritingTestBase
{
    public static TheoryData<string, IEnumerable<KeyValuePair<string, string>>> GetAttributeNameValuePairsData
    {
        get
        {
            Func<string, string, KeyValuePair<string, string>> kvp =
                (key, value) => new KeyValuePair<string, string>(key, value);
            var empty = Enumerable.Empty<KeyValuePair<string, string>>();
            var csharp = TagHelperParseTreeRewriter.Rewriter.InvalidAttributeValueMarker.ToString();
 
            // documentContent, expectedPairs
            return new TheoryData<string, IEnumerable<KeyValuePair<string, string>>>
            {
                { "<a>", empty },
                { "<a @{ } href='~/home'>", new[] { kvp("href", "~/home") } },
                { "<a href=\"@true\">", new[] { kvp("href", csharp) } },
                { "<a href=\"prefix @true suffix\">", new[] { kvp("href", $"prefix{csharp} suffix") } },
                { "<a href=~/home>", new[] { kvp("href", "~/home") } },
                { "<a href=~/home @{ } nothing='something'>", new[] { kvp("href", "~/home"), kvp("nothing", "something") } },
                {
                    "<a href=\"@DateTime.Now::0\" class='btn btn-success' random>",
                    new[] { kvp("href", $"{csharp}::0"), kvp("class", "btn btn-success"), kvp("random", "") }
                },
                { "<a href=>", new[] { kvp("href", "") } },
                { "<a href='\">  ", new[] { kvp("href", "\">  ") } },
                { "<a href'", new[] { kvp("href'", "") } },
            };
        }
    }
 
    [Theory]
    [MemberData(nameof(GetAttributeNameValuePairsData))]
    public void GetAttributeNameValuePairs_ParsesPairsCorrectly(
        string documentContent,
        IEnumerable<KeyValuePair<string, string>> expectedPairs)
    {
        // Arrange
        using var errorSink = new ErrorSink();
        var parseResult = ParseDocument(documentContent);
        var document = parseResult.Root;
 
        // Assert - Guard
        var rootBlock = Assert.IsType<RazorDocumentSyntax>(document);
        var rootMarkup = Assert.IsType<MarkupBlockSyntax>(rootBlock.Document);
        var childBlock = Assert.Single(rootMarkup.Children);
        var element = Assert.IsType<MarkupElementSyntax>(childBlock);
        Assert.Empty(errorSink.GetErrorsAndClear());
 
        // Act
        var pairs = TagHelperParseTreeRewriter.Rewriter.GetAttributeNameValuePairs(element.MarkupStartTag);
 
        // Assert
        Assert.Equal(expectedPairs, pairs);
    }
 
    public static readonly TagHelperCollection PartialRequiredParentTags_TagHelpers =
    [
        TagHelperDescriptorBuilder.CreateTagHelper("StrongTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"))
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
            .Build(),
        TagHelperDescriptorBuilder.CreateTagHelper("CatchALlTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
            .Build(),
        TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
            .Build(),
    ];
 
    [Fact]
    public void UnderstandsPartialRequiredParentTags1()
    {
        var document = "<p><strong>";
        EvaluateData(PartialRequiredParentTags_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsPartialRequiredParentTags2()
    {
        var document = "<p><strong></strong>";
        EvaluateData(PartialRequiredParentTags_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsPartialRequiredParentTags3()
    {
        var document = "<p><strong></p><strong>";
        EvaluateData(PartialRequiredParentTags_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsPartialRequiredParentTags4()
    {
        var document = "<<p><<strong></</strong</strong></p>";
        EvaluateData(PartialRequiredParentTags_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsPartialRequiredParentTags5()
    {
        var document = "<<p><<strong></</strong></strong></p>";
        EvaluateData(PartialRequiredParentTags_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsPartialRequiredParentTags6()
    {
        var document = "<<p><<custom></<</custom></custom></p>";
        EvaluateData(PartialRequiredParentTags_TagHelpers, document);
    }
 
    public static readonly TagHelperCollection NestedVoidSelfClosingRequiredParent_TagHelpers =
    [
        TagHelperDescriptorBuilder.CreateTagHelper("InputTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule
                .RequireTagName("input")
                .RequireTagStructure(TagStructure.WithoutEndTag))
            .Build(),
        TagHelperDescriptorBuilder.CreateTagHelper("StrongTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule
                .RequireTagName("strong")
                .RequireParentTag("p"))
            .TagMatchingRuleDescriptor(rule => rule
                .RequireTagName("strong")
                .RequireParentTag("input"))
            .Build(),
        TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
            .Build(),
    ];
 
    public static readonly TagHelperCollection CatchAllAttribute_TagHelpers =
    [
        TagHelperDescriptorBuilder.CreateEventHandler("InputTagHelper1", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule
                .RequireTagName("*")
                .RequireAttributeDescriptor(b =>
                {
                    b.Name = "onclick";
                }))
            .Build(),
    ];
 
    [Fact]
    public void UnderstandsInvalidHtml()
    {
        var document = @"<a onclick=""() => {}""><a/></a><strong>Miscolored!</strong>";
        EvaluateData(CatchAllAttribute_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNestedVoidSelfClosingRequiredParent1()
    {
        var document = "<input><strong></strong>";
        EvaluateData(NestedVoidSelfClosingRequiredParent_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNestedVoidSelfClosingRequiredParent2()
    {
        var document = "<p><input><strong></strong></p>";
        EvaluateData(NestedVoidSelfClosingRequiredParent_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNestedVoidSelfClosingRequiredParent3()
    {
        var document = "<p><br><strong></strong></p>";
        EvaluateData(NestedVoidSelfClosingRequiredParent_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNestedVoidSelfClosingRequiredParent4()
    {
        var document = "<p><p><br></p><strong></strong></p>";
        EvaluateData(NestedVoidSelfClosingRequiredParent_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNestedVoidSelfClosingRequiredParent5()
    {
        var document = "<input><strong></strong>";
        EvaluateData(NestedVoidSelfClosingRequiredParent_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNestedVoidSelfClosingRequiredParent6()
    {
        var document = "<p><input /><strong /></p>";
        EvaluateData(NestedVoidSelfClosingRequiredParent_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNestedVoidSelfClosingRequiredParent7()
    {
        var document = "<p><br /><strong /></p>";
        EvaluateData(NestedVoidSelfClosingRequiredParent_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNestedVoidSelfClosingRequiredParent8()
    {
        var document = "<p><p><br /></p><strong /></p>";
        EvaluateData(NestedVoidSelfClosingRequiredParent_TagHelpers, document);
    }
 
    public static readonly TagHelperCollection NestedRequiredParent_TagHelpers =
    [
        TagHelperDescriptorBuilder.CreateTagHelper("StrongTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule =>
                rule
                .RequireTagName("strong")
                .RequireParentTag("p"))
            .TagMatchingRuleDescriptor(rule =>
                rule
                .RequireTagName("strong")
                .RequireParentTag("div"))
            .Build(),
        TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
            .Build(),
    ];
 
    [Fact]
    public void UnderstandsNestedRequiredParent1()
    {
        var document = "<strong></strong>";
        EvaluateData(NestedRequiredParent_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNestedRequiredParent2()
    {
        var document = "<p><strong></strong></p>";
        EvaluateData(NestedRequiredParent_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNestedRequiredParent3()
    {
        var document = "<div><strong></strong></div>";
        EvaluateData(NestedRequiredParent_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNestedRequiredParent4()
    {
        var document = "<strong><strong></strong></strong>";
        EvaluateData(NestedRequiredParent_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNestedRequiredParent5()
    {
        var document = "<p><strong><strong></strong></strong></p>";
        EvaluateData(NestedRequiredParent_TagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsTagHelperPrefixAndAllowedChildren()
    {
        // Arrange
        var documentContent = "<th:p><th:strong></th:strong></th:p>";
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
                .AllowChildTag("strong")
                .Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("StrongTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"))
                .Build(),
        ];
 
        // Act & Assert
        EvaluateData(
            tagHelpers,
            documentContent,
            tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void UnderstandsTagHelperPrefixAndAllowedChildrenAndRequireParent()
    {
        // Arrange
        var documentContent = "<th:p><th:strong></th:strong></th:p>";
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
                .AllowChildTag("strong")
                .Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("StrongTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong").RequireParentTag("p"))
                .Build(),
        ];
 
        // Act & Assert
        EvaluateData(
            tagHelpers,
            documentContent,
            tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void InvalidStructure_UnderstandsTHPrefixAndAllowedChildrenAndRequireParent()
    {
        // Rewrite_InvalidStructure_UnderstandsTagHelperPrefixAndAllowedChildrenAndRequireParent
        // Arrange
        var documentContent = "<th:p></th:strong></th:p>";
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
                .AllowChildTag("strong")
                .Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("StrongTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong").RequireParentTag("p"))
                .Build(),
        ];
 
        // Act & Assert
        EvaluateData(
            tagHelpers,
            documentContent,
            tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void NonTagHelperChild_UnderstandsTagHelperPrefixAndAllowedChildren()
    {
        // Arrange
        var documentContent = "<th:p><strong></strong></th:p>";
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
                .AllowChildTag("strong")
                .Build(),
        ];
 
        // Act & Assert
        EvaluateData(
            tagHelpers,
            documentContent,
            tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void DoesNotUnderstandTagHelpersInInvalidHtmlTypedScriptTags1()
    {
        var document = "<script type><input /></script>";
        RunParseTreeRewriterTest(document, "input");
    }
 
    [Fact]
    public void DoesNotUnderstandTagHelpersInInvalidHtmlTypedScriptTags2()
    {
        var document = "<script types='text/html'><input /></script>";
        RunParseTreeRewriterTest(document, "input");
    }
 
    [Fact]
    public void DoesNotUnderstandTagHelpersInInvalidHtmlTypedScriptTags3()
    {
        var document = "<script type='text/html invalid'><input /></script>";
        RunParseTreeRewriterTest(document, "input");
    }
 
    [Fact]
    public void DoesNotUnderstandTagHelpersInInvalidHtmlTypedScriptTags4()
    {
        var document = "<script type='text/ng-*' type='text/html'><input /></script>";
        RunParseTreeRewriterTest(document, "input");
    }
 
    [Fact]
    public void UnderstandsTagHelpersInHtmlTypedScriptTags1()
    {
        var document = "<script type='text/html'><input /></script>";
        RunParseTreeRewriterTest(document, "p", "input");
    }
 
    [Fact]
    public void UnderstandsTagHelpersInHtmlTypedScriptTags2()
    {
        var document = "<script id='scriptTag' type='text/html' class='something'><input /></script>";
        RunParseTreeRewriterTest(document, "p", "input");
    }
 
    [Fact]
    public void UnderstandsTagHelpersInHtmlTypedScriptTags3()
    {
        var document = "<script type='text/html'><p><script type='text/html'><input /></script></p></script>";
        RunParseTreeRewriterTest(document, "p", "input");
    }
 
    [Fact]
    public void UnderstandsTagHelpersInHtmlTypedScriptTags4()
    {
        var document = "<script type='text/html'><p><script type='text/ html'><input /></script></p></script>";
        RunParseTreeRewriterTest(document, "p", "input");
    }
 
    [Fact]
    public void CanHandleInvalidChildrenWithWhitespace()
    {
        // Arrange
        var documentContent = $"""
            <p>
                <strong>
                    Hello
                </strong>
            </p>
            """;
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
                .AllowChildTag("br")
                .Build()
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void RecoversWhenRequiredAttributeMismatchAndRestrictedChildren()
    {
        // Arrange
        var documentContent = "<strong required><strong></strong></strong>";
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("StrongTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule =>
                    rule
                    .RequireTagName("strong")
                    .RequireAttributeDescriptor(attribute => attribute.Name("required")))
                .AllowChildTag("br")
                .Build()
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void CanHandleMultipleTagHelpersWithAllowedChildren_OneNull()
    {
        // Arrange
        var documentContent = "<p><strong>Hello World</strong><br></p>";
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper1", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
                .AllowChildTag("strong")
                .AllowChildTag("br")
                .Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper2", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
                .Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("StrongTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"))
                .Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("BRTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule =>
                    rule
                    .RequireTagName("br")
                    .RequireTagStructure(TagStructure.WithoutEndTag))
                .Build(),
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void CanHandleMultipleTagHelpersWithAllowedChildren()
    {
        // Arrange
        var documentContent = "<p><strong>Hello World</strong><br></p>";
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper1", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
                .AllowChildTag("strong")
                .Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper2", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
                .AllowChildTag("br")
                .Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("StrongTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"))
                .Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("BRTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule =>
                    rule
                    .RequireTagName("br")
                    .RequireTagStructure(TagStructure.WithoutEndTag))
                .Build(),
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren1()
    {
        // Arrange
        var documentContent = "<p><br /></p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["br"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren2()
    {
        // Arrange
        var documentContent = $"""
            <p>
            <br />
            </p>
            """;
        var tagHelpers = GetAllowedChildrenTagHelpers(["br"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren3()
    {
        // Arrange
        var documentContent = "<p><br></p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["strong"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren4()
    {
        // Arrange
        var documentContent = "<p>Hello</p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["strong"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren5()
    {
        // Arrange
        var documentContent = "<p><hr /></p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["br", "strong"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren6()
    {
        // Arrange
        var documentContent = "<p><br>Hello</p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["strong"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren7()
    {
        // Arrange
        var documentContent = "<p><strong>Title:</strong><br />Something</p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["strong"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren8()
    {
        // Arrange
        var documentContent = "<p><strong>Title:</strong><br />Something</p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["strong", "br"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren9()
    {
        // Arrange
        var documentContent = "<p>  <strong>Title:</strong>  <br />  Something</p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["strong", "br"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren10()
    {
        // Arrange
        var documentContent = "<p><strong>Title:<br><em>A Very Cool</em></strong><br />Something</p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["strong"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren11()
    {
        // Arrange
        var documentContent = "<p><custom>Title:<br><em>A Very Cool</em></custom><br />Something</p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["custom"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren12()
    {
        // Arrange
        var documentContent = "<p></</p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["custom"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren13()
    {
        // Arrange
        var documentContent = "<p><</p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["custom"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsAllowedChildren14()
    {
        // Arrange
        var documentContent = "<p><custom><br>:<strong><strong>Hello</strong></strong>:<input></custom></p>";
        var tagHelpers = GetAllowedChildrenTagHelpers(["custom", "strong"]);
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    private static TagHelperCollection GetAllowedChildrenTagHelpers(string[] allowedChildren)
    {
        var pTagHelperBuilder = TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"));
        var strongTagHelperBuilder = TagHelperDescriptorBuilder.CreateTagHelper("StrongTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"));
 
        foreach (var childTag in allowedChildren)
        {
            pTagHelperBuilder.AllowChildTag(childTag);
            strongTagHelperBuilder.AllowChildTag(childTag);
        }
 
        return
        [
            pTagHelperBuilder.Build(),
            strongTagHelperBuilder.Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("BRTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule =>
                    rule
                    .RequireTagName("br")
                    .RequireTagStructure(TagStructure.WithoutEndTag))
                .Build(),
        ];
    }
 
    [Fact]
    public void AllowsSimpleHtmlCommentsAsChildren()
    {
        // Arrange
        var allowedChildren = new List<string> { "b" };
        var literal = "asdf";
        var commentOutput = "Hello World";
        var document = $"<p><b>{literal}</b><!--{commentOutput}--></p>";
 
        var pTagHelperBuilder = TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"));
        foreach (var childTag in allowedChildren)
        {
            pTagHelperBuilder.AllowChildTag(childTag);
        }
 
        TagHelperCollection tagHelpers =
        [
            pTagHelperBuilder.Build()
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, document);
    }
 
    [Fact]
    public void DoesntAllowSimpleHtmlCommentsAsChildrenWhenFeatureFlagIsOff()
    {
        // Arrange
        var allowedChildren = new List<string> { "b" };
        var comment1 = "Hello";
        var document = $"<p><!--{comment1}--></p>";
 
        var pTagHelperBuilder = TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"));
        foreach (var childTag in allowedChildren)
        {
            pTagHelperBuilder.AllowChildTag(childTag);
        }
 
        TagHelperCollection tagHelpers =
        [
            pTagHelperBuilder.Build()
        ];
 
        // Act & Assert
        EvaluateData(
            tagHelpers,
            document,
            languageVersion: RazorLanguageVersion.Version_2_0,
            fileKind: RazorFileKind.Legacy);
    }
 
    [Fact]
    public void FailsForContentWithCommentsAsChildren()
    {
        // Arrange
        var allowedChildren = new List<string> { "b" };
        var comment1 = "Hello";
        var literal = "asdf";
        var comment2 = "World";
        var document = $"<p><!--{comment1}-->{literal}<!--{comment2}--></p>";
 
        var pTagHelperBuilder = TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"));
        foreach (var childTag in allowedChildren)
        {
            pTagHelperBuilder.AllowChildTag(childTag);
        }
 
        TagHelperCollection tagHelpers =
        [
            pTagHelperBuilder.Build()
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, document);
    }
 
    [Fact]
    public void AllowsRazorCommentsAsChildren()
    {
        // Arrange
        var allowedChildren = new List<string> { "b" };
        var literal = "asdf";
        var commentOutput = $"@*{literal}*@";
        var document = $"<p><b>{literal}</b>{commentOutput}</p>";
 
        var pTagHelperBuilder = TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"));
        foreach (var childTag in allowedChildren)
        {
            pTagHelperBuilder.AllowChildTag(childTag);
        }
 
        TagHelperCollection tagHelpers =
        [
            pTagHelperBuilder.Build()
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, document);
    }
 
    [Fact]
    public void AllowsRazorMarkupInHtmlComment()
    {
        // Arrange
        var allowedChildren = new List<string> { "b" };
        var literal = "asdf";
        var part1 = "Hello ";
        var part2 = "World";
        var commentStart = "<!--";
        var commentEnd = "-->";
        var document = $"<p><b>{literal}</b>{commentStart}{part1}@{part2}{commentEnd}</p>";
 
        var pTagHelperBuilder = TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"));
        foreach (var childTag in allowedChildren)
        {
            pTagHelperBuilder.AllowChildTag(childTag);
        }
 
        TagHelperCollection tagHelpers =
        [
            pTagHelperBuilder.Build()
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, document);
    }
 
    [Fact]
    public void UnderstandsNullTagNameWithAllowedChildrenForCatchAll()
    {
        // Arrange
        var documentContent = "<p></</p>";
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
                .AllowChildTag("custom")
                .Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("CatchAllTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
                .Build(),
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void UnderstandsNullTagNameWithAllowedChildrenForCatchAllWithPrefix()
    {
        // Arrange
        var documentContent = "<th:p></</th:p>";
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
                .AllowChildTag("custom")
                .Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("CatchAllTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
                .Build(),
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent, "th:");
    }
 
    [Fact]
    public void CanHandleStartTagOnlyTagTagMode()
    {
        // Arrange
        var documentContent = "<input>";
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("InputTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule =>
                    rule
                    .RequireTagName("input")
                    .RequireTagStructure(TagStructure.WithoutEndTag))
                .Build()
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void CreatesErrorForWithoutEndTagTagStructureForEndTags()
    {
        // Arrange
        var documentContent = "</input>";
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("InputTagHelper", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule =>
                    rule
                    .RequireTagName("input")
                    .RequireTagStructure(TagStructure.WithoutEndTag))
                .Build()
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    [Fact]
    public void CreatesErrorForInconsistentTagStructures()
    {
        // Arrange
        var documentContent = "<input>";
        TagHelperCollection tagHelpers =
        [
            TagHelperDescriptorBuilder.CreateTagHelper("InputTagHelper1", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule =>
                    rule
                    .RequireTagName("input")
                    .RequireTagStructure(TagStructure.WithoutEndTag))
                .Build(),
            TagHelperDescriptorBuilder.CreateTagHelper("InputTagHelper2", "SomeAssembly")
                .TagMatchingRuleDescriptor(rule =>
                    rule
                    .RequireTagName("input")
                    .RequireTagStructure(TagStructure.NormalOrSelfClosing))
                .Build()
        ];
 
        // Act & Assert
        EvaluateData(tagHelpers, documentContent);
    }
 
    public static readonly TagHelperCollection RequiredAttribute_TagHelpers =
    [
        TagHelperDescriptorBuilder.CreateTagHelper("pTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule =>
                rule
                .RequireTagName("p")
                .RequireAttributeDescriptor(attribute => attribute.Name("class")))
            .Build(),
        TagHelperDescriptorBuilder.CreateTagHelper("divTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule =>
                rule
                .RequireTagName("div")
                .RequireAttributeDescriptor(attribute => attribute.Name("class"))
                .RequireAttributeDescriptor(attribute => attribute.Name("style")))
            .Build(),
        TagHelperDescriptorBuilder.CreateTagHelper("catchAllTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule =>
                rule
                .RequireTagName("*")
                .RequireAttributeDescriptor(attribute => attribute.Name("catchAll")))
            .Build()
    ];
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly1()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<p />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly2()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<p></p>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly3()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly4()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div></div>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly5()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<p class=\"btn\" />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly6()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<p class=\"@DateTime.Now\" />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly7()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<p class=\"btn\">words and spaces</p>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly8()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<p class=\"@DateTime.Now\">words and spaces</p>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly9()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<p class=\"btn\">words<strong>and</strong>spaces</p>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly10()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<strong catchAll=\"hi\" />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly11()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<strong catchAll=\"@DateTime.Now\" />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly12()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<strong catchAll=\"hi\">words and spaces</strong>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly13()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<strong catchAll=\"@DateTime.Now\">words and spaces</strong>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly14()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div class=\"btn\" />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly15()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div class=\"btn\"></div>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly16()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<p notRequired=\"a\" class=\"btn\" />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly17()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<p notRequired=\"@DateTime.Now\" class=\"btn\" />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly18()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<p notRequired=\"a\" class=\"btn\">words and spaces</p>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly19()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div style=\"\" class=\"btn\" />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly20()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div style=\"@DateTime.Now\" class=\"btn\" />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly21()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div style=\"\" class=\"btn\">words and spaces</div>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly22()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div style=\"@DateTime.Now\" class=\"@DateTime.Now\">words and spaces</div>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly23()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div style=\"\" class=\"btn\">words<strong>and</strong>spaces</div>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly24()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<p class=\"btn\" catchAll=\"hi\" />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly25()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<p class=\"btn\" catchAll=\"hi\">words and spaces</p>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly26()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div style=\"\" class=\"btn\" catchAll=\"hi\" />");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly27()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div style=\"\" class=\"btn\" catchAll=\"hi\" >words and spaces</div>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly28()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div style=\"\" class=\"btn\" catchAll=\"@@hi\" >words and spaces</div>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly29()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div style=\"@DateTime.Now\" class=\"@DateTime.Now\" catchAll=\"@DateTime.Now\" >words and spaces</div>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly30()
    {
        EvaluateData(RequiredAttribute_TagHelpers, "<div style=\"\" class=\"btn\" catchAll=\"hi\" >words<strong>and</strong>spaces</div>");
    }
 
    public static readonly TagHelperCollection NestedRequiredAttribute_TagHelpers =
    [
        TagHelperDescriptorBuilder.CreateTagHelper("pTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule =>
                rule
                .RequireTagName("p")
                .RequireAttributeDescriptor(attribute => attribute.Name("class")))
            .Build(),
        TagHelperDescriptorBuilder.CreateTagHelper("catchAllTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule =>
                rule
                .RequireTagName("*")
                .RequireAttributeDescriptor(attribute => attribute.Name("catchAll")))
            .Build(),
    ];
 
    [Fact]
    public void NestedRequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly1()
    {
        EvaluateData(NestedRequiredAttribute_TagHelpers, "<p class=\"btn\"><p></p></p>");
    }
 
    [Fact]
    public void NestedRequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly2()
    {
        EvaluateData(NestedRequiredAttribute_TagHelpers, "<strong catchAll=\"hi\"><strong></strong></strong>");
    }
 
    [Fact]
    public void NestedRequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly3()
    {
        EvaluateData(NestedRequiredAttribute_TagHelpers, "<p class=\"btn\"><strong><p></p></strong></p>");
    }
 
    [Fact]
    public void NestedRequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly4()
    {
        EvaluateData(NestedRequiredAttribute_TagHelpers, "<strong catchAll=\"hi\"><p><strong></strong></p></strong>");
    }
 
    [Fact]
    public void NestedRequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly5()
    {
        EvaluateData(NestedRequiredAttribute_TagHelpers, "<p class=\"btn\"><strong catchAll=\"hi\"><p></p></strong></p>");
    }
 
    [Fact]
    public void NestedRequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly6()
    {
        EvaluateData(NestedRequiredAttribute_TagHelpers, "<strong catchAll=\"hi\"><p class=\"btn\"><strong></strong></p></strong>");
    }
 
    [Fact]
    public void NestedRequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly7()
    {
        EvaluateData(NestedRequiredAttribute_TagHelpers, "<p class=\"btn\"><p class=\"btn\"><p></p></p></p>");
    }
 
    [Fact]
    public void NestedRequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly8()
    {
        EvaluateData(NestedRequiredAttribute_TagHelpers, "<strong catchAll=\"hi\"><strong catchAll=\"hi\"><strong></strong></strong></strong>");
    }
 
    [Fact]
    public void NestedRequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly9()
    {
        EvaluateData(NestedRequiredAttribute_TagHelpers, "<p class=\"btn\"><p><p><p class=\"btn\"><p></p></p></p></p></p>");
    }
 
    [Fact]
    public void NestedRequiredAttributeDescriptorsCreateTagHelperBlocksCorrectly10()
    {
        EvaluateData(NestedRequiredAttribute_TagHelpers, "<strong catchAll=\"hi\"><strong><strong><strong catchAll=\"hi\"><strong></strong></strong></strong></strong></strong>");
    }
 
    public static readonly TagHelperCollection MalformedRequiredAttribute_TagHelpers =
    [
        TagHelperDescriptorBuilder.CreateTagHelper("pTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule =>
                rule
                .RequireTagName("p")
                .RequireAttributeDescriptor(attribute => attribute.Name("class")))
            .Build(),
    ];
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly1()
    {
        EvaluateData(MalformedRequiredAttribute_TagHelpers, "<p");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly2()
    {
        EvaluateData(MalformedRequiredAttribute_TagHelpers, "<p class=\"btn\"");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly3()
    {
        EvaluateData(MalformedRequiredAttribute_TagHelpers, "<p notRequired=\"hi\" class=\"btn\"");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly4()
    {
        EvaluateData(MalformedRequiredAttribute_TagHelpers, "<p></p");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly5()
    {
        EvaluateData(MalformedRequiredAttribute_TagHelpers, "<p class=\"btn\"></p");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly6()
    {
        EvaluateData(MalformedRequiredAttribute_TagHelpers, "<p notRequired=\"hi\" class=\"btn\"></p");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly7()
    {
        EvaluateData(MalformedRequiredAttribute_TagHelpers, "<p class=\"btn\" <p>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly8()
    {
        EvaluateData(MalformedRequiredAttribute_TagHelpers, "<p notRequired=\"hi\" class=\"btn\" <p>");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly9()
    {
        EvaluateData(MalformedRequiredAttribute_TagHelpers, "<p class=\"btn\" </p");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly10()
    {
        EvaluateData(MalformedRequiredAttribute_TagHelpers, "<p notRequired=\"hi\" class=\"btn\" </p");
    }
 
    [Fact]
    public void RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly11()
    {
        var document = "<p class='foo'>@if(true){</p>}</p>";
        EvaluateData(MalformedRequiredAttribute_TagHelpers, document);
    }
 
    public static readonly TagHelperCollection PrefixedTagHelperColon_TagHelpers =
    [
        TagHelperDescriptorBuilder.CreateTagHelper("mythTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("myth"))
            .Build(),
        TagHelperDescriptorBuilder.CreateTagHelper("mythTagHelper2", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("myth2"))
            .BoundAttributeDescriptor(attribute =>
                attribute
                .Name("bound")
                .PropertyName("Bound")
                .TypeName(typeof(bool).FullName))
            .Build()
    ];
 
    public static readonly TagHelperCollection PrefixedTagHelperCatchAll_TagHelpers =
    [
        TagHelperDescriptorBuilder.CreateTagHelper("mythTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
            .Build(),
    ];
 
    [Fact]
    public void AllowsPrefixedTagHelpers1()
    {
        EvaluateData(PrefixedTagHelperCatchAll_TagHelpers, "<th: />", tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void AllowsPrefixedTagHelpers2()
    {
        EvaluateData(PrefixedTagHelperCatchAll_TagHelpers, "<th:>words and spaces</th:>", tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void AllowsPrefixedTagHelpers3()
    {
        EvaluateData(PrefixedTagHelperColon_TagHelpers, "<th:myth />", tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void AllowsPrefixedTagHelpers4()
    {
        EvaluateData(PrefixedTagHelperColon_TagHelpers, "<th:myth></th:myth>", tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void AllowsPrefixedTagHelpers5()
    {
        EvaluateData(PrefixedTagHelperColon_TagHelpers, "<th:myth><th:my2th></th:my2th></th:myth>", tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void AllowsPrefixedTagHelpers6()
    {
        EvaluateData(PrefixedTagHelperColon_TagHelpers, "<!th:myth />", tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void AllowsPrefixedTagHelpers7()
    {
        EvaluateData(PrefixedTagHelperColon_TagHelpers, "<!th:myth></!th:myth>", tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void AllowsPrefixedTagHelpers8()
    {
        EvaluateData(PrefixedTagHelperColon_TagHelpers, "<th:myth class=\"btn\" />", tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void AllowsPrefixedTagHelpers9()
    {
        EvaluateData(PrefixedTagHelperColon_TagHelpers, "<th:myth2 class=\"btn\" />", tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void AllowsPrefixedTagHelpers10()
    {
        EvaluateData(PrefixedTagHelperColon_TagHelpers, "<th:myth class=\"btn\">words and spaces</th:myth>", tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void AllowsPrefixedTagHelpers11()
    {
        EvaluateData(PrefixedTagHelperColon_TagHelpers, "<th:myth2 bound=\"@DateTime.Now\" />", tagHelperPrefix: "th:");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithAttrTextTag1()
    {
        RunParseTreeRewriterTest("@{<!text class=\"btn\">}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithAttrTextTag2()
    {
        RunParseTreeRewriterTest("@{<!text class=\"btn\"></!text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithAttrTextTag3()
    {
        RunParseTreeRewriterTest("@{<!text class=\"btn\">words with spaces</!text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithAttrTextTag4()
    {
        RunParseTreeRewriterTest("@{<!text class='btn1 btn2' class2=btn></!text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithAttrTextTag5()
    {
        RunParseTreeRewriterTest("@{<!text class='btn1 @DateTime.Now btn2'></!text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithBlockTextTag1()
    {
        RunParseTreeRewriterTest("@{<!text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithBlockTextTag2()
    {
        RunParseTreeRewriterTest("@{</!text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithBlockTextTag3()
    {
        RunParseTreeRewriterTest("@{<!text></!text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithBlockTextTag4()
    {
        RunParseTreeRewriterTest("@{<!text>words and spaces</!text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithBlockTextTag5()
    {
        RunParseTreeRewriterTest("@{<!text></text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithBlockTextTag6()
    {
        RunParseTreeRewriterTest("@{<text></!text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithBlockTextTag7()
    {
        RunParseTreeRewriterTest("@{<!text><text></text></!text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithBlockTextTag8()
    {
        RunParseTreeRewriterTest("@{<text><!text></!text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTHElementOptForCompleteTextTagInCSharpBlock_WithBlockTextTag9()
    {
        RunParseTreeRewriterTest("@{<!text></!text></text>}", "p", "text");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteTextTagInCSharpBlock1()
    {
        RunParseTreeRewriterTest("@{<!text}", "text");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteTextTagInCSharpBlock2()
    {
        RunParseTreeRewriterTest("@{<!text /}", "text");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteTextTagInCSharpBlock3()
    {
        RunParseTreeRewriterTest("@{<!text class=}", "text");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteTextTagInCSharpBlock4()
    {
        RunParseTreeRewriterTest("@{<!text class=\"btn}", "text");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteTextTagInCSharpBlock5()
    {
        RunParseTreeRewriterTest("@{<!text class=\"btn\"}", "text");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteTextTagInCSharpBlock6()
    {
        RunParseTreeRewriterTest("@{<!text class=\"btn\" /}", "text");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTMLInCSharpBlock1()
    {
        RunParseTreeRewriterTest("@{<!}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTMLInCSharpBlock2()
    {
        RunParseTreeRewriterTest("@{<!p}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTMLInCSharpBlock3()
    {
        RunParseTreeRewriterTest("@{<!p /}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTMLInCSharpBlock4()
    {
        RunParseTreeRewriterTest("@{<!p class=}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTMLInCSharpBlock5()
    {
        RunParseTreeRewriterTest("@{<!p class=\"btn}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTMLInCSharpBlock6()
    {
        RunParseTreeRewriterTest("@{<!p class=\"btn@@}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTMLInCSharpBlock7()
    {
        RunParseTreeRewriterTest("@{<!p class=\"btn\"}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTMLInCSharpBlock8()
    {
        RunParseTreeRewriterTest("@{<!p class=\"btn\" /}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTML1()
    {
        RunParseTreeRewriterTest("<!", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTML2()
    {
        RunParseTreeRewriterTest("<!p", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTML3()
    {
        RunParseTreeRewriterTest("<!p /", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTML4()
    {
        RunParseTreeRewriterTest("<!p class=", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTML5()
    {
        RunParseTreeRewriterTest("<!p class=\"btn", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTML6()
    {
        RunParseTreeRewriterTest("<!p class=\"btn\"", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptForIncompleteHTML7()
    {
        RunParseTreeRewriterTest("<!p class=\"btn\" /", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithBlockData1()
    {
        RunParseTreeRewriterTest("@{<!p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithBlockData2()
    {
        RunParseTreeRewriterTest("@{</!p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithBlockData3()
    {
        RunParseTreeRewriterTest("@{<!p></!p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithBlockData4()
    {
        RunParseTreeRewriterTest("@{<!p>words and spaces</!p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithBlockData5()
    {
        RunParseTreeRewriterTest("@{<!p></p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithBlockData6()
    {
        RunParseTreeRewriterTest("@{<p></!p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithBlockData7()
    {
        RunParseTreeRewriterTest("@{<p><!p></!p></p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithBlockData8()
    {
        RunParseTreeRewriterTest("@{<p><!p></!p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithBlockData9()
    {
        RunParseTreeRewriterTest("@{<!p></!p></p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithBlockData10()
    {
        RunParseTreeRewriterTest("@{<strong></!p></strong>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithBlockData11()
    {
        RunParseTreeRewriterTest("@{<strong></strong><!p></!p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithBlockData12()
    {
        RunParseTreeRewriterTest("@{<p><strong></!strong><!p></strong></!p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithAttributeData1()
    {
        RunParseTreeRewriterTest("@{<!p class=\"btn\">}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithAttributeData2()
    {
        RunParseTreeRewriterTest("@{<!p class=\"btn\"></!p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithAttributeData3()
    {
        RunParseTreeRewriterTest("@{<!p class=\"btn\">words with spaces</!p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithAttributeData4()
    {
        RunParseTreeRewriterTest("@{<!p class='btn1 btn2' class2=btn></!p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutCSharp_WithAttributeData5()
    {
        RunParseTreeRewriterTest("@{<!p class='btn1 @DateTime.Now btn2'></!p>}", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithBlockData1()
    {
        RunParseTreeRewriterTest("<!p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithBlockData2()
    {
        RunParseTreeRewriterTest("</!p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithBlockData3()
    {
        RunParseTreeRewriterTest("<!p></!p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithBlockData4()
    {
        RunParseTreeRewriterTest("<!p>words and spaces</!p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithBlockData5()
    {
        RunParseTreeRewriterTest("<!p></p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithBlockData6()
    {
        RunParseTreeRewriterTest("<p></!p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithBlockData7()
    {
        RunParseTreeRewriterTest("<p><!p></!p></p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithBlockData8()
    {
        RunParseTreeRewriterTest("<p><!p></!p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithBlockData9()
    {
        RunParseTreeRewriterTest("<!p></!p></p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithBlockData10()
    {
        RunParseTreeRewriterTest("<strong></!p></strong>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithBlockData11()
    {
        RunParseTreeRewriterTest("<strong></strong><!p></!p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithBlockData12()
    {
        RunParseTreeRewriterTest("<p><strong></!strong><!p></strong></!p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithAttributeData1()
    {
        RunParseTreeRewriterTest("<!p class=\"btn\">", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithAttributeData2()
    {
        RunParseTreeRewriterTest("<!p class=\"btn\"></!p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithAttributeData3()
    {
        RunParseTreeRewriterTest("<!p class=\"btn\">words and spaces</!p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithAttributeData4()
    {
        RunParseTreeRewriterTest("<!p class='btn1 btn2' class2=btn></!p>", "strong", "p");
    }
 
    [Fact]
    public void AllowsTagHelperElementOptOutHTML_WithAttributeData5()
    {
        RunParseTreeRewriterTest("<!p class='btn1 @DateTime.Now btn2'></!p>", "strong", "p");
    }
 
    [Fact]
    public void DoesNotRewriteTextTagTransitionTagHelpers1()
    {
        RunParseTreeRewriterTest("<text>Hello World</text>", "p", "text");
    }
 
    [Fact]
    public void DoesNotRewriteTextTagTransitionTagHelpers2()
    {
        RunParseTreeRewriterTest("@{<text>Hello World</text>}", "p", "text");
    }
 
    [Fact]
    public void DoesNotRewriteTextTagTransitionTagHelpers3()
    {
        RunParseTreeRewriterTest("@{<text><p>Hello World</p></text>}", "p", "text");
    }
 
    [Fact]
    public void DoesNotRewriteTextTagTransitionTagHelpers4()
    {
        RunParseTreeRewriterTest("@{<p><text>Hello World</text></p>}", "p", "text");
    }
 
    [Fact]
    public void DoesNotRewriteSpecialTagTagHelpers1()
    {
        RunParseTreeRewriterTest("<foo><!-- Hello World --></foo>", "!--", "?xml", "![CDATA[", "!DOCTYPE");
    }
 
    [Fact]
    public void DoesNotRewriteSpecialTagTagHelpers2()
    {
        RunParseTreeRewriterTest("<foo><!-- @foo --></foo>", "!--", "?xml", "![CDATA[", "!DOCTYPE");
    }
 
    [Fact]
    public void DoesNotRewriteSpecialTagTagHelpers3()
    {
        RunParseTreeRewriterTest("<foo><?xml Hello World ?></foo>", "!--", "?xml", "![CDATA[", "!DOCTYPE");
    }
 
    [Fact]
    public void DoesNotRewriteSpecialTagTagHelpers4()
    {
        RunParseTreeRewriterTest("<foo><?xml @foo ?></foo>", "!--", "?xml", "![CDATA[", "!DOCTYPE");
    }
 
    [Fact]
    public void DoesNotRewriteSpecialTagTagHelpers5()
    {
        RunParseTreeRewriterTest("<foo><!DOCTYPE @foo ></foo>", "!--", "?xml", "![CDATA[", "!DOCTYPE");
    }
 
    [Fact]
    public void DoesNotRewriteSpecialTagTagHelpers6()
    {
        RunParseTreeRewriterTest("<foo><!DOCTYPE hello=\"world\" ></foo>", "!--", "?xml", "![CDATA[", "!DOCTYPE");
    }
 
    [Fact]
    public void DoesNotRewriteSpecialTagTagHelpers7()
    {
        RunParseTreeRewriterTest("<foo><![CDATA[ Hello World ]]></foo>", "!--", "?xml", "![CDATA[", "!DOCTYPE");
    }
 
    [Fact]
    public void DoesNotRewriteSpecialTagTagHelpers8()
    {
        RunParseTreeRewriterTest("<foo><![CDATA[ @foo ]]></foo>", "!--", "?xml", "![CDATA[", "!DOCTYPE");
    }
 
    [Fact]
    public void RewritesNestedTagHelperTagBlocks1()
    {
        RunParseTreeRewriterTest("<p><div></div></p>", "p", "div");
    }
 
    [Fact]
    public void RewritesNestedTagHelperTagBlocks2()
    {
        RunParseTreeRewriterTest("<p>Hello World <div></div></p>", "p", "div");
    }
 
    [Fact]
    public void RewritesNestedTagHelperTagBlocks3()
    {
        RunParseTreeRewriterTest("<p>Hel<p>lo</p></p> <p><div>World</div></p>", "p", "div");
    }
 
    [Fact]
    public void RewritesNestedTagHelperTagBlocks4()
    {
        RunParseTreeRewriterTest("<p>Hel<strong>lo</strong></p> <p><span>World</span></p>", "p", "div");
    }
 
    [Fact]
    public void HandlesMalformedNestedNonTagHelperTags_Correctly()
    {
        RunParseTreeRewriterTest("<div>@{</div>}");
    }
 
    [Fact]
    public void HandlesNonTagHelperStartAndEndVoidTags_Correctly()
    {
        RunParseTreeRewriterTest("<input>Foo</input>");
    }
 
    public static readonly TagHelperCollection CaseSensitive_TagHelpers =
    [
        TagHelperDescriptorBuilder.CreateTagHelper("pTagHelper", "SomeAssembly")
            .SetCaseSensitive()
            .BoundAttributeDescriptor(attribute =>
                attribute
                .Name("bound")
                .PropertyName("Bound")
                .TypeName(typeof(bool).FullName))
            .TagMatchingRuleDescriptor(rule =>
                rule
                .RequireTagName("p")
                .RequireAttributeDescriptor(attribute => attribute.Name("class")))
            .Build(),
        TagHelperDescriptorBuilder.CreateTagHelper("catchAllTagHelper", "SomeAssembly")
            .TagMatchingRuleDescriptor(rule =>
                rule
                .RequireTagName("*")
                .RequireAttributeDescriptor(attribute => attribute.Name("catchAll")))
            .Build(),
    ];
 
    [Fact]
    public void HandlesCaseSensitiveTagHelpersCorrectly1()
    {
        EvaluateData(CaseSensitive_TagHelpers, "<p class='foo' catchAll></p>");
    }
 
    [Fact]
    public void HandlesCaseSensitiveTagHelpersCorrectly2()
    {
        EvaluateData(CaseSensitive_TagHelpers, "<p CLASS='foo' CATCHAll></p>");
    }
 
    [Fact]
    public void HandlesCaseSensitiveTagHelpersCorrectly3()
    {
        EvaluateData(CaseSensitive_TagHelpers, "<P class='foo' CATCHAll></P>");
    }
 
    [Fact]
    public void HandlesCaseSensitiveTagHelpersCorrectly4()
    {
        EvaluateData(CaseSensitive_TagHelpers, "<P class='foo'></P>");
    }
 
    [Fact]
    public void HandlesCaseSensitiveTagHelpersCorrectly5()
    {
        EvaluateData(CaseSensitive_TagHelpers, "<p Class='foo'></p>");
    }
}