diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs
new file mode 100644
index 0000000000..4d60856852
--- /dev/null
+++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs
@@ -0,0 +1,982 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using Microsoft.AspNet.Razor.Generator;
+using Microsoft.AspNet.Razor.Parser;
+using Microsoft.AspNet.Razor.Parser.SyntaxTree;
+using Microsoft.AspNet.Razor.Parser.TagHelpers.Internal;
+using Microsoft.AspNet.Razor.TagHelpers;
+using Microsoft.AspNet.Razor.Test.Framework;
+using Microsoft.AspNet.Razor.Text;
+using Microsoft.AspNet.Razor.Tokenizer;
+using Xunit;
+
+namespace Microsoft.AspNet.Razor.Test.TagHelpers
+{
+ public class TagHelperParseTreeRewriterTest : CsHtmlMarkupParserTestBase
+ {
+ public static IEnumerable
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("script",
+ factory.Markup("Hel
lo
")),
+ factory.Markup(" "),
+ new MarkupTagHelperBlock("p",
+ new MarkupTagHelperBlock("div",
+ factory.Markup("World"))))
+ };
+ yield return new object[] {
+ " ",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("script",
+ factory.Markup("Hello")),
+ factory.Markup(" "),
+ new MarkupTagHelperBlock("script",
+ factory.Markup("World")))
+ };
+ yield return new object[] {
+ "",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("script",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) },
+ { "style", new MarkupBlock(factory.Markup("color:red;")) }
+ }))
+ };
+ yield return new object[] {
+ "
Hello World
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ factory.Markup("Hello "),
+ new MarkupTagHelperBlock("script",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) },
+ { "style", new MarkupBlock(factory.Markup("color:red;")) }
+ }),
+ factory.Markup(" World")))
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(ScriptBlockData))]
+ public void TagHelperParseTreeRewriter_RewritesScriptTagHelpers(
+ string documentContent,
+ MarkupBlock expectedOutput)
+ {
+ RunParseTreeRewriterTest(documentContent, expectedOutput, "p", "div", "script");
+ }
+
+ public static IEnumerable SelfClosingBlockData
+ {
+ get
+ {
+ var factory = CreateDefaultSpanFactory();
+
+ yield return new object[] {
+ "",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) },
+ { "style", new MarkupBlock(factory.Markup("color:red;")) }
+ }))
+ };
+ yield return new object[] {
+ "
Hello
World",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ factory.Markup("Hello "),
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) },
+ { "style", new MarkupBlock(factory.Markup("color:red;")) }
+ }),
+ factory.Markup(" World")))
+ };
+ yield return new object[] {
+ "Hello World",
+ new MarkupBlock(
+ factory.Markup("Hello"),
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) }
+ }),
+ factory.Markup(" "),
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "style", new MarkupBlock(factory.Markup("color:red;")) }
+ }),
+ factory.Markup("World"))
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(SelfClosingBlockData))]
+ public void TagHelperParseTreeRewriter_RewritesSelfClosingTagHelpers(
+ string documentContent,
+ MarkupBlock expectedOutput)
+ {
+ RunParseTreeRewriterTest(documentContent, expectedOutput, "p");
+ }
+
+ public static IEnumerable QuotelessAttributeBlockData
+ {
+ get
+ {
+ var factory = CreateDefaultSpanFactory();
+ var blockFactory = new BlockFactory(factory);
+ var dateTimeNow = new MarkupBlock(
+ new ExpressionBlock(
+ factory.CodeTransition(),
+ factory.Code("DateTime.Now")
+ .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
+ .Accepts(AcceptedCharacters.NonWhiteSpace)));
+
+ yield return new object[] {
+ "",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) },
+ { "dynamic", new MarkupBlock(dateTimeNow) },
+ { "style", new MarkupBlock(factory.Markup("color:red;")) }
+ }))
+ };
+ yield return new object[] {
+ "
Hello World
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) },
+ { "dynamic", new MarkupBlock(dateTimeNow) },
+ { "style", new MarkupBlock(factory.Markup("color:red;")) }
+ },
+ factory.Markup("Hello World")))
+ };
+ yield return new object[] {
+ "
Hello
World
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) },
+ { "dynamic", new MarkupBlock(dateTimeNow) }
+ },
+ factory.Markup("Hello")),
+ factory.Markup(" "),
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "style", new MarkupBlock(factory.Markup("color:red;")) },
+ { "dynamic", new MarkupBlock(dateTimeNow) }
+ },
+ factory.Markup("World")))
+ };
+ yield return new object[] {
+ "
Hello World inside of strong tag
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) },
+ { "dynamic", new MarkupBlock(dateTimeNow) },
+ { "style", new MarkupBlock(factory.Markup("color:red;")) }
+ },
+ factory.Markup("Hello World "),
+ new MarkupTagBlock(
+ factory.Markup("(" class=\"", 71, 0, 71),
+ suffix: new LocationTagged("\"", 82, 0, 82)),
+ factory.Markup(" class=\"").With(SpanCodeGenerator.Null),
+ factory.Markup("foo").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged(string.Empty, 79, 0, 79),
+ value: new LocationTagged("foo", 79, 0, 79))),
+ factory.Markup("\"").With(SpanCodeGenerator.Null)),
+ factory.Markup(">")),
+ factory.Markup("inside of strong tag"),
+ blockFactory.MarkupTagBlock("")))
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(QuotelessAttributeBlockData))]
+ public void TagHelperParseTreeRewriter_RewritesTagHelpersWithQuotelessAttributes(
+ string documentContent,
+ MarkupBlock expectedOutput)
+ {
+ RunParseTreeRewriterTest(documentContent, expectedOutput, "p");
+ }
+
+ public static IEnumerable PlainAttributeBlockData
+ {
+ get
+ {
+ var factory = CreateDefaultSpanFactory();
+ var blockFactory = new BlockFactory(factory);
+
+ yield return new object[] {
+ "",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) },
+ { "style", new MarkupBlock(factory.Markup("color:red;")) }
+ }))
+ };
+ yield return new object[] {
+ "
Hello World
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) },
+ { "style", new MarkupBlock(factory.Markup("color:red;")) }
+ },
+ factory.Markup("Hello World")))
+ };
+ yield return new object[] {
+ "
Hello
World
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) }
+ },
+ factory.Markup("Hello")),
+ factory.Markup(" "),
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "style", new MarkupBlock(factory.Markup("color:red;")) }
+ },
+ factory.Markup("World")))
+ };
+ yield return new object[] {
+ "
Hello World inside of strong tag
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ new Dictionary
+ {
+ { "class", new MarkupBlock(factory.Markup("foo")) },
+ { "style", new MarkupBlock(factory.Markup("color:red;")) }
+ },
+ factory.Markup("Hello World "),
+ new MarkupTagBlock(
+ factory.Markup("(" class=\"", 53, 0, 53),
+ suffix: new LocationTagged("\"", 64, 0, 64)),
+ factory.Markup(" class=\"").With(SpanCodeGenerator.Null),
+ factory.Markup("foo").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged(string.Empty, 61, 0, 61),
+ value: new LocationTagged("foo", 61, 0, 61))),
+ factory.Markup("\"").With(SpanCodeGenerator.Null)),
+ factory.Markup(">")),
+ factory.Markup("inside of strong tag"),
+ blockFactory.MarkupTagBlock("")))
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(PlainAttributeBlockData))]
+ public void TagHelperParseTreeRewriter_RewritesTagHelpersWithPlainAttributes(
+ string documentContent,
+ MarkupBlock expectedOutput)
+ {
+ RunParseTreeRewriterTest(documentContent, expectedOutput, "p");
+ }
+
+ public static IEnumerable PlainBlockData
+ {
+ get
+ {
+ var factory = CreateDefaultSpanFactory();
+ var blockFactory = new BlockFactory(factory);
+
+ yield return new object[] {
+ "",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p"))
+ };
+ yield return new object[] {
+ "
Hello World
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ factory.Markup("Hello World")))
+ };
+ yield return new object[] {
+ "
Hello
World
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ factory.Markup("Hello")),
+ factory.Markup(" "),
+ new MarkupTagHelperBlock("p",
+ factory.Markup("World")))
+ };
+ yield return new object[] {
+ "
Hello World inside of strong tag
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ factory.Markup("Hello World "),
+ blockFactory.MarkupTagBlock(""),
+ factory.Markup("inside of strong tag"),
+ blockFactory.MarkupTagBlock("")))
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(PlainBlockData))]
+ public void TagHelperParseTreeRewriter_RewritesPlainTagHelperTagBlocks(
+ string documentContent,
+ MarkupBlock expectedOutput)
+ {
+ RunParseTreeRewriterTest(documentContent, expectedOutput, "p");
+ }
+
+ public static IEnumerable NestedBlockData
+ {
+ get
+ {
+ var factory = CreateDefaultSpanFactory();
+ var blockFactory = new BlockFactory(factory);
+
+ yield return new object[] {
+ "",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ new MarkupTagHelperBlock("div")))
+ };
+ yield return new object[] {
+ "
Hello World
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ factory.Markup("Hello World "),
+ new MarkupTagHelperBlock("div")))
+ };
+ yield return new object[] {
+ "
Hel
lo
World
",
+ new MarkupBlock(
+ new MarkupTagHelperBlock("p",
+ factory.Markup("Hel"),
+ new MarkupTagHelperBlock("p",
+ factory.Markup("lo"))),
+ factory.Markup(" "),
+ new MarkupTagHelperBlock("p",
+ new MarkupTagHelperBlock("div",
+ factory.Markup("World"))))
+ };
+ yield return new object[] {
+ "