diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs index 42aeb729c0..0d44d5de5e 100644 --- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs @@ -3,17 +3,2152 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; +using Microsoft.AspNet.Razor.Chunks.Generators; using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Test.Framework; using Microsoft.AspNet.Razor.Test.TagHelpers; +using Microsoft.AspNet.Razor.Text; using Xunit; namespace Microsoft.AspNet.Razor.TagHelpers { public class TagHelperBlockRewriterTest : TagHelperRewritingTestBase { + public static TheoryData MalformedTagHelperAttributeBlockData + { + get + { + var factory = CreateDefaultSpanFactory(); + var blockFactory = new BlockFactory(factory); + var errorFormatUnclosed = "Found a malformed '{0}' tag helper. Tag helpers must have a start and " + + "end tag or be self closing."; + var errorFormatNoCloseAngle = "Missing close angle for tag helper '{0}'."; + var errorFormatNoCSharp = "The tag helper '{0}' must not have C# in the element's attribute " + + "declaration area."; + Func createInvalidDoBlock = extraCode => + { + return new MarkupBlock( + new MarkupBlock( + new StatementBlock( + factory.CodeTransition(), + factory.Code("do {" + extraCode).AsStatement()))); + }; + var dateTimeNow = new MarkupBlock( + new MarkupBlock( + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("DateTime.Now") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)))); + + return new TheoryData + { + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List>())), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("bar", factory.Markup("false")) + })), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero) + } + }, + { + "

> + { + new KeyValuePair( + "bar", + new MarkupBlock( + factory.Markup("false"), + factory.Markup(" > + { + new KeyValuePair( + "bar", + factory.Markup("false")) + })), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero), + new RazorError( + "TagHelper attributes must be well-formed.", + absoluteIndex: 12, + lineIndex: 0, + columnIndex: 12) + } + }, + { + "

> + { + new KeyValuePair( + "bar", + factory.Markup("false'")) + })), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair( + "bar", + new MarkupBlock( + factory.Markup("false'"), + factory.Markup(" >

"))) + })), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("foo", null), + new KeyValuePair("bar", null) + }, + new MarkupTagHelperBlock("strong"))), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), + absoluteIndex: 10, lineIndex: 0, columnIndex: 10) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", factory.Markup("btn")) + })), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", factory.Markup("btn")) + })), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair( + "class", + new MarkupBlock(factory.Markup("btn"), factory.Markup(" bar="))), + new KeyValuePair("foo", null) + }, + new MarkupTagHelperBlock("strong"))), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), + absoluteIndex: 23, lineIndex: 0, columnIndex: 23) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair( + "class", + new MarkupBlock(factory.Markup("btn"), factory.Markup(" bar="))), + new KeyValuePair("foo", null), + })), + new RazorError[0] + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p")), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCSharp, "p"), + absoluteIndex: 3, lineIndex: 0 , columnIndex: 3) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p")), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCSharp, "p"), + absoluteIndex: 3, lineIndex: 0 , columnIndex: 3) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", dateTimeNow) + })), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero) + } + }, + { + "

> + { + new KeyValuePair( + "class", + createInvalidDoBlock(string.Empty)) + })), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero), + new RazorError( + RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF("do", "}", "{"), + absoluteIndex: 11, lineIndex: 0, columnIndex: 11) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", createInvalidDoBlock("\">

")) + })), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero), + new RazorError( + RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF("do", "}", "{"), + absoluteIndex: 11, lineIndex: 0, columnIndex: 11), + new RazorError( + RazorResources.ParseError_Unterminated_String_Literal, + absoluteIndex: 15, lineIndex: 0, columnIndex: 15) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p")), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCSharp, "p"), + absoluteIndex: 3, lineIndex: 0 , columnIndex: 3), + new RazorError( + RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF("do", "}", "{"), + absoluteIndex: 4, lineIndex: 0, columnIndex: 4), + new RazorError( + RazorResources.FormatParseError_UnexpectedEndTag("p"), + absoluteIndex: 29, lineIndex: 0, columnIndex: 29) + } + } + }; + } + } + + [Theory] + [MemberData(nameof(MalformedTagHelperAttributeBlockData))] + public void Rewrite_CreatesErrorForMalformedTagHelpersWithAttributes( + string documentContent, + MarkupBlock expectedOutput, + RazorError[] expectedErrors) + { + RunParseTreeRewriterTest(documentContent, expectedOutput, expectedErrors, "strong", "p"); + } + + public static TheoryData MalformedTagHelperBlockData + { + get + { + var factory = CreateDefaultSpanFactory(); + var blockFactory = new BlockFactory(factory); + var errorFormatUnclosed = "Found a malformed '{0}' tag helper. Tag helpers must have a start and " + + "end tag or be self closing."; + var errorFormatNoCloseAngle = "Missing close angle for tag helper '{0}'."; + + return new TheoryData + { + { + "", + new MarkupBlock( + new MarkupTagHelperBlock("strong", + new MarkupTagHelperBlock("p"))), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "strong"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + absoluteIndex: 8, lineIndex: 0, columnIndex: 8) + } + }, + { + " <

", + new MarkupBlock( + blockFactory.MarkupTagBlock("<"), + blockFactory.MarkupTagBlock("<"), + blockFactory.MarkupTagBlock(""), + factory.Markup(" "), + blockFactory.MarkupTagBlock("<"), + new MarkupTagHelperBlock("p")), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), + absoluteIndex: 2, lineIndex: 0, columnIndex: 2), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + absoluteIndex: 13, lineIndex: 0, columnIndex: 13) + } + }, + { + "<<> <<>>", + new MarkupBlock( + blockFactory.MarkupTagBlock("<"), + blockFactory.MarkupTagBlock("<"), + new MarkupTagHelperBlock("strong", + factory.Markup("> "), + blockFactory.MarkupTagBlock("<"), + blockFactory.MarkupTagBlock("<>"), + factory.Markup(">"))), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), + absoluteIndex: 2, lineIndex: 0, columnIndex: 2) + } + }, + { + "

", + new MarkupBlock( + blockFactory.MarkupTagBlock(""))), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + absoluteIndex: 12, lineIndex: 0, columnIndex: 12) + } + } + }; + } + } + + [Theory] + [MemberData(nameof(MalformedTagHelperBlockData))] + public void Rewrite_CreatesErrorForMalformedTagHelper( + string documentContent, + MarkupBlock expectedOutput, + RazorError[] expectedErrors) + { + RunParseTreeRewriterTest(documentContent, expectedOutput, expectedErrors, "strong", "p"); + } + + public static TheoryData CodeTagHelperAttributesData + { + get + { + var factory = CreateDefaultSpanFactory(); + var dateTimeNow = new MarkupBlock( + factory.Markup(" "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("DateTime.Now") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))); + + return new TheoryData + { + { + "", + new MarkupBlock( + new MarkupTagHelperBlock("person", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("age", factory.CodeMarkup("12")) + })) + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock("person", + selfClosing: true, + attributes: new List> + { + new KeyValuePair( + "birthday", + factory.CodeMarkup("DateTime.Now")) + })) + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock("person", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("name", factory.Markup("John")) + })) + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock("person", + selfClosing: true, + attributes: new List> + { + new KeyValuePair( + "name", + new MarkupBlock(factory.Markup("Time:"), dateTimeNow)) + })) + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock("person", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("age", factory.CodeMarkup("12")), + new KeyValuePair( + "birthday", + factory.CodeMarkup("DateTime.Now")), + new KeyValuePair( + "name", + new MarkupBlock(factory.Markup("Time:"), dateTimeNow)) + })) + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock("person", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("age", factory.CodeMarkup("12")), + new KeyValuePair( + "birthday", + factory.CodeMarkup("DateTime.Now")), + new KeyValuePair( + "name", + new MarkupBlock( + factory.Markup("Time:"), + new MarkupBlock( + factory.Markup(" @").Accepts(AcceptedCharacters.None), + factory.Markup("@") + .With(SpanChunkGenerator.Null) + .Accepts(AcceptedCharacters.None)), + dateTimeNow)) + })) + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock("person", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("age", factory.CodeMarkup("12")), + new KeyValuePair( + "birthday", + factory.CodeMarkup("DateTime.Now")), + new KeyValuePair( + "name", + new MarkupBlock( + new MarkupBlock( + factory.Markup("@").Accepts(AcceptedCharacters.None), + factory.Markup("@") + .With(SpanChunkGenerator.Null) + .Accepts(AcceptedCharacters.None)), + factory.Markup("BoundStringAttribute"))) + })) + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock("person", + selfClosing: true, + attributes: new List> + { + new KeyValuePair( + "age", + new MarkupBlock( + new MarkupBlock( + factory.Markup("@").Accepts(AcceptedCharacters.None), + factory.Markup("@") + .With(SpanChunkGenerator.Null) + .Accepts(AcceptedCharacters.None)), + new MarkupBlock( + factory.EmptyHtml(), + new ExpressionBlock( + factory.CodeTransition(), + factory.MetaCode("(").Accepts(AcceptedCharacters.None), + factory.Code("11+1").AsExpression(), + factory.MetaCode(")").Accepts(AcceptedCharacters.None))))), + new KeyValuePair( + "birthday", + factory.CodeMarkup("DateTime.Now")), + new KeyValuePair( + "name", + new MarkupBlock(factory.Markup("Time:"), dateTimeNow)) + })) + }, + }; + } + } + + [Theory] + [MemberData(nameof(CodeTagHelperAttributesData))] + public void TagHelperParseTreeRewriter_CreatesMarkupCodeSpansForNonStringTagHelperAttributes( + string documentContent, + MarkupBlock expectedOutput) + { + // Arrange + var descriptors = new TagHelperDescriptor[] + { + new TagHelperDescriptor("person", "PersonTagHelper", "personAssembly", + attributes: new[] + { + new TagHelperAttributeDescriptor("age", "Age", typeof(int).FullName, isIndexer: false), + new TagHelperAttributeDescriptor( + "birthday", + "BirthDay", + typeof(DateTime).FullName, + isIndexer: false), + new TagHelperAttributeDescriptor("name", "Name", typeof(string).FullName, isIndexer: false), + }) + }; + var providerContext = new TagHelperDescriptorProvider(descriptors); + + // Act & Assert + EvaluateData(providerContext, + documentContent, + expectedOutput, + expectedErrors: Enumerable.Empty()); + } + + public static IEnumerable IncompleteHelperBlockData + { + get + { + var factory = CreateDefaultSpanFactory(); + var blockFactory = new BlockFactory(factory); + var malformedErrorFormat = "Found a malformed '{0}' tag helper. Tag helpers must have a start and " + + "end tag or be self closing."; + 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 List> + { + new KeyValuePair("class", factory.Markup("foo")), + new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)), + new KeyValuePair("style", factory.Markup("color:red;")) + }, + new MarkupTagHelperBlock("strong")), + blockFactory.MarkupTagBlock("")), + new RazorError[] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "strong"), + absoluteIndex: 52, lineIndex: 0, columnIndex: 52), + new RazorError( + string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "strong"), + absoluteIndex: 64, lineIndex: 0, columnIndex: 64) + } + }; + yield return new object[] + { + "

Hello World

", + new MarkupBlock( + blockFactory.MarkupTagBlock("
"), + new MarkupTagHelperBlock("p", + factory.Markup("Hello "), + new MarkupTagHelperBlock("strong", + factory.Markup("World")), + blockFactory.MarkupTagBlock("
"))), + new RazorError[] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "p"), + absoluteIndex: 5, lineIndex: 0, columnIndex: 5) + } + }; + yield return new object[] + { + "

Hello World

", + new MarkupBlock( + blockFactory.MarkupTagBlock("
"), + new MarkupTagHelperBlock("p", + factory.Markup("Hello "), + new MarkupTagHelperBlock("strong", + factory.Markup("World"), + blockFactory.MarkupTagBlock("
")))), + new RazorError[] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "p"), + absoluteIndex: 5, lineIndex: 0, columnIndex: 5), + new RazorError( + string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "strong"), + absoluteIndex: 14, lineIndex: 0, columnIndex: 14) + } + }; + yield return new object[] + { + "

Hello

World

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", factory.Markup("foo")) + }, + factory.Markup("Hello "), + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("style", factory.Markup("color:red;")) + }, + factory.Markup("World")))), + new RazorError[] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "p"), + SourceLocation.Zero) + } + }; + } + } + + [Theory] + [MemberData(nameof(IncompleteHelperBlockData))] + public void TagHelperParseTreeRewriter_CreatesErrorForIncompleteTagHelper( + string documentContent, + MarkupBlock expectedOutput, + RazorError[] expectedErrors) + { + RunParseTreeRewriterTest(documentContent, expectedOutput, expectedErrors, "strong", "p"); + } + + + public static IEnumerable OddlySpacedBlockData + { + get + { + var factory = CreateDefaultSpanFactory(); + + yield return new object[] + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", factory.Markup(" foo")), + new KeyValuePair( + "style", + new MarkupBlock( + factory.Markup(" color"), + factory.Markup(" :"), + factory.Markup(" red"), + factory.Markup(" ;"), + factory.Markup(" "))) + })) + }; + yield return new object[] + { + "

Hello World

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", factory.Markup(" foo")), + new KeyValuePair( + "style", + new MarkupBlock( + factory.Markup(" color"), + factory.Markup(" :"), + factory.Markup(" red"), + factory.Markup(" ;"), + factory.Markup(" "))) + }, + factory.Markup("Hello World"))) + }; + yield return new object[] + { + "

Hello

World

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair( + "class", + new MarkupBlock(factory.Markup(" foo"), factory.Markup(" "))) + }, + factory.Markup("Hello")), + factory.Markup(" "), + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair( + "style", + new MarkupBlock(factory.Markup(" color:red;"), factory.Markup(" "))) + }, + factory.Markup("World"))) + }; + } + } + + [Theory] + [MemberData(nameof(OddlySpacedBlockData))] + public void TagHelperParseTreeRewriter_RewritesOddlySpacedTagHelperTagBlocks( + string documentContent, + MarkupBlock expectedOutput) + { + RunParseTreeRewriterTest(documentContent, expectedOutput, "p"); + } + + public static IEnumerable ComplexAttributeTagHelperBlockData + { + get + { + var factory = CreateDefaultSpanFactory(); + var blockFactory = new BlockFactory(factory); + var dateTimeNowString = "@DateTime.Now"; + var dateTimeNow = new MarkupBlock( + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("DateTime.Now") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))); + var doWhileString = "@do { var foo = bar; Foo foo++; } while (foo);"; + var doWhile = new MarkupBlock( + new StatementBlock( + factory.CodeTransition(), + factory.Code("do { var foo = bar;").AsStatement(), + new MarkupBlock( + new MarkupTagBlock( + factory.MarkupTransition("")), + factory.Markup("Foo").Accepts(AcceptedCharacters.None), + new MarkupTagBlock( + factory.MarkupTransition("")), + factory.CodeMarkup(" ").With(new StatementChunkGenerator()).Accepts(AcceptedCharacters.None)), + factory.Code("foo++; } while (foo);").AsStatement().Accepts(AcceptedCharacters.None))); + + var currentFormattedString = "

"; + yield return new object[] + { + string.Format(currentFormattedString, dateTimeNowString), + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", new MarkupBlock(dateTimeNow)), + new KeyValuePair("style", new MarkupBlock(dateTimeNow)) + })) + }; + yield return new object[] + { + string.Format(currentFormattedString, doWhileString), + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", new MarkupBlock(doWhile)), + new KeyValuePair("style", new MarkupBlock(doWhile)) + })) + }; + + currentFormattedString = "

Hello World

"; + yield return new object[] + { + string.Format(currentFormattedString, dateTimeNowString), + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", new MarkupBlock(dateTimeNow)), + new KeyValuePair("style", new MarkupBlock(dateTimeNow)) + }, + factory.Markup("Hello World"))) + }; + yield return new object[] + { + string.Format(currentFormattedString, doWhileString), + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", new MarkupBlock(doWhile)), + new KeyValuePair("style", new MarkupBlock(doWhile)) + }, + factory.Markup("Hello World"))) + }; + + currentFormattedString = "

Hello

World

"; + yield return new object[] + { + string.Format(currentFormattedString, dateTimeNowString), + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", new MarkupBlock(dateTimeNow)) + }, + factory.Markup("Hello")), + factory.Markup(" "), + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("style", new MarkupBlock(dateTimeNow)) + }, + factory.Markup("World"))) + }; + yield return new object[] + { + string.Format(currentFormattedString, doWhileString), + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", new MarkupBlock(doWhile)) + }, + factory.Markup("Hello")), + factory.Markup(" "), + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("style", new MarkupBlock(doWhile)) + }, + factory.Markup("World"))) + }; + + currentFormattedString = + "

Hello World inside of strong tag

"; + yield return new object[] + { + string.Format(currentFormattedString, dateTimeNowString), + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", new MarkupBlock(dateTimeNow)), + new KeyValuePair("style", new MarkupBlock(dateTimeNow)) + }, + factory.Markup("Hello World "), + new MarkupTagBlock( + factory.Markup("(" class=\"", 66, 0, 66), + suffix: new LocationTagged("\"", 87, 0, 87)), + factory.Markup(" class=\"").With(SpanChunkGenerator.Null), + new MarkupBlock( + new DynamicAttributeBlockChunkGenerator( + new LocationTagged(string.Empty, 74, 0, 74), 74, 0, 74), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("DateTime.Now") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + factory.Markup("\"").With(SpanChunkGenerator.Null)), + factory.Markup(">")), + factory.Markup("inside of strong tag"), + blockFactory.MarkupTagBlock("
"))) + }; + } + } + + [Theory] + [MemberData(nameof(ComplexAttributeTagHelperBlockData))] + public void TagHelperParseTreeRewriter_RewritesComplexAttributeTagHelperTagBlocks( + string documentContent, + MarkupBlock expectedOutput) + { + RunParseTreeRewriterTest(documentContent, expectedOutput, "p"); + } + + public static IEnumerable ComplexTagHelperBlockData + { + get + { + var factory = CreateDefaultSpanFactory(); + var blockFactory = new BlockFactory(factory); + var dateTimeNowString = "@DateTime.Now"; + var dateTimeNow = new ExpressionBlock( + factory.CodeTransition(), + factory.Code("DateTime.Now") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)); + var doWhileString = "@do { var foo = bar;

Foo

foo++; } while (foo);"; + var doWhile = new StatementBlock( + factory.CodeTransition(), + factory.Code("do { var foo = bar;").AsStatement(), + new MarkupBlock( + factory.Markup(" "), + new MarkupTagHelperBlock("p", + factory.Markup("Foo")), + factory.Markup(" ").Accepts(AcceptedCharacters.None)), + factory.Code("foo++; } while (foo);") + .AsStatement() + .Accepts(AcceptedCharacters.None)); + + var currentFormattedString = "

{0}

"; + yield return new object[] + { + string.Format(currentFormattedString, dateTimeNowString), + new MarkupBlock( + new MarkupTagHelperBlock("p", dateTimeNow)) + }; + yield return new object[] + { + string.Format(currentFormattedString, doWhileString), + new MarkupBlock( + new MarkupTagHelperBlock("p", doWhile)) + }; + + currentFormattedString = "

Hello World {0}

"; + yield return new object[] + { + string.Format(currentFormattedString, dateTimeNowString), + new MarkupBlock( + new MarkupTagHelperBlock("p", + factory.Markup("Hello World "), + dateTimeNow)) + }; + yield return new object[] + { + string.Format(currentFormattedString, doWhileString), + new MarkupBlock( + new MarkupTagHelperBlock("p", + factory.Markup("Hello World "), + doWhile)) + }; + + currentFormattedString = "

{0}

{0}

"; + yield return new object[] + { + string.Format(currentFormattedString, dateTimeNowString), + new MarkupBlock( + new MarkupTagHelperBlock("p", dateTimeNow), + factory.Markup(" "), + new MarkupTagHelperBlock("p", dateTimeNow)) + }; + yield return new object[] + { + string.Format(currentFormattedString, doWhileString), + new MarkupBlock( + new MarkupTagHelperBlock("p", doWhile), + factory.Markup(" "), + new MarkupTagHelperBlock("p", doWhile)) + }; + + currentFormattedString = "

Hello {0}inside of {0} strong tag

"; + yield return new object[] + { + string.Format(currentFormattedString, dateTimeNowString), + new MarkupBlock( + new MarkupTagHelperBlock("p", + factory.Markup("Hello "), + dateTimeNow, + blockFactory.MarkupTagBlock(""), + factory.Markup("inside of "), + dateTimeNow, + factory.Markup(" strong tag"), + blockFactory.MarkupTagBlock(""))) + }; + yield return new object[] + { + string.Format(currentFormattedString, doWhileString), + new MarkupBlock( + new MarkupTagHelperBlock("p", + factory.Markup("Hello "), + doWhile, + blockFactory.MarkupTagBlock(""), + factory.Markup("inside of "), + doWhile, + factory.Markup(" strong tag"), + blockFactory.MarkupTagBlock(""))) + }; + } + } + + [Theory] + [MemberData(nameof(ComplexTagHelperBlockData))] + public void TagHelperParseTreeRewriter_RewritesComplexTagHelperTagBlocks( + string documentContent, + MarkupBlock expectedOutput) + { + RunParseTreeRewriterTest(documentContent, expectedOutput, "p"); + } + + + public static TheoryData InvalidHtmlBlockData + { + get + { + var factory = CreateDefaultSpanFactory(); + var blockFactory = new BlockFactory(factory); + var dateTimeNow = new ExpressionBlock( + factory.CodeTransition(), + factory.Code("DateTime.Now") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)); + + return new TheoryData + { + { + "<<

>>

", + new MarkupBlock( + blockFactory.MarkupTagBlock("<"), + blockFactory.MarkupTagBlock("<"), + new MarkupTagHelperBlock("p", + factory.Markup(">>"))) + }, + { + "<

", + new MarkupBlock( + blockFactory.MarkupTagBlock("<"), + new MarkupTagHelperBlock("p", selfClosing: true)) + }, + { + "< p />", + new MarkupBlock( + new MarkupTagBlock( + factory.Markup("<"), + new MarkupBlock( + factory.Markup(" p")), + factory.Markup(" />"))) + }, + { + "", + new MarkupBlock( + blockFactory.MarkupTagBlock("", + new MarkupBlock( + new MarkupTagBlock( + factory.Markup("<"), + new MarkupBlock( + new AttributeBlockChunkGenerator( + name: "class", + prefix: new LocationTagged(" class=\"", 1, 0, 1), + suffix: new LocationTagged("\"", 12, 0, 12)), + factory.Markup(" class=\"").With(SpanChunkGenerator.Null), + factory.Markup("foo").With(new LiteralAttributeChunkGenerator( + prefix: new LocationTagged(string.Empty, 9, 0, 9), + value: new LocationTagged("foo", 9, 0, 9))), + factory.Markup("\"").With(SpanChunkGenerator.Null)), + factory.Markup(" ")), + new MarkupTagHelperBlock("p", selfClosing: true)) + }, + { + "/>

>", + new MarkupBlock( + blockFactory.MarkupTagBlock("")), + factory.Markup(">")) + }, + { + "/>

>", + new MarkupBlock( + blockFactory.MarkupTagBlock(""), + blockFactory.MarkupTagBlock("")), + factory.Markup(">")) + }, + { + "@DateTime.Now/>

>", + new MarkupBlock( + blockFactory.MarkupTagBlock(""), + blockFactory.MarkupTagBlock("")), + factory.Markup(">")) + }, + { + "

@DateTime.Now / >

", + new MarkupBlock( + blockFactory.MarkupTagBlock(""), + new MarkupTagHelperBlock("p", + dateTimeNow, + factory.Markup(" / >"), + blockFactory.MarkupTagBlock("")), + blockFactory.MarkupTagBlock("")) + }, + { + "

< @DateTime.Now >

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new MarkupTagBlock( + factory.Markup("< "), + dateTimeNow, + factory.Markup(" >")), + blockFactory.MarkupTagBlock(""))) + } + }; + } + } + + [Theory] + [MemberData(nameof(InvalidHtmlBlockData))] + public void TagHelperParseTreeRewriter_AllowsInvalidHtml(string documentContent, MarkupBlock expectedOutput) + { + RunParseTreeRewriterTest(documentContent, expectedOutput, "p"); + } + + public static TheoryData EmptyAttributeTagHelperData + { + get + { + var factory = CreateDefaultSpanFactory(); + + // documentContent, expectedOutput + return new TheoryData + { + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", new MarkupBlock()) + })) + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", new MarkupBlock()) + })) + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + // We expected a markup node here because attribute values without quotes can only ever + // be a single item, hence don't need to be enclosed by a block. + new KeyValuePair( + "class", + factory.Markup("").With(SpanChunkGenerator.Null)), + })) + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("class1", new MarkupBlock()), + new KeyValuePair( + "class2", + factory.Markup("").With(SpanChunkGenerator.Null)), + new KeyValuePair("class3", new MarkupBlock()), + })) + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("class1", new MarkupBlock()), + new KeyValuePair("class2", new MarkupBlock()), + new KeyValuePair( + "class3", + factory.Markup("").With(SpanChunkGenerator.Null)), + })) + }, + }; + } + } + + [Theory] + [MemberData(nameof(EmptyAttributeTagHelperData))] + public void Rewrite_UnderstandsEmptyAttributeTagHelpers(string documentContent, MarkupBlock expectedOutput) + { + RunParseTreeRewriterTest(documentContent, expectedOutput, new RazorError[0], "p"); + } + + public static TheoryData EmptyTagHelperBoundAttributeData + { + get + { + var factory = CreateDefaultSpanFactory(); + var emptyAttributeError = + "Attribute '{0}' on tag helper element '{1}' requires a value. Tag helper bound attributes of " + + "type '{2}' cannot be empty or contain only whitespace."; + var boolTypeName = typeof(bool).FullName; + + // documentContent, expectedOutput, expectedErrors + return new TheoryData + { + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("bound", new MarkupBlock()) + })), + new[] + { + new RazorError( + string.Format(emptyAttributeError, "bound", "myth", boolTypeName), + absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("bound", factory.CodeMarkup(" true")) + })), + new RazorError[0] + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("bound", factory.CodeMarkup(" ")) + })), + new[] + { + new RazorError( + string.Format(emptyAttributeError, "bound", "myth", boolTypeName), + absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("bound", new MarkupBlock()), + new KeyValuePair("bound", new MarkupBlock()) + })), + new[] + { + new RazorError( + string.Format(emptyAttributeError, "bound", "myth", boolTypeName), + absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5), + new RazorError( + string.Format(emptyAttributeError, "bound", "myth", boolTypeName), + absoluteIndex: 16, lineIndex: 0, columnIndex: 16, length: 5) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("bound", factory.CodeMarkup(" ")), + new KeyValuePair("bound", factory.CodeMarkup(" ")) + })), + new[] + { + new RazorError( + string.Format(emptyAttributeError, "bound", "myth", boolTypeName), + absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5), + new RazorError( + string.Format(emptyAttributeError, "bound", "myth", boolTypeName), + absoluteIndex: 17, lineIndex: 0, columnIndex: 17, length: 5) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("bound", factory.CodeMarkup("true")), + new KeyValuePair( + "bound", + factory.CodeMarkup(string.Empty).With(SpanChunkGenerator.Null)) + })), + new[] + { + new RazorError( + string.Format(emptyAttributeError, "bound", "myth", boolTypeName), + absoluteIndex: 19, lineIndex: 0, columnIndex: 19, length: 5) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + new KeyValuePair( + "bound", + factory.CodeMarkup(string.Empty).With(SpanChunkGenerator.Null)), + new KeyValuePair("name", new MarkupBlock()) + })), + new[] + { + new RazorError( + string.Format(emptyAttributeError, "bound", "myth", boolTypeName), + absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5), + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + new KeyValuePair( + "bound", + factory.CodeMarkup(string.Empty).With(SpanChunkGenerator.Null)), + new KeyValuePair("name", factory.Markup(" ")) + })), + new[] + { + new RazorError( + string.Format(emptyAttributeError, "bound", "myth", boolTypeName), + absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5), + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("bound", factory.CodeMarkup("true")), + new KeyValuePair("name", factory.Markup("john")), + new KeyValuePair( + "bound", + factory.CodeMarkup(string.Empty).With(SpanChunkGenerator.Null)), + new KeyValuePair( + "name", + factory.Markup(string.Empty).With(SpanChunkGenerator.Null)) + })), + new[] + { + new RazorError( + string.Format(emptyAttributeError, "bound", "myth", boolTypeName), + absoluteIndex: 31, lineIndex: 0, columnIndex: 31, length: 5), + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("BouND", new MarkupBlock()) + })), + new[] + { + new RazorError( + string.Format(emptyAttributeError, "BouND", "myth", boolTypeName), + absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5), + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("BOUND", new MarkupBlock()), + new KeyValuePair("bOUnd", new MarkupBlock()) + })), + new[] + { + new RazorError( + string.Format(emptyAttributeError, "BOUND", "myth", boolTypeName), + absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5), + new RazorError( + string.Format(emptyAttributeError, "bOUnd", "myth", boolTypeName), + absoluteIndex: 18, lineIndex: 0, columnIndex: 18, length: 5) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + new List> + { + new KeyValuePair( + "BOUND", + factory.CodeMarkup(string.Empty).With(SpanChunkGenerator.Null)), + new KeyValuePair("nAMe", factory.Markup("john")) + })), + new[] + { + new RazorError( + string.Format(emptyAttributeError, "BOUND", "myth", boolTypeName), + absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + { + new KeyValuePair( + "bound", + new MarkupBlock( + new MarkupBlock( + factory.Markup(" "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("true") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + factory.Markup(" "))) + } + })), + new RazorError[0] + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "myth", + selfClosing: true, + attributes: new List> + { + { + new KeyValuePair( + "bound", + new MarkupBlock( + new MarkupBlock( + factory.Markup(" "), + new ExpressionBlock( + factory.CodeTransition(), + factory.MetaCode("(").Accepts(AcceptedCharacters.None), + factory.Code("true").AsExpression(), + factory.MetaCode(")").Accepts(AcceptedCharacters.None))), + factory.Markup(" "))) + } + })), + new RazorError[0] + }, + }; + } + } + + [Theory] + [MemberData(nameof(EmptyTagHelperBoundAttributeData))] + public void Rewrite_CreatesErrorForEmptyTagHelperBoundAttributes( + string documentContent, + MarkupBlock expectedOutput, + RazorError[] expectedErrors) + { + // Arrange + var descriptors = new TagHelperDescriptor[] + { + new TagHelperDescriptor( + tagName: "myth", + typeName: "mythTagHelper", + assemblyName: "SomeAssembly", + attributes: new[] + { + new TagHelperAttributeDescriptor( + name: "bound", + propertyName: "Bound", + typeName: typeof(bool).FullName, + isIndexer: false), + new TagHelperAttributeDescriptor( + name: "name", + propertyName: "Name", + typeName: typeof(string).FullName, + isIndexer: false) + }) + }; + var descriptorProvider = new TagHelperDescriptorProvider(descriptors); + + // Act & Assert + EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors); + } + + public static IEnumerable ScriptBlockData + { + get + { + var factory = CreateDefaultSpanFactory(); + + yield return new object[] + { + "", + new MarkupBlock( + new MarkupTagHelperBlock("script", + factory.Markup("", + new MarkupBlock( + new MarkupTagHelperBlock("script", + factory.Markup("Hello World

"))) + }; + yield return new object[] + { + "

World

", + 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[] + { + " World

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + factory.Markup("Hello "), + new MarkupTagHelperBlock("script", + new List> + { + new KeyValuePair("class", factory.Markup("foo")), + new KeyValuePair("style", factory.Markup("color:red;")) + }), + factory.Markup(" World"))) + }; + yield return new object[] + { + "

Hello World

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + factory.Markup("Hello "), + new MarkupTagHelperBlock("script", + new List> + { + new KeyValuePair( + "class", + new MarkupBlock( + new MarkupBlock( + factory.Markup("@").Accepts(AcceptedCharacters.None), + factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharacters.None)), + factory.Markup("foo@bar.com"))), + new KeyValuePair("style", 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", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("class", factory.Markup("foo")), + new KeyValuePair("style", factory.Markup("color:red;")) + })) + }; + yield return new object[] + { + "

Hello

World

", + new MarkupBlock( + new MarkupTagHelperBlock( + "p", + selfClosing: false, + children: new SyntaxTreeNode[] + { + factory.Markup("Hello "), + new MarkupTagHelperBlock( + "p", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("class", factory.Markup("foo")), + new KeyValuePair( + "style", + factory.Markup("color:red;")) + }), + factory.Markup(" World") + })) + }; + yield return new object[] + { + "Hello

World", + new MarkupBlock( + factory.Markup("Hello"), + new MarkupTagHelperBlock("p", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("class", factory.Markup("foo")) + }), + factory.Markup(" "), + new MarkupTagHelperBlock("p", + selfClosing: true, + attributes: new List> + { + new KeyValuePair("style", 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 List> + { + new KeyValuePair("class", factory.Markup("foo")), + new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)), + new KeyValuePair("style", factory.Markup("color:red;")) + })) + }; + yield return new object[] + { + "

Hello World

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", factory.Markup("foo")), + new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)), + new KeyValuePair("style", factory.Markup("color:red;")) + }, + factory.Markup("Hello World"))) + }; + yield return new object[] + { + "

Hello World

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", factory.Markup("foo")), + new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)), + new KeyValuePair( + "style", + new MarkupBlock( + factory.Markup("color"), + new MarkupBlock( + factory.Markup("@").Accepts(AcceptedCharacters.None), + factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharacters.None)), + factory.Markup(":red;"))) + }, + factory.Markup("Hello World"))) + }; + yield return new object[] + { + "

Hello

World

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", factory.Markup("foo")), + new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)) + }, + factory.Markup("Hello")), + factory.Markup(" "), + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("style", factory.Markup("color:red;")), + new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)) + }, + factory.Markup("World"))) + }; + yield return new object[] + { + "

Hello World inside of strong tag

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", factory.Markup("foo")), + new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)), + new KeyValuePair("style", 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(SpanChunkGenerator.Null), + factory.Markup("foo").With(new LiteralAttributeChunkGenerator(prefix: new LocationTagged(string.Empty, 79, 0, 79), + value: new LocationTagged("foo", 79, 0, 79))), + factory.Markup("\"").With(SpanChunkGenerator.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 List> + { + new KeyValuePair("class", factory.Markup("foo")), + new KeyValuePair("style", factory.Markup("color:red;")) + })) + }; + yield return new object[] + { + "

Hello World

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", factory.Markup("foo")), + new KeyValuePair("style", factory.Markup("color:red;")) + }, + factory.Markup("Hello World"))) + }; + yield return new object[] + { + "

Hello

World

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", factory.Markup("foo")) + }, + factory.Markup("Hello")), + factory.Markup(" "), + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("style", factory.Markup("color:red;")) + }, + factory.Markup("World"))) + }; + yield return new object[] + { + "

Hello World inside of strong tag

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("class", factory.Markup("foo")), + new KeyValuePair("style", 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(SpanChunkGenerator.Null), + factory.Markup("foo").With(new LiteralAttributeChunkGenerator(prefix: new LocationTagged(string.Empty, 61, 0, 61), + value: new LocationTagged("foo", 61, 0, 61))), + factory.Markup("\"").With(SpanChunkGenerator.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 TheoryData DataDashAttributeData_Document { get diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs index c37c115f2d..0b1172a37d 100644 --- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs @@ -1209,322 +1209,6 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers expectedErrors: Enumerable.Empty()); } - public static TheoryData EmptyTagHelperBoundAttributeData - { - get - { - var factory = CreateDefaultSpanFactory(); - var emptyAttributeError = - "Attribute '{0}' on tag helper element '{1}' requires a value. Tag helper bound attributes of " + - "type '{2}' cannot be empty or contain only whitespace."; - var boolTypeName = typeof(bool).FullName; - - // documentContent, expectedOutput, expectedErrors - return new TheoryData - { - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("bound", new MarkupBlock()) - })), - new[] - { - new RazorError( - string.Format(emptyAttributeError, "bound", "myth", boolTypeName), - absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5) - } - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("bound", factory.CodeMarkup(" true")) - })), - new RazorError[0] - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("bound", factory.CodeMarkup(" ")) - })), - new[] - { - new RazorError( - string.Format(emptyAttributeError, "bound", "myth", boolTypeName), - absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5) - } - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("bound", new MarkupBlock()), - new KeyValuePair("bound", new MarkupBlock()) - })), - new[] - { - new RazorError( - string.Format(emptyAttributeError, "bound", "myth", boolTypeName), - absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5), - new RazorError( - string.Format(emptyAttributeError, "bound", "myth", boolTypeName), - absoluteIndex: 16, lineIndex: 0, columnIndex: 16, length: 5) - } - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("bound", factory.CodeMarkup(" ")), - new KeyValuePair("bound", factory.CodeMarkup(" ")) - })), - new[] - { - new RazorError( - string.Format(emptyAttributeError, "bound", "myth", boolTypeName), - absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5), - new RazorError( - string.Format(emptyAttributeError, "bound", "myth", boolTypeName), - absoluteIndex: 17, lineIndex: 0, columnIndex: 17, length: 5) - } - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("bound", factory.CodeMarkup("true")), - new KeyValuePair("bound", factory.CodeMarkup(string.Empty).With(SpanChunkGenerator.Null)) - })), - new[] - { - new RazorError( - string.Format(emptyAttributeError, "bound", "myth", boolTypeName), - absoluteIndex: 19, lineIndex: 0, columnIndex: 19, length: 5) - } - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("bound", factory.CodeMarkup(string.Empty).With(SpanChunkGenerator.Null)), - new KeyValuePair("name", new MarkupBlock()) - })), - new[] - { - new RazorError( - string.Format(emptyAttributeError, "bound", "myth", boolTypeName), - absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5), - } - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("bound", factory.CodeMarkup(string.Empty).With(SpanChunkGenerator.Null)), - new KeyValuePair("name", factory.Markup(" ")) - })), - new[] - { - new RazorError( - string.Format(emptyAttributeError, "bound", "myth", boolTypeName), - absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5), - } - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("bound", factory.CodeMarkup("true")), - new KeyValuePair("name", factory.Markup("john")), - new KeyValuePair("bound", factory.CodeMarkup(string.Empty).With(SpanChunkGenerator.Null)), - new KeyValuePair("name", factory.Markup(string.Empty).With(SpanChunkGenerator.Null)) - })), - new[] - { - new RazorError( - string.Format(emptyAttributeError, "bound", "myth", boolTypeName), - absoluteIndex: 31, lineIndex: 0, columnIndex: 31, length: 5), - } - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("BouND", new MarkupBlock()) - })), - new[] - { - new RazorError( - string.Format(emptyAttributeError, "BouND", "myth", boolTypeName), - absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5), - } - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("BOUND", new MarkupBlock()), - new KeyValuePair("bOUnd", new MarkupBlock()) - })), - new[] - { - new RazorError( - string.Format(emptyAttributeError, "BOUND", "myth", boolTypeName), - absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5), - new RazorError( - string.Format(emptyAttributeError, "bOUnd", "myth", boolTypeName), - absoluteIndex: 18, lineIndex: 0, columnIndex: 18, length: 5) - } - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - new List> - { - new KeyValuePair("BOUND", factory.CodeMarkup(string.Empty).With(SpanChunkGenerator.Null)), - new KeyValuePair("nAMe", factory.Markup("john")) - })), - new[] - { - new RazorError( - string.Format(emptyAttributeError, "BOUND", "myth", boolTypeName), - absoluteIndex: 6, lineIndex: 0, columnIndex: 6, length: 5) - } - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - { - new KeyValuePair( - "bound", - new MarkupBlock( - new MarkupBlock( - factory.Markup(" "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("true") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharacters.NonWhiteSpace))), - factory.Markup(" "))) - } - })), - new RazorError[0] - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock( - "myth", - selfClosing: true, - attributes: new List> - { - { - new KeyValuePair( - "bound", - new MarkupBlock( - new MarkupBlock( - factory.Markup(" "), - new ExpressionBlock( - factory.CodeTransition(), - factory.MetaCode("(").Accepts(AcceptedCharacters.None), - factory.Code("true").AsExpression(), - factory.MetaCode(")").Accepts(AcceptedCharacters.None))), - factory.Markup(" "))) - } - })), - new RazorError[0] - }, - }; - } - } - - [Theory] - [MemberData(nameof(EmptyTagHelperBoundAttributeData))] - public void Rewrite_CreatesErrorForEmptyTagHelperBoundAttributes( - string documentContent, - MarkupBlock expectedOutput, - RazorError[] expectedErrors) - { - // Arrange - var descriptors = new TagHelperDescriptor[] - { - new TagHelperDescriptor( - tagName: "myth", - typeName: "mythTagHelper", - assemblyName: "SomeAssembly", - attributes: new[] - { - new TagHelperAttributeDescriptor( - name: "bound", - propertyName: "Bound", - typeName: typeof(bool).FullName, - isIndexer: false), - new TagHelperAttributeDescriptor( - name: "name", - propertyName: "Name", - typeName: typeof(string).FullName, - isIndexer: false) - }) - }; - var descriptorProvider = new TagHelperDescriptorProvider(descriptors); - - // Act & Assert - EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors); - } - public static TheoryData OptOut_WithAttributeTextTagData { get @@ -3204,1023 +2888,6 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers RunParseTreeRewriterTest(documentContent, expectedOutput, expectedErrors, "strong", "p"); } - public static TheoryData EmptyAttributeTagHelperData - { - get - { - var factory = CreateDefaultSpanFactory(); - - // documentContent, expectedOutput - return new TheoryData - { - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", new MarkupBlock()) - })) - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", new MarkupBlock()) - })) - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - // We expected a markup node here because attribute values without quotes can only ever - // be a single item, hence don't need to be enclosed by a block. - new KeyValuePair( - "class", - factory.Markup("").With(SpanChunkGenerator.Null)), - })) - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("class1", new MarkupBlock()), - new KeyValuePair( - "class2", - factory.Markup("").With(SpanChunkGenerator.Null)), - new KeyValuePair("class3", new MarkupBlock()), - })) - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("class1", new MarkupBlock()), - new KeyValuePair("class2", new MarkupBlock()), - new KeyValuePair( - "class3", - factory.Markup("").With(SpanChunkGenerator.Null)), - })) - }, - }; - } - } - - [Theory] - [MemberData(nameof(EmptyAttributeTagHelperData))] - public void Rewrite_UnderstandsEmptyAttributeTagHelpers(string documentContent, MarkupBlock expectedOutput) - { - RunParseTreeRewriterTest(documentContent, expectedOutput, new RazorError[0], "p"); - } - - public static TheoryData MalformedTagHelperAttributeBlockData - { - get - { - var factory = CreateDefaultSpanFactory(); - var blockFactory = new BlockFactory(factory); - var errorFormatUnclosed = "Found a malformed '{0}' tag helper. Tag helpers must have a start and " + - "end tag or be self closing."; - var errorFormatNoCloseAngle = "Missing close angle for tag helper '{0}'."; - var errorFormatNoCSharp = "The tag helper '{0}' must not have C# in the element's attribute " + - "declaration area."; - Func createInvalidDoBlock = extraCode => - { - return new MarkupBlock( - new MarkupBlock( - new StatementBlock( - factory.CodeTransition(), - factory.Code("do {" + extraCode).AsStatement()))); - }; - var dateTimeNow = new MarkupBlock( - new MarkupBlock( - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime.Now") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharacters.NonWhiteSpace)))); - - return new TheoryData - { - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List>())), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero) - } - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("bar", factory.Markup("false")) - })), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero) - } - }, - { - "

> - { - new KeyValuePair( - "bar", - new MarkupBlock( - factory.Markup("false"), - factory.Markup(" > - { - new KeyValuePair( - "bar", - factory.Markup("false")) - })), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero), - new RazorError( - "TagHelper attributes must be well-formed.", - absoluteIndex: 12, - lineIndex: 0, - columnIndex: 12) - } - }, - { - "

> - { - new KeyValuePair( - "bar", - factory.Markup("false'")) - })), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero) - } - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair( - "bar", - new MarkupBlock( - factory.Markup("false'"), - factory.Markup(" >

"))) - })), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero) - } - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("foo", null), - new KeyValuePair("bar", null) - }, - new MarkupTagHelperBlock("strong"))), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), - absoluteIndex: 10, lineIndex: 0, columnIndex: 10) - } - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", factory.Markup("btn")) - })), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero) - } - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", factory.Markup("btn")) - })), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero) - } - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair( - "class", - new MarkupBlock(factory.Markup("btn"), factory.Markup(" bar="))), - new KeyValuePair("foo", null) - }, - new MarkupTagHelperBlock("strong"))), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), - absoluteIndex: 23, lineIndex: 0, columnIndex: 23) - } - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", new MarkupBlock(factory.Markup("btn"), factory.Markup(" bar="))), - new KeyValuePair("foo", null), - })), - new RazorError[0] - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p")), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatNoCSharp, "p"), - absoluteIndex: 3, lineIndex: 0 , columnIndex: 3) - } - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p")), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatNoCSharp, "p"), - absoluteIndex: 3, lineIndex: 0 , columnIndex: 3) - } - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", dateTimeNow) - })), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero) - } - }, - { - "

> - { - new KeyValuePair("class", createInvalidDoBlock(string.Empty)) - })), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero), - new RazorError( - RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF("do", "}", "{"), - absoluteIndex: 11, lineIndex: 0, columnIndex: 11) - } - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", createInvalidDoBlock("\">

")) - })), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero), - new RazorError( - RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF("do", "}", "{"), - absoluteIndex: 11, lineIndex: 0, columnIndex: 11), - new RazorError( - RazorResources.ParseError_Unterminated_String_Literal, - absoluteIndex: 15, lineIndex: 0, columnIndex: 15) - } - }, - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p")), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "p"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatNoCSharp, "p"), - absoluteIndex: 3, lineIndex: 0 , columnIndex: 3), - new RazorError( - RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF("do", "}", "{"), - absoluteIndex: 4, lineIndex: 0, columnIndex: 4), - new RazorError( - RazorResources.FormatParseError_UnexpectedEndTag("p"), - absoluteIndex: 29, lineIndex: 0, columnIndex: 29) - } - } - }; - } - } - - [Theory] - [MemberData(nameof(MalformedTagHelperAttributeBlockData))] - public void Rewrite_CreatesErrorForMalformedTagHelpersWithAttributes( - string documentContent, - MarkupBlock expectedOutput, - RazorError[] expectedErrors) - { - RunParseTreeRewriterTest(documentContent, expectedOutput, expectedErrors, "strong", "p"); - } - - public static TheoryData MalformedTagHelperBlockData - { - get - { - var factory = CreateDefaultSpanFactory(); - var blockFactory = new BlockFactory(factory); - var errorFormatUnclosed = "Found a malformed '{0}' tag helper. Tag helpers must have a start and " + - "end tag or be self closing."; - var errorFormatNoCloseAngle = "Missing close angle for tag helper '{0}'."; - - return new TheoryData - { - { - "", - new MarkupBlock( - new MarkupTagHelperBlock("strong", - new MarkupTagHelperBlock("p"))), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "strong"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), - SourceLocation.Zero), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - absoluteIndex: 8, lineIndex: 0, columnIndex: 8) - } - }, - { - " <

", - new MarkupBlock( - blockFactory.MarkupTagBlock("<"), - blockFactory.MarkupTagBlock("<"), - blockFactory.MarkupTagBlock(""), - factory.Markup(" "), - blockFactory.MarkupTagBlock("<"), - new MarkupTagHelperBlock("p")), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), - absoluteIndex: 2, lineIndex: 0, columnIndex: 2), - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - absoluteIndex: 13, lineIndex: 0, columnIndex: 13) - } - }, - { - "<<> <<>>", - new MarkupBlock( - blockFactory.MarkupTagBlock("<"), - blockFactory.MarkupTagBlock("<"), - new MarkupTagHelperBlock("strong", - factory.Markup("> "), - blockFactory.MarkupTagBlock("<"), - blockFactory.MarkupTagBlock("<>"), - factory.Markup(">"))), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), - absoluteIndex: 2, lineIndex: 0, columnIndex: 2) - } - }, - { - "

", - new MarkupBlock( - blockFactory.MarkupTagBlock(""))), - new [] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), - absoluteIndex: 12, lineIndex: 0, columnIndex: 12) - } - } - }; - } - } - - [Theory] - [MemberData(nameof(MalformedTagHelperBlockData))] - public void Rewrite_CreatesErrorForMalformedTagHelper( - string documentContent, - MarkupBlock expectedOutput, - RazorError[] expectedErrors) - { - RunParseTreeRewriterTest(documentContent, expectedOutput, expectedErrors, "strong", "p"); - } - - public static TheoryData CodeTagHelperAttributesData - { - get - { - var factory = CreateDefaultSpanFactory(); - var dateTimeNow = new MarkupBlock( - factory.Markup(" "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime.Now") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharacters.NonWhiteSpace))); - - return new TheoryData - { - { - "", - new MarkupBlock( - new MarkupTagHelperBlock("person", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("age", factory.CodeMarkup("12")) - })) - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock("person", - selfClosing: true, - attributes: new List> - { - new KeyValuePair( - "birthday", - factory.CodeMarkup("DateTime.Now")) - })) - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock("person", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("name", factory.Markup("John")) - })) - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock("person", - selfClosing: true, - attributes: new List> - { - new KeyValuePair( - "name", - new MarkupBlock(factory.Markup("Time:"), dateTimeNow)) - })) - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock("person", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("age", factory.CodeMarkup("12")), - new KeyValuePair( - "birthday", - factory.CodeMarkup("DateTime.Now")), - new KeyValuePair( - "name", - new MarkupBlock(factory.Markup("Time:"), dateTimeNow)) - })) - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock("person", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("age", factory.CodeMarkup("12")), - new KeyValuePair( - "birthday", - factory.CodeMarkup("DateTime.Now")), - new KeyValuePair( - "name", - new MarkupBlock( - factory.Markup("Time:"), - new MarkupBlock( - factory.Markup(" @").Accepts(AcceptedCharacters.None), - factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharacters.None)), - dateTimeNow)) - })) - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock("person", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("age", factory.CodeMarkup("12")), - new KeyValuePair( - "birthday", - factory.CodeMarkup("DateTime.Now")), - new KeyValuePair( - "name", - new MarkupBlock( - new MarkupBlock( - factory.Markup("@").Accepts(AcceptedCharacters.None), - factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharacters.None)), - factory.Markup("BoundStringAttribute"))) - })) - }, - { - "", - new MarkupBlock( - new MarkupTagHelperBlock("person", - selfClosing: true, - attributes: new List> - { - new KeyValuePair( - "age", - new MarkupBlock( - new MarkupBlock( - factory.Markup("@").Accepts(AcceptedCharacters.None), - factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharacters.None)), - new MarkupBlock( - factory.EmptyHtml(), - new ExpressionBlock( - factory.CodeTransition(), - factory.MetaCode("(").Accepts(AcceptedCharacters.None), - factory.Code("11+1").AsExpression(), - factory.MetaCode(")").Accepts(AcceptedCharacters.None))))), - new KeyValuePair( - "birthday", - factory.CodeMarkup("DateTime.Now")), - new KeyValuePair( - "name", - new MarkupBlock(factory.Markup("Time:"), dateTimeNow)) - })) - }, - }; - } - } - - [Theory] - [MemberData(nameof(CodeTagHelperAttributesData))] - public void TagHelperParseTreeRewriter_CreatesMarkupCodeSpansForNonStringTagHelperAttributes( - string documentContent, - MarkupBlock expectedOutput) - { - // Arrange - var descriptors = new TagHelperDescriptor[] - { - new TagHelperDescriptor("person", "PersonTagHelper", "personAssembly", - attributes: new[] - { - new TagHelperAttributeDescriptor("age", "Age", typeof(int).FullName, isIndexer: false), - new TagHelperAttributeDescriptor( - "birthday", - "BirthDay", - typeof(DateTime).FullName, - isIndexer: false), - new TagHelperAttributeDescriptor("name", "Name", typeof(string).FullName, isIndexer: false), - }) - }; - var providerContext = new TagHelperDescriptorProvider(descriptors); - - // Act & Assert - EvaluateData(providerContext, - documentContent, - expectedOutput, - expectedErrors: Enumerable.Empty()); - } - - public static IEnumerable IncompleteHelperBlockData - { - get - { - var factory = CreateDefaultSpanFactory(); - var blockFactory = new BlockFactory(factory); - var malformedErrorFormat = "Found a malformed '{0}' tag helper. Tag helpers must have a start and " + - "end tag or be self closing."; - 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 List> - { - new KeyValuePair("class", factory.Markup("foo")), - new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)), - new KeyValuePair("style", factory.Markup("color:red;")) - }, - new MarkupTagHelperBlock("strong")), - blockFactory.MarkupTagBlock("")), - new RazorError[] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "strong"), - absoluteIndex: 52, lineIndex: 0, columnIndex: 52), - new RazorError( - string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "strong"), - absoluteIndex: 64, lineIndex: 0, columnIndex: 64) - } - }; - yield return new object[] - { - "

Hello World

", - new MarkupBlock( - blockFactory.MarkupTagBlock("
"), - new MarkupTagHelperBlock("p", - factory.Markup("Hello "), - new MarkupTagHelperBlock("strong", - factory.Markup("World")), - blockFactory.MarkupTagBlock("
"))), - new RazorError[] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "p"), - absoluteIndex: 5, lineIndex: 0, columnIndex: 5) - } - }; - yield return new object[] - { - "

Hello World

", - new MarkupBlock( - blockFactory.MarkupTagBlock("
"), - new MarkupTagHelperBlock("p", - factory.Markup("Hello "), - new MarkupTagHelperBlock("strong", - factory.Markup("World"), - blockFactory.MarkupTagBlock("
")))), - new RazorError[] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "p"), - absoluteIndex: 5, lineIndex: 0, columnIndex: 5), - new RazorError( - string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "strong"), - absoluteIndex: 14, lineIndex: 0, columnIndex: 14) - } - }; - yield return new object[] - { - "

Hello

World

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", factory.Markup("foo")) - }, - factory.Markup("Hello "), - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("style", factory.Markup("color:red;")) - }, - factory.Markup("World")))), - new RazorError[] - { - new RazorError( - string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "p"), - SourceLocation.Zero) - } - }; - } - } - - [Theory] - [MemberData(nameof(IncompleteHelperBlockData))] - public void TagHelperParseTreeRewriter_CreatesErrorForIncompleteTagHelper( - string documentContent, - MarkupBlock expectedOutput, - RazorError[] expectedErrors) - { - RunParseTreeRewriterTest(documentContent, expectedOutput, expectedErrors, "strong", "p"); - } - - public static TheoryData InvalidHtmlBlockData - { - get - { - var factory = CreateDefaultSpanFactory(); - var blockFactory = new BlockFactory(factory); - var dateTimeNow = new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime.Now") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharacters.NonWhiteSpace)); - - return new TheoryData - { - { - "<<

>>

", - new MarkupBlock( - blockFactory.MarkupTagBlock("<"), - blockFactory.MarkupTagBlock("<"), - new MarkupTagHelperBlock("p", - factory.Markup(">>"))) - }, - { - "<

", - new MarkupBlock( - blockFactory.MarkupTagBlock("<"), - new MarkupTagHelperBlock("p", selfClosing: true)) - }, - { - "< p />", - new MarkupBlock( - new MarkupTagBlock( - factory.Markup("<"), - new MarkupBlock( - factory.Markup(" p")), - factory.Markup(" />"))) - }, - { - "", - new MarkupBlock( - blockFactory.MarkupTagBlock("", - new MarkupBlock( - new MarkupTagBlock( - factory.Markup("<"), - new MarkupBlock( - new AttributeBlockChunkGenerator( - name: "class", - prefix: new LocationTagged(" class=\"", 1, 0, 1), - suffix: new LocationTagged("\"", 12, 0, 12)), - factory.Markup(" class=\"").With(SpanChunkGenerator.Null), - factory.Markup("foo").With(new LiteralAttributeChunkGenerator( - prefix: new LocationTagged(string.Empty, 9, 0, 9), - value: new LocationTagged("foo", 9, 0, 9))), - factory.Markup("\"").With(SpanChunkGenerator.Null)), - factory.Markup(" ")), - new MarkupTagHelperBlock("p", selfClosing: true)) - }, - { - "/>

>", - new MarkupBlock( - blockFactory.MarkupTagBlock("")), - factory.Markup(">")) - }, - { - "/>

>", - new MarkupBlock( - blockFactory.MarkupTagBlock(""), - blockFactory.MarkupTagBlock("")), - factory.Markup(">")) - }, - { - "@DateTime.Now/>

>", - new MarkupBlock( - blockFactory.MarkupTagBlock(""), - blockFactory.MarkupTagBlock("")), - factory.Markup(">")) - }, - { - "

@DateTime.Now / >

", - new MarkupBlock( - blockFactory.MarkupTagBlock(""), - new MarkupTagHelperBlock("p", - dateTimeNow, - factory.Markup(" / >"), - blockFactory.MarkupTagBlock("")), - blockFactory.MarkupTagBlock("")) - }, - { - "

< @DateTime.Now >

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new MarkupTagBlock( - factory.Markup("< "), - dateTimeNow, - factory.Markup(" >")), - blockFactory.MarkupTagBlock(""))) - } - }; - } - } - - [Theory] - [MemberData(nameof(InvalidHtmlBlockData))] - public void TagHelperParseTreeRewriter_AllowsInvalidHtml(string documentContent, MarkupBlock expectedOutput) - { - RunParseTreeRewriterTest(documentContent, expectedOutput, "p"); - } - public static IEnumerable TextTagsBlockData { get @@ -4422,775 +3089,6 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers RunParseTreeRewriterTest(documentContent, expectedOutput, "!--", "?xml", "![CDATA[", "!DOCTYPE"); } - public static IEnumerable OddlySpacedBlockData - { - get - { - var factory = CreateDefaultSpanFactory(); - - yield return new object[] - { - "

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", factory.Markup(" foo")), - new KeyValuePair( - "style", - new MarkupBlock( - factory.Markup(" color"), - factory.Markup(" :"), - factory.Markup(" red"), - factory.Markup(" ;"), - factory.Markup(" "))) - })) - }; - yield return new object[] - { - "

Hello World

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", factory.Markup(" foo")), - new KeyValuePair( - "style", - new MarkupBlock( - factory.Markup(" color"), - factory.Markup(" :"), - factory.Markup(" red"), - factory.Markup(" ;"), - factory.Markup(" "))) - }, - factory.Markup("Hello World"))) - }; - yield return new object[] - { - "

Hello

World

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair( - "class", - new MarkupBlock(factory.Markup(" foo"), factory.Markup(" "))) - }, - factory.Markup("Hello")), - factory.Markup(" "), - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair( - "style", - new MarkupBlock(factory.Markup(" color:red;"), factory.Markup(" "))) - }, - factory.Markup("World"))) - }; - } - } - - [Theory] - [MemberData(nameof(OddlySpacedBlockData))] - public void TagHelperParseTreeRewriter_RewritesOddlySpacedTagHelperTagBlocks( - string documentContent, - MarkupBlock expectedOutput) - { - RunParseTreeRewriterTest(documentContent, expectedOutput, "p"); - } - - public static IEnumerable ComplexAttributeTagHelperBlockData - { - get - { - var factory = CreateDefaultSpanFactory(); - var blockFactory = new BlockFactory(factory); - var dateTimeNowString = "@DateTime.Now"; - var dateTimeNow = new MarkupBlock( - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime.Now") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharacters.NonWhiteSpace))); - var doWhileString = "@do { var foo = bar; Foo foo++; } while (foo);"; - var doWhile = new MarkupBlock( - new StatementBlock( - factory.CodeTransition(), - factory.Code("do { var foo = bar;").AsStatement(), - new MarkupBlock( - new MarkupTagBlock( - factory.MarkupTransition("")), - factory.Markup("Foo").Accepts(AcceptedCharacters.None), - new MarkupTagBlock( - factory.MarkupTransition("")), - factory.CodeMarkup(" ").With(new StatementChunkGenerator()).Accepts(AcceptedCharacters.None)), - factory.Code("foo++; } while (foo);").AsStatement().Accepts(AcceptedCharacters.None))); - - var currentFormattedString = "

"; - yield return new object[] - { - string.Format(currentFormattedString, dateTimeNowString), - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", new MarkupBlock(dateTimeNow)), - new KeyValuePair("style", new MarkupBlock(dateTimeNow)) - })) - }; - yield return new object[] - { - string.Format(currentFormattedString, doWhileString), - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", new MarkupBlock(doWhile)), - new KeyValuePair("style", new MarkupBlock(doWhile)) - })) - }; - - currentFormattedString = "

Hello World

"; - yield return new object[] - { - string.Format(currentFormattedString, dateTimeNowString), - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", new MarkupBlock(dateTimeNow)), - new KeyValuePair("style", new MarkupBlock(dateTimeNow)) - }, - factory.Markup("Hello World"))) - }; - yield return new object[] - { - string.Format(currentFormattedString, doWhileString), - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", new MarkupBlock(doWhile)), - new KeyValuePair("style", new MarkupBlock(doWhile)) - }, - factory.Markup("Hello World"))) - }; - - currentFormattedString = "

Hello

World

"; - yield return new object[] - { - string.Format(currentFormattedString, dateTimeNowString), - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", new MarkupBlock(dateTimeNow)) - }, - factory.Markup("Hello")), - factory.Markup(" "), - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("style", new MarkupBlock(dateTimeNow)) - }, - factory.Markup("World"))) - }; - yield return new object[] - { - string.Format(currentFormattedString, doWhileString), - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", new MarkupBlock(doWhile)) - }, - factory.Markup("Hello")), - factory.Markup(" "), - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("style", new MarkupBlock(doWhile)) - }, - factory.Markup("World"))) - }; - - currentFormattedString = - "

Hello World inside of strong tag

"; - yield return new object[] - { - string.Format(currentFormattedString, dateTimeNowString), - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", new MarkupBlock(dateTimeNow)), - new KeyValuePair("style", new MarkupBlock(dateTimeNow)) - }, - factory.Markup("Hello World "), - new MarkupTagBlock( - factory.Markup("(" class=\"", 66, 0, 66), - suffix: new LocationTagged("\"", 87, 0, 87)), - factory.Markup(" class=\"").With(SpanChunkGenerator.Null), - new MarkupBlock(new DynamicAttributeBlockChunkGenerator(new LocationTagged(string.Empty, 74, 0, 74), 74, 0, 74), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime.Now") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharacters.NonWhiteSpace))), - factory.Markup("\"").With(SpanChunkGenerator.Null)), - factory.Markup(">")), - factory.Markup("inside of strong tag"), - blockFactory.MarkupTagBlock("
"))) - }; - } - } - - [Theory] - [MemberData(nameof(ComplexAttributeTagHelperBlockData))] - public void TagHelperParseTreeRewriter_RewritesComplexAttributeTagHelperTagBlocks( - string documentContent, - MarkupBlock expectedOutput) - { - RunParseTreeRewriterTest(documentContent, expectedOutput, "p"); - } - - public static IEnumerable ComplexTagHelperBlockData - { - get - { - var factory = CreateDefaultSpanFactory(); - var blockFactory = new BlockFactory(factory); - var dateTimeNowString = "@DateTime.Now"; - var dateTimeNow = new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime.Now") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharacters.NonWhiteSpace)); - var doWhileString = "@do { var foo = bar;

Foo

foo++; } while (foo);"; - var doWhile = new StatementBlock( - factory.CodeTransition(), - factory.Code("do { var foo = bar;").AsStatement(), - new MarkupBlock( - factory.Markup(" "), - new MarkupTagHelperBlock("p", - factory.Markup("Foo")), - factory.Markup(" ").Accepts(AcceptedCharacters.None)), - factory.Code("foo++; } while (foo);").AsStatement().Accepts(AcceptedCharacters.None)); - - var currentFormattedString = "

{0}

"; - yield return new object[] - { - string.Format(currentFormattedString, dateTimeNowString), - new MarkupBlock( - new MarkupTagHelperBlock("p", dateTimeNow)) - }; - yield return new object[] - { - string.Format(currentFormattedString, doWhileString), - new MarkupBlock( - new MarkupTagHelperBlock("p", doWhile)) - }; - - currentFormattedString = "

Hello World {0}

"; - yield return new object[] - { - string.Format(currentFormattedString, dateTimeNowString), - new MarkupBlock( - new MarkupTagHelperBlock("p", - factory.Markup("Hello World "), - dateTimeNow)) - }; - yield return new object[] - { - string.Format(currentFormattedString, doWhileString), - new MarkupBlock( - new MarkupTagHelperBlock("p", - factory.Markup("Hello World "), - doWhile)) - }; - - currentFormattedString = "

{0}

{0}

"; - yield return new object[] - { - string.Format(currentFormattedString, dateTimeNowString), - new MarkupBlock( - new MarkupTagHelperBlock("p", dateTimeNow), - factory.Markup(" "), - new MarkupTagHelperBlock("p", dateTimeNow)) - }; - yield return new object[] - { - string.Format(currentFormattedString, doWhileString), - new MarkupBlock( - new MarkupTagHelperBlock("p", doWhile), - factory.Markup(" "), - new MarkupTagHelperBlock("p", doWhile)) - }; - - currentFormattedString = "

Hello {0}inside of {0} strong tag

"; - yield return new object[] - { - string.Format(currentFormattedString, dateTimeNowString), - new MarkupBlock( - new MarkupTagHelperBlock("p", - factory.Markup("Hello "), - dateTimeNow, - blockFactory.MarkupTagBlock(""), - factory.Markup("inside of "), - dateTimeNow, - factory.Markup(" strong tag"), - blockFactory.MarkupTagBlock(""))) - }; - yield return new object[] - { - string.Format(currentFormattedString, doWhileString), - new MarkupBlock( - new MarkupTagHelperBlock("p", - factory.Markup("Hello "), - doWhile, - blockFactory.MarkupTagBlock(""), - factory.Markup("inside of "), - doWhile, - factory.Markup(" strong tag"), - blockFactory.MarkupTagBlock(""))) - }; - } - } - - [Theory] - [MemberData(nameof(ComplexTagHelperBlockData))] - public void TagHelperParseTreeRewriter_RewritesComplexTagHelperTagBlocks( - string documentContent, - MarkupBlock expectedOutput) - { - RunParseTreeRewriterTest(documentContent, expectedOutput, "p"); - } - - public static IEnumerable ScriptBlockData - { - get - { - var factory = CreateDefaultSpanFactory(); - - yield return new object[] - { - "", - new MarkupBlock( - new MarkupTagHelperBlock("script", - factory.Markup("", - new MarkupBlock( - new MarkupTagHelperBlock("script", - factory.Markup("Hello World
"))) - }; - yield return new object[] - { - "

World

", - 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[] - { - " World

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - factory.Markup("Hello "), - new MarkupTagHelperBlock("script", - new List> - { - new KeyValuePair("class", factory.Markup("foo")), - new KeyValuePair("style", factory.Markup("color:red;")) - }), - factory.Markup(" World"))) - }; - yield return new object[] - { - "

Hello World

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - factory.Markup("Hello "), - new MarkupTagHelperBlock("script", - new List> - { - new KeyValuePair( - "class", - new MarkupBlock( - new MarkupBlock( - factory.Markup("@").Accepts(AcceptedCharacters.None), - factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharacters.None)), - factory.Markup("foo@bar.com"))), - new KeyValuePair("style", 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", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("class", factory.Markup("foo")), - new KeyValuePair("style", factory.Markup("color:red;")) - })) - }; - yield return new object[] - { - "

Hello

World

", - new MarkupBlock( - new MarkupTagHelperBlock( - "p", - selfClosing: false, - children: new SyntaxTreeNode[] - { - factory.Markup("Hello "), - new MarkupTagHelperBlock( - "p", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("class", factory.Markup("foo")), - new KeyValuePair( - "style", - factory.Markup("color:red;")) - }), - factory.Markup(" World") - })) - }; - yield return new object[] - { - "Hello

World", - new MarkupBlock( - factory.Markup("Hello"), - new MarkupTagHelperBlock("p", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("class", factory.Markup("foo")) - }), - factory.Markup(" "), - new MarkupTagHelperBlock("p", - selfClosing: true, - attributes: new List> - { - new KeyValuePair("style", 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 List> - { - new KeyValuePair("class", factory.Markup("foo")), - new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)), - new KeyValuePair("style", factory.Markup("color:red;")) - })) - }; - yield return new object[] - { - "

Hello World

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", factory.Markup("foo")), - new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)), - new KeyValuePair("style", factory.Markup("color:red;")) - }, - factory.Markup("Hello World"))) - }; - yield return new object[] - { - "

Hello World

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", factory.Markup("foo")), - new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)), - new KeyValuePair( - "style", - new MarkupBlock( - factory.Markup("color"), - new MarkupBlock( - factory.Markup("@").Accepts(AcceptedCharacters.None), - factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharacters.None)), - factory.Markup(":red;"))) - }, - factory.Markup("Hello World"))) - }; - yield return new object[] - { - "

Hello

World

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", factory.Markup("foo")), - new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)) - }, - factory.Markup("Hello")), - factory.Markup(" "), - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("style", factory.Markup("color:red;")), - new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)) - }, - factory.Markup("World"))) - }; - yield return new object[] - { - "

Hello World inside of strong tag

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", factory.Markup("foo")), - new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)), - new KeyValuePair("style", 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(SpanChunkGenerator.Null), - factory.Markup("foo").With(new LiteralAttributeChunkGenerator(prefix: new LocationTagged(string.Empty, 79, 0, 79), - value: new LocationTagged("foo", 79, 0, 79))), - factory.Markup("\"").With(SpanChunkGenerator.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 List> - { - new KeyValuePair("class", factory.Markup("foo")), - new KeyValuePair("style", factory.Markup("color:red;")) - })) - }; - yield return new object[] - { - "

Hello World

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", factory.Markup("foo")), - new KeyValuePair("style", factory.Markup("color:red;")) - }, - factory.Markup("Hello World"))) - }; - yield return new object[] - { - "

Hello

World

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", factory.Markup("foo")) - }, - factory.Markup("Hello")), - factory.Markup(" "), - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("style", factory.Markup("color:red;")) - }, - factory.Markup("World"))) - }; - yield return new object[] - { - "

Hello World inside of strong tag

", - new MarkupBlock( - new MarkupTagHelperBlock("p", - new List> - { - new KeyValuePair("class", factory.Markup("foo")), - new KeyValuePair("style", 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(SpanChunkGenerator.Null), - factory.Markup("foo").With(new LiteralAttributeChunkGenerator(prefix: new LocationTagged(string.Empty, 61, 0, 61), - value: new LocationTagged("foo", 61, 0, 61))), - factory.Markup("\"").With(SpanChunkGenerator.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