diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CaseSensitiveTagHelperAttributeComparer.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CaseSensitiveTagHelperAttributeComparer.cs index a5ba8e300d..0bb90234b8 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CaseSensitiveTagHelperAttributeComparer.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CaseSensitiveTagHelperAttributeComparer.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { @@ -26,16 +25,13 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Normal comparer (TagHelperAttribute.Equals()) doesn't care about the Name case, in tests we do. return attributeX != null && string.Equals(attributeX.Name, attributeY.Name, StringComparison.Ordinal) && - Equals(attributeX.Value, attributeY.Value); + attributeX.Minimized == attributeY.Minimized && + (attributeX.Minimized || Equals(attributeX.Value, attributeY.Value)); } public int GetHashCode(IReadOnlyTagHelperAttribute attribute) { - return HashCodeCombiner - .Start() - .Add(attribute.Name, StringComparer.Ordinal) - .Add(attribute.Value) - .CombinedHash; + return attribute.GetHashCode(); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperExecutionContextTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperExecutionContextTest.cs index 865c4c894c..2d5e012c56 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperExecutionContextTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperExecutionContextTest.cs @@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Arrange & Act var executionContext = new TagHelperExecutionContext("p", selfClosing); - // Assert + // Assert Assert.Equal(selfClosing, executionContext.SelfClosing); } @@ -202,6 +202,54 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers CaseSensitiveTagHelperAttributeComparer.Default); } + [Fact] + public void AddMinimizedHtmlAttribute_MaintainsHTMLAttributes() + { + // Arrange + var executionContext = new TagHelperExecutionContext("input", selfClosing: true); + var expectedAttributes = new TagHelperAttributeList + { + ["checked"] = new TagHelperAttribute { Name = "checked", Minimized = true }, + ["visible"] = new TagHelperAttribute { Name = "visible", Minimized = true } + }; + + // Act + executionContext.AddMinimizedHtmlAttribute("checked"); + executionContext.AddMinimizedHtmlAttribute("visible"); + + // Assert + Assert.Equal( + expectedAttributes, + executionContext.HTMLAttributes, + CaseSensitiveTagHelperAttributeComparer.Default); + } + + [Fact] + public void AddMinimizedHtmlAttribute_MaintainsHTMLAttributes_SomeMinimized() + { + // Arrange + var executionContext = new TagHelperExecutionContext("input", selfClosing: true); + var expectedAttributes = new TagHelperAttributeList + { + { "class", "btn" }, + { "foo", "bar" } + }; + expectedAttributes.Add(new TagHelperAttribute { Name = "checked", Minimized = true }); + expectedAttributes.Add(new TagHelperAttribute { Name = "visible", Minimized = true }); + + // Act + executionContext.AddHtmlAttribute("class", "btn"); + executionContext.AddHtmlAttribute("foo", "bar"); + executionContext.AddMinimizedHtmlAttribute("checked"); + executionContext.AddMinimizedHtmlAttribute("visible"); + + // Assert + Assert.Equal( + expectedAttributes, + executionContext.HTMLAttributes, + CaseSensitiveTagHelperAttributeComparer.Default); + } + [Fact] public void TagHelperExecutionContext_MaintainsAllAttributes() { diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/BlockFactory.cs b/test/Microsoft.AspNet.Razor.Test/Framework/BlockFactory.cs index 27dfa2b7f7..ff56d4e2d0 100644 --- a/test/Microsoft.AspNet.Razor.Test/Framework/BlockFactory.cs +++ b/test/Microsoft.AspNet.Razor.Test/Framework/BlockFactory.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Generic; using Microsoft.AspNet.Razor.Parser.SyntaxTree; namespace Microsoft.AspNet.Razor.Test.Framework @@ -19,12 +20,28 @@ namespace Microsoft.AspNet.Razor.Test.Framework return EscapedMarkupTagBlock(prefix, suffix, AcceptedCharacters.Any); } - public Block EscapedMarkupTagBlock(string prefix, string suffix, AcceptedCharacters acceptedCharacters) + public Block EscapedMarkupTagBlock(string prefix, string suffix, params SyntaxTreeNode[] children) { - return new MarkupTagBlock( - _factory.Markup(prefix), - _factory.BangEscape(), - _factory.Markup(suffix).Accepts(acceptedCharacters)); + return EscapedMarkupTagBlock(prefix, suffix, AcceptedCharacters.Any, children); + } + + public Block EscapedMarkupTagBlock( + string prefix, + string suffix, + AcceptedCharacters acceptedCharacters, + params SyntaxTreeNode[] children) + { + var newChildren = new List( + new SyntaxTreeNode[] + { + _factory.Markup(prefix), + _factory.BangEscape(), + _factory.Markup(suffix).Accepts(acceptedCharacters) + }); + + newChildren.AddRange(children); + + return new MarkupTagBlock(newChildren.ToArray()); } public Block MarkupTagBlock(string content) diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/ParserTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Framework/ParserTestBase.cs index 439d8643d2..8c0f89a323 100644 --- a/test/Microsoft.AspNet.Razor.Test/Framework/ParserTestBase.cs +++ b/test/Microsoft.AspNet.Razor.Test/Framework/ParserTestBase.cs @@ -303,6 +303,7 @@ namespace Microsoft.AspNet.Razor.Test.Framework if (actual == null) { AddNullActualError(collector, actual, expected); + return; } if (actual.IsBlock != expected.IsBlock) @@ -335,7 +336,14 @@ namespace Microsoft.AspNet.Razor.Test.Framework collector.AddMessage("{0} - PASSED :: Attribute names match", expected.Key); } - EvaluateSyntaxTreeNode(collector, actual.Value, expected.Value); + if (actual.Value == null && expected.Value == null) + { + collector.AddMessage("{0} - PASSED :: Minimized attribute values match", expected.Key); + } + else + { + EvaluateSyntaxTreeNode(collector, actual.Value, expected.Value); + } } private static void EvaluateSpan(ErrorCollector collector, Span actual, Span expected) diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs index 1c0e33c291..02670e278d 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs @@ -19,6 +19,44 @@ namespace Microsoft.AspNet.Razor.Test.Generator private static IEnumerable PrefixedPAndInputTagHelperDescriptors => BuildPAndInputTagHelperDescriptors("THS"); + private static IEnumerable MinimizedTagHelpers_Descriptors + { + get + { + return new[] + { + new TagHelperDescriptor( + tagName: "*", + typeName: "CatchAllTagHelper", + assemblyName: "SomeAssembly", + attributes: new[] + { + new TagHelperAttributeDescriptor( + "catchall-bound-string", + "BoundRequiredString", + typeof(string).FullName), + }, + requiredAttributes: new[] { "catchall-unbound-required" }), + new TagHelperDescriptor( + tagName: "input", + typeName: "InputTagHelper", + assemblyName: "SomeAssembly", + attributes: new[] + { + new TagHelperAttributeDescriptor( + "input-bound-required-string", + "BoundRequiredString", + typeof(string).FullName), + new TagHelperAttributeDescriptor( + "input-bound-string", + "BoundString", + typeof(string).FullName) + }, + requiredAttributes: new[] { "input-bound-required-string", "input-unbound-required" }), + }; + } + } + private static IEnumerable DuplicateTargetTagHelperDescriptors { get @@ -207,6 +245,20 @@ namespace Microsoft.AspNet.Razor.Test.Generator AttributeTargetingTagHelperDescriptors, AttributeTargetingTagHelperDescriptors, true + }, + { + "MinimizedTagHelpers", + "MinimizedTagHelpers", + MinimizedTagHelpers_Descriptors, + MinimizedTagHelpers_Descriptors, + false + }, + { + "MinimizedTagHelpers", + "MinimizedTagHelpers.DesignTime", + MinimizedTagHelpers_Descriptors, + MinimizedTagHelpers_Descriptors, + true } }; } diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlAttributeTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlAttributeTest.cs index 5f139cc6ed..26f44600af 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlAttributeTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlAttributeTest.cs @@ -70,7 +70,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html new MarkupBlock(new AttributeBlockCodeGenerator(name: "href", prefix: new LocationTagged(" href=", 2, 0, 2), suffix: new LocationTagged(string.Empty, 11, 0, 11)), Factory.Markup(" href=").With(SpanCodeGenerator.Null), Factory.Markup("Foo").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged(string.Empty, 8, 0, 8), value: new LocationTagged("Foo", 8, 0, 8)))), - Factory.Markup(" Bar Baz />").Accepts(AcceptedCharacters.None)))); + new MarkupBlock(Factory.Markup(" Bar")), + new MarkupBlock(Factory.Markup(" Baz")), + Factory.Markup(" />").Accepts(AcceptedCharacters.None)))); } [Fact] diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlDocumentTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlDocumentTest.cs index ed797f121b..81776a164e 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlDocumentTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlDocumentTest.cs @@ -72,7 +72,11 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html ParseDocumentTest("

Foo

", new MarkupBlock( BlockFactory.MarkupTagBlock("
"), - BlockFactory.MarkupTagBlock("

"), + new MarkupTagBlock( + Factory.Markup("")), Factory.Markup(" Foo "), BlockFactory.MarkupTagBlock("

"), BlockFactory.MarkupTagBlock("
"))); diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs new file mode 100644 index 0000000000..7c1886f201 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs @@ -0,0 +1,766 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Test.TagHelpers; +using Xunit; + +namespace Microsoft.AspNet.Razor.TagHelpers +{ + public class TagHelperBlockRewriterTest : TagHelperRewritingTestBase + { + public static TheoryData MinimizedAttributeData_Document + { + get + { + var factory = CreateDefaultSpanFactory(); + var noErrors = new RazorError[0]; + var errorFormat = "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 stringType = typeof(string).FullName; + var intType = typeof(int).FullName; + var expressionString = "@DateTime.Now + 1"; + var expression = new MarkupBlock( + new MarkupBlock( + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("DateTime.Now") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + factory.Markup(" +"), + factory.Markup(" 1")); + + // documentContent, expectedOutput, expectedErrors + return new TheoryData + { + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("unbound-required", null), + })), + noErrors + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock( + "p", + selfClosing: false, + attributes: new List>() + { + new KeyValuePair("bound-string", null), + })), + new[] + { + new RazorError( + string.Format(errorFormat, "bound-string", "p", stringType), 3, 0, 3, 12) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("bound-required-string", null), + })), + new[] + { + new RazorError( + string.Format(errorFormat, "bound-required-string", "input", stringType), 7, 0, 7, 21) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("bound-required-int", null), + })), + new[] + { + new RazorError( + string.Format(errorFormat, "bound-required-int", "input", intType), 7, 0, 7, 18) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock( + "p", + selfClosing: false, + attributes: new List>() + { + new KeyValuePair("bound-int", null), + })), + new[] { new RazorError(string.Format(errorFormat, "bound-int", "p", intType), 3, 0, 3, 9) } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("unbound-required", null), + new KeyValuePair("bound-required-string", null), + })), + new[] + { + new RazorError( + string.Format(errorFormat, "bound-required-string", "input", stringType), + absoluteIndex: 24, + lineIndex: 0, + columnIndex: 24, + length: 21) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock( + "p", + selfClosing: false, + attributes: new List>() + { + new KeyValuePair("bound-int", null), + new KeyValuePair("bound-string", null), + })), + new[] + { + new RazorError(string.Format(errorFormat, "bound-int", "p", intType), 3, 0, 3, 9), + new RazorError(string.Format(errorFormat, "bound-string", "p", stringType), 13, 0, 13, 12), + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("bound-required-int", null), + new KeyValuePair("unbound-required", null), + new KeyValuePair("bound-required-string", null), + })), + new[] + { + new RazorError( + string.Format(errorFormat, "bound-required-int", "input", intType), 7, 0, 7, 18), + new RazorError( + string.Format(errorFormat, "bound-required-string", "input", stringType), + absoluteIndex: 43, + lineIndex: 0, + columnIndex: 43, + length: 21) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock( + "p", + selfClosing: false, + attributes: new List>() + { + new KeyValuePair("bound-int", null), + new KeyValuePair("bound-string", null), + new KeyValuePair("bound-string", null), + })), + new[] + { + new RazorError(string.Format(errorFormat, "bound-int", "p", intType), 3, 0, 3, 9), + new RazorError(string.Format(errorFormat, "bound-string", "p", stringType), 13, 0, 13, 12), + new RazorError(string.Format(errorFormat, "bound-string", "p", stringType), 26, 0, 26, 12), + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("unbound-required", null), + new KeyValuePair("class", factory.Markup("btn")), + })), + noErrors + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock( + "p", + selfClosing: false, + attributes: new List>() + { + new KeyValuePair("bound-string", null), + new KeyValuePair("class", factory.Markup("btn")), + })), + new[] + { + new RazorError( + string.Format(errorFormat, "bound-string", "p", stringType), + absoluteIndex: 3, + lineIndex: 0, + columnIndex: 3, + length: 12) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("class", factory.Markup("btn")), + new KeyValuePair("unbound-required", null), + })), + noErrors + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock( + "p", + selfClosing: false, + attributes: new List>() + { + new KeyValuePair("class", factory.Markup("btn")), + new KeyValuePair("bound-string", null), + })), + new[] + { + new RazorError( + string.Format(errorFormat, "bound-string", "p", stringType), + absoluteIndex: 15, + lineIndex: 0, + columnIndex: 15, + length: 12) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("bound-required-string", null), + new KeyValuePair("class", factory.Markup("btn")), + })), + new[] + { + new RazorError( + string.Format(errorFormat, "bound-required-string", "input", stringType), + absoluteIndex: 7, + lineIndex: 0, + columnIndex: 7, + length: 21) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("class", factory.Markup("btn")), + new KeyValuePair("bound-required-string", null), + })), + new[] + { + new RazorError( + string.Format(errorFormat, "bound-required-string", "input", stringType), + absoluteIndex: 19, + lineIndex: 0, + columnIndex: 19, + length: 21) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("bound-required-int", null), + new KeyValuePair("class", factory.Markup("btn")), + })), + new[] + { + new RazorError( + string.Format(errorFormat, "bound-required-int", "input", intType), 7, 0, 7, 18) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock( + "p", + selfClosing: false, + attributes: new List>() + { + new KeyValuePair("bound-int", null), + new KeyValuePair("class", factory.Markup("btn")), + })), + new[] + { + new RazorError(string.Format(errorFormat, "bound-int", "p", intType), 3, 0, 3, 9) + } + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("class", factory.Markup("btn")), + new KeyValuePair("bound-required-int", null), + })), + new[] + { + new RazorError(string.Format(errorFormat, "bound-required-int", "input", intType), 19, 0, 19, 18) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock( + "p", + selfClosing: false, + attributes: new List>() + { + new KeyValuePair("class", factory.Markup("btn")), + new KeyValuePair("bound-int", null), + })), + new[] + { + new RazorError(string.Format(errorFormat, "bound-int", "p", intType), 15, 0, 15, 9) + } + }, + { + $"", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("class", expression), + new KeyValuePair("bound-required-int", null), + })), + new[] + { + new RazorError( + string.Format(errorFormat, "bound-required-int", "input", intType), 33, 0, 33, 18) + } + }, + { + $"

", + new MarkupBlock( + new MarkupTagHelperBlock( + "p", + selfClosing: false, + attributes: new List>() + { + new KeyValuePair("class", expression), + new KeyValuePair("bound-int", null), + })), + new[] + { + new RazorError(string.Format(errorFormat, "bound-int", "p", intType), 29, 0, 29, 9) + } + }, + { + $"", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("bound-required-int", null), + new KeyValuePair("class", expression), + new KeyValuePair("bound-required-string", null), + new KeyValuePair("class", expression), + new KeyValuePair("unbound-required", null), + })), + new[] + { + new RazorError( + string.Format(errorFormat, "bound-required-int", "input", intType), 10, 0, 10, 18), + new RazorError( + string.Format(errorFormat, "bound-required-string", "input", stringType), + absoluteIndex: 57, + lineIndex: 0, + columnIndex: 57, + length: 21), + } + }, + { + $"

", + new MarkupBlock( + new MarkupTagHelperBlock( + "p", + selfClosing: false, + attributes: new List>() + { + new KeyValuePair("bound-int", null), + new KeyValuePair("class", expression), + new KeyValuePair("bound-string", null), + new KeyValuePair("class", expression), + new KeyValuePair("bound-string", null), + })), + new[] + { + new RazorError(string.Format(errorFormat, "bound-int", "p", intType), 6, 0, 6, 9), + new RazorError(string.Format(errorFormat, "bound-string", "p", stringType), 44, 0, 44, 12), + new RazorError(string.Format(errorFormat, "bound-string", "p", stringType), 84, 0, 84, 12), + } + }, + }; + } + } + + public static TheoryData MinimizedAttributeData_CSharpBlock + { + get + { + var factory = CreateDefaultSpanFactory(); + var documentData = MinimizedAttributeData_Document; + Func, MarkupBlock> buildStatementBlock = (insideBuilder) => + { + return new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("{").Accepts(AcceptedCharacters.None), + insideBuilder(), + factory.EmptyCSharp().AsStatement(), + factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml()); + }; + + foreach (var data in documentData) + { + data[0] = $"@{{{data[0]}}}"; + data[1] = buildStatementBlock(() => data[1] as MarkupBlock); + + var errors = data[2] as RazorError[]; + + for (var i = 0; i < errors.Length; i++) + { + var error = errors[i]; + error.Location = SourceLocation.Advance(error.Location, "@{"); + } + } + + return documentData; + } + } + + public static TheoryData MinimizedAttributeData_PartialTags + { + get + { + var factory = CreateDefaultSpanFactory(); + var noErrors = new RazorError[0]; + 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 errorFormatNoValue = "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 stringType = typeof(string).FullName; + var intType = typeof(int).FullName; + + // documentContent, expectedOutput, expectedErrors + return new TheoryData + { + { + ">() + { + new KeyValuePair("unbound-required", null), + })), + new[] + { + new RazorError(string.Format(errorFormatNoCloseAngle, "input"), SourceLocation.Zero), + new RazorError(string.Format(errorFormatUnclosed, "input"), SourceLocation.Zero), + } + }, + { + ">() + { + new KeyValuePair("bound-required-string", null), + })), + new[] + { + new RazorError(string.Format(errorFormatNoCloseAngle, "input"), SourceLocation.Zero), + new RazorError(string.Format(errorFormatUnclosed, "input"), SourceLocation.Zero), + new RazorError( + string.Format(errorFormatNoValue, "bound-required-string", "input", stringType), + absoluteIndex: 7, + lineIndex: 0, + columnIndex: 7, + length: 21), + } + }, + { + ">() + { + new KeyValuePair("bound-required-int", null), + })), + new[] + { + new RazorError(string.Format(errorFormatNoCloseAngle, "input"), SourceLocation.Zero), + new RazorError(string.Format(errorFormatUnclosed, "input"), SourceLocation.Zero), + new RazorError( + string.Format(errorFormatNoValue, "bound-required-int", "input", intType), + absoluteIndex: 7, + lineIndex: 0, + columnIndex: 7, + length: 18), + } + }, + { + ">() + { + new KeyValuePair("bound-required-int", null), + new KeyValuePair("unbound-required", null), + new KeyValuePair("bound-required-string", null), + })), + new[] + { + new RazorError(string.Format(errorFormatNoCloseAngle, "input"), SourceLocation.Zero), + new RazorError(string.Format(errorFormatUnclosed, "input"), SourceLocation.Zero), + new RazorError( + string.Format(errorFormatNoValue, "bound-required-int", "input", intType), + absoluteIndex: 7, + lineIndex: 0, + columnIndex: 7, + length: 18), + new RazorError( + string.Format(errorFormatNoValue, "bound-required-string", "input", stringType), + absoluteIndex: 43, + lineIndex: 0, + columnIndex: 43, + length: 21), + } + }, + { + "

>() + { + new KeyValuePair("bound-string", null), + })), + new[] + { + new RazorError(string.Format(errorFormatNoCloseAngle, "p"), SourceLocation.Zero), + new RazorError(string.Format(errorFormatUnclosed, "p"), SourceLocation.Zero), + new RazorError( + string.Format(errorFormatNoValue, "bound-string", "p", stringType), 3, 0, 3, 12), + } + }, + { + "

>() + { + new KeyValuePair("bound-int", null), + })), + new[] + { + new RazorError(string.Format(errorFormatNoCloseAngle, "p"), SourceLocation.Zero), + new RazorError(string.Format(errorFormatUnclosed, "p"), SourceLocation.Zero), + new RazorError(string.Format(errorFormatNoValue, "bound-int", "p", intType), 3, 0, 3, 9), + } + }, + { + "

>() + { + new KeyValuePair("bound-int", null), + new KeyValuePair("bound-string", null), + })), + new[] + { + new RazorError(string.Format(errorFormatNoCloseAngle, "p"), SourceLocation.Zero), + new RazorError(string.Format(errorFormatUnclosed, "p"), SourceLocation.Zero), + new RazorError(string.Format(errorFormatNoValue, "bound-int", "p", intType), 3, 0, 3, 9), + new RazorError( + string.Format(errorFormatNoValue, "bound-string", "p", stringType), 13, 0, 13, 12), + } + }, + { + ">() + { + new KeyValuePair("bound-required-int", null), + new KeyValuePair("unbound-required", null), + new KeyValuePair("bound-required-string", null), + }, + children: new MarkupTagHelperBlock( + "p", + selfClosing: false, + attributes: new List>() + { + new KeyValuePair("bound-int", null), + new KeyValuePair("bound-string", null), + }))), + new[] + { + new RazorError(string.Format(errorFormatNoCloseAngle, "input"), SourceLocation.Zero), + new RazorError(string.Format(errorFormatUnclosed, "input"), SourceLocation.Zero), + new RazorError( + string.Format(errorFormatNoValue, "bound-required-int", "input", intType), 7, 0, 7, 18), + new RazorError( + string.Format(errorFormatNoValue, "bound-required-string", "input", stringType), + absoluteIndex: 43, + lineIndex: 0, + columnIndex: 43, + length: 21), + new RazorError(string.Format(errorFormatNoCloseAngle, "p"), 64, 0, 64), + new RazorError(string.Format(errorFormatUnclosed, "p"), 64, 0, 64), + new RazorError(string.Format(errorFormatNoValue, "bound-int", "p", intType), 67, 0, 67, 9), + new RazorError( + string.Format(errorFormatNoValue, "bound-string", "p", stringType), + absoluteIndex: 77, + lineIndex: 0, + columnIndex: 77, + length: 12), + } + }, + }; + } + } + + [Theory] + [MemberData(nameof(MinimizedAttributeData_Document))] + [MemberData(nameof(MinimizedAttributeData_CSharpBlock))] + [MemberData(nameof(MinimizedAttributeData_PartialTags))] + public void Rewrite_UnderstandsMinimizedAttributes( + string documentContent, + MarkupBlock expectedOutput, + RazorError[] expectedErrors) + { + // Arrange + var descriptors = new TagHelperDescriptor[] + { + new TagHelperDescriptor( + tagName: "input", + typeName: "InputTagHelper", + assemblyName: "SomeAssembly", + attributes: new TagHelperAttributeDescriptor[0], + requiredAttributes: new[] { "unbound-required" }), + new TagHelperDescriptor( + tagName: "input", + typeName: "InputTagHelper", + assemblyName: "SomeAssembly", + attributes: new[] + { + new TagHelperAttributeDescriptor( + "bound-required-string", + "BoundRequiredString", + typeof(string).FullName) + }, + requiredAttributes: new[] { "bound-required-string" }), + new TagHelperDescriptor( + tagName: "input", + typeName: "InputTagHelper", + assemblyName: "SomeAssembly", + attributes: new[] + { + new TagHelperAttributeDescriptor( + "bound-required-int", + "BoundRequiredInt", + typeof(int).FullName) + }, + requiredAttributes: new[] { "bound-required-int" }), + new TagHelperDescriptor( + tagName: "p", + typeName: "PTagHelper", + assemblyName: "SomeAssembly", + attributes: new[] + { + new TagHelperAttributeDescriptor( + "bound-string", + "BoundRequiredString", + typeof(string).FullName), + new TagHelperAttributeDescriptor( + "bound-int", + "BoundRequiredString", + typeof(int).FullName) + }, + requiredAttributes: Enumerable.Empty()), + }; + var descriptorProvider = new TagHelperDescriptorProvider(descriptors); + + // Act & Assert + EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs index adafe042d6..f67f629591 100644 --- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs @@ -8,16 +8,14 @@ using System.Linq; using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser.SyntaxTree; -using Microsoft.AspNet.Razor.Parser.TagHelpers.Internal; using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.AspNet.Razor.Test.Framework; using Microsoft.AspNet.Razor.Text; -using Microsoft.AspNet.Razor.Tokenizer; using Xunit; namespace Microsoft.AspNet.Razor.Test.TagHelpers { - public class TagHelperParseTreeRewriterTest : CsHtmlMarkupParserTestBase + public class TagHelperParseTreeRewriterTest : TagHelperRewritingTestBase { public static TheoryData RequiredAttributeData { @@ -1949,7 +1947,11 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers { "@{ new MarkupBlock(blockFactory.EscapedMarkupTagBlock("<", "text /}"))), + () => new MarkupBlock( + blockFactory.EscapedMarkupTagBlock( + "<", + "text /", + new MarkupBlock(factory.Markup("}"))))), new [] { new RazorError( @@ -2036,7 +2038,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers prefix: new LocationTagged(string.Empty, 16, 0, 16), value: new LocationTagged("btn", 16, 0, 16))), factory.Markup("\"").With(SpanCodeGenerator.Null)), - factory.Markup("}")))), + new MarkupBlock(factory.Markup("}"))))), new [] { new RazorError( @@ -2067,7 +2069,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers prefix: new LocationTagged(string.Empty, 16, 0, 16), value: new LocationTagged("btn", 16, 0, 16))), factory.Markup("\"").With(SpanCodeGenerator.Null)), - factory.Markup(" /}")))), + factory.Markup(" /"), + new MarkupBlock(factory.Markup("}"))))), new [] { new RazorError( @@ -2152,7 +2155,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers { "@{ new MarkupBlock(blockFactory.EscapedMarkupTagBlock("<", "p /}"))), + () => new MarkupBlock( + blockFactory.EscapedMarkupTagBlock("<", "p /", new MarkupBlock(factory.Markup("}"))))), new [] { new RazorError( @@ -2274,7 +2278,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers prefix: new LocationTagged(string.Empty, 13, 0, 13), value: new LocationTagged("btn", 13, 0, 13))), factory.Markup("\"").With(SpanCodeGenerator.Null)), - factory.Markup("}")))), + new MarkupBlock(factory.Markup("}"))))), new [] { new RazorError( @@ -2305,7 +2309,9 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers prefix: new LocationTagged(string.Empty, 13, 0, 13), value: new LocationTagged("btn", 13, 0, 13))), factory.Markup("\"").With(SpanCodeGenerator.Null)), - factory.Markup(" /}")))), + factory.Markup(" /"), + new MarkupBlock( + factory.Markup("}"))))), new [] { new RazorError( @@ -3353,6 +3359,11 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers "

", new MarkupBlock( new MarkupTagHelperBlock("p", + new List> + { + new KeyValuePair("foo", null), + new KeyValuePair("bar", null) + }, new MarkupTagHelperBlock("strong"))), new [] { @@ -3406,7 +3417,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers new KeyValuePair( "class", new MarkupBlock(factory.Markup("btn"), factory.Markup(" bar="))), - new KeyValuePair("foo", factory.Markup(string.Empty)) + new KeyValuePair("foo", null) }, new MarkupTagHelperBlock("strong"))), new [] @@ -3429,6 +3440,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers new List> { new KeyValuePair("class", new MarkupBlock(factory.Markup("btn"), factory.Markup(" bar="))), + new KeyValuePair("foo", null), })), new RazorError[0] }, @@ -4013,7 +4025,11 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers { "< p />", new MarkupBlock( - blockFactory.MarkupTagBlock("< p />")) + new MarkupTagBlock( + factory.Markup("<"), + new MarkupBlock( + factory.Markup(" p")), + factory.Markup(" />"))) }, { "", @@ -5076,74 +5092,5 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers { RunParseTreeRewriterTest(documentContent, expectedOutput, "p", "div"); } - - private void RunParseTreeRewriterTest(string documentContent, - MarkupBlock expectedOutput, - params string[] tagNames) - { - RunParseTreeRewriterTest(documentContent, - expectedOutput, - errors: Enumerable.Empty(), - tagNames: tagNames); - } - - private void RunParseTreeRewriterTest(string documentContent, - MarkupBlock expectedOutput, - IEnumerable errors, - params string[] tagNames) - { - // Arrange - var providerContext = BuildProviderContext(tagNames); - - // Act & Assert - EvaluateData(providerContext, documentContent, expectedOutput, errors); - } - - private TagHelperDescriptorProvider BuildProviderContext(params string[] tagNames) - { - var descriptors = new List(); - - foreach (var tagName in tagNames) - { - descriptors.Add( - new TagHelperDescriptor(tagName, tagName + "taghelper", "SomeAssembly")); - } - - return new TagHelperDescriptorProvider(descriptors); - } - - public override ParserContext CreateParserContext(ITextDocument input, - ParserBase codeParser, - ParserBase markupParser, - ErrorSink errorSink) - { - return base.CreateParserContext(input, codeParser, markupParser, errorSink); - } - - private void EvaluateData(TagHelperDescriptorProvider provider, - string documentContent, - MarkupBlock expectedOutput, - IEnumerable expectedErrors) - { - var errorSink = new ErrorSink(); - var results = ParseDocument(documentContent, errorSink); - var rewritingContext = new RewritingContext(results.Document, errorSink); - new TagHelperParseTreeRewriter(provider).Rewrite(rewritingContext); - var rewritten = rewritingContext.SyntaxTree; - var actualErrors = errorSink.Errors.OrderBy(error => error.Location.AbsoluteIndex) - .ToList(); - - EvaluateRazorErrors(actualErrors, expectedErrors.ToList()); - EvaluateParseTree(rewritten, expectedOutput); - } - - private static SpanFactory CreateDefaultSpanFactory() - { - return new SpanFactory - { - MarkupTokenizerFactory = doc => new HtmlTokenizer(doc), - CodeTokenizerFactory = doc => new CSharpTokenizer(doc) - }; - } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperRewritingTestBase.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperRewritingTestBase.cs new file mode 100644 index 0000000000..f0a4db8fc8 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperRewritingTestBase.cs @@ -0,0 +1,89 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.TagHelpers.Internal; +using Microsoft.AspNet.Razor.TagHelpers; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer; + +namespace Microsoft.AspNet.Razor.Test.TagHelpers +{ + public class TagHelperRewritingTestBase : CsHtmlMarkupParserTestBase + { + public void RunParseTreeRewriterTest( + string documentContent, + MarkupBlock expectedOutput, + params string[] tagNames) + { + RunParseTreeRewriterTest( + documentContent, + expectedOutput, + errors: Enumerable.Empty(), + tagNames: tagNames); + } + + public void RunParseTreeRewriterTest( + string documentContent, + MarkupBlock expectedOutput, + IEnumerable errors, + params string[] tagNames) + { + var providerContext = BuildProviderContext(tagNames); + + EvaluateData(providerContext, documentContent, expectedOutput, errors); + } + + public TagHelperDescriptorProvider BuildProviderContext(params string[] tagNames) + { + var descriptors = new List(); + + foreach (var tagName in tagNames) + { + descriptors.Add( + new TagHelperDescriptor(tagName, tagName + "taghelper", "SomeAssembly")); + } + + return new TagHelperDescriptorProvider(descriptors); + } + + public override ParserContext CreateParserContext( + ITextDocument input, + ParserBase codeParser, + ParserBase markupParser, + ErrorSink errorSink) + { + return base.CreateParserContext(input, codeParser, markupParser, errorSink); + } + + public void EvaluateData( + TagHelperDescriptorProvider provider, + string documentContent, + MarkupBlock expectedOutput, + IEnumerable expectedErrors) + { + var errorSink = new ErrorSink(); + var results = ParseDocument(documentContent, errorSink); + var rewritingContext = new RewritingContext(results.Document, errorSink); + new TagHelperParseTreeRewriter(provider).Rewrite(rewritingContext); + var rewritten = rewritingContext.SyntaxTree; + var actualErrors = errorSink.Errors.OrderBy(error => error.Location.AbsoluteIndex) + .ToList(); + + EvaluateRazorErrors(actualErrors, expectedErrors.ToList()); + EvaluateParseTree(rewritten, expectedOutput); + } + + public static SpanFactory CreateDefaultSpanFactory() + { + return new SpanFactory + { + MarkupTokenizerFactory = doc => new HtmlTokenizer(doc), + CodeTokenizerFactory = doc => new CSharpTokenizer(doc) + }; + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/MinimizedTagHelpers.DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/MinimizedTagHelpers.DesignTime.cs new file mode 100644 index 0000000000..dc4e928a67 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/MinimizedTagHelpers.DesignTime.cs @@ -0,0 +1,49 @@ +namespace TestOutput +{ + using Microsoft.AspNet.Razor.Runtime.TagHelpers; + using System; + using System.Threading.Tasks; + + public class MinimizedTagHelpers + { + private static object @__o; + private void @__RazorDesignTimeHelpers__() + { + #pragma warning disable 219 + string __tagHelperDirectiveSyntaxHelper = null; + __tagHelperDirectiveSyntaxHelper = +#line 1 "MinimizedTagHelpers.cshtml" + "something, nice" + +#line default +#line hidden + ; + #pragma warning restore 219 + } + #line hidden + private CatchAllTagHelper __CatchAllTagHelper = null; + private InputTagHelper __InputTagHelper = null; + #line hidden + public MinimizedTagHelpers() + { + } + + #pragma warning disable 1998 + public override async Task ExecuteAsync() + { + __CatchAllTagHelper = CreateTagHelper(); + __InputTagHelper = CreateTagHelper(); + __InputTagHelper.BoundRequiredString = "hello"; + __CatchAllTagHelper = CreateTagHelper(); + __InputTagHelper = CreateTagHelper(); + __InputTagHelper.BoundRequiredString = "hello2"; + __CatchAllTagHelper = CreateTagHelper(); + __CatchAllTagHelper.BoundRequiredString = "world"; + __InputTagHelper = CreateTagHelper(); + __InputTagHelper.BoundRequiredString = "world"; + __CatchAllTagHelper = CreateTagHelper(); + __CatchAllTagHelper = CreateTagHelper(); + } + #pragma warning restore 1998 + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/MinimizedTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/MinimizedTagHelpers.cs new file mode 100644 index 0000000000..050b8f6b52 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/MinimizedTagHelpers.cs @@ -0,0 +1,106 @@ +#pragma checksum "MinimizedTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "07839be4304797e30b19b50b95e2247c93cdff06" +namespace TestOutput +{ + using Microsoft.AspNet.Razor.Runtime.TagHelpers; + using System; + using System.Threading.Tasks; + + public class MinimizedTagHelpers + { + #line hidden + #pragma warning disable 0414 + private TagHelperContent __tagHelperStringValueBuffer = null; + #pragma warning restore 0414 + private TagHelperExecutionContext __tagHelperExecutionContext = null; + private TagHelperRunner __tagHelperRunner = null; + private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager(); + private CatchAllTagHelper __CatchAllTagHelper = null; + private InputTagHelper __InputTagHelper = null; + #line hidden + public MinimizedTagHelpers() + { + } + + #pragma warning disable 1998 + public override async Task ExecuteAsync() + { + __tagHelperRunner = __tagHelperRunner ?? new TagHelperRunner(); + Instrumentation.BeginContext(33, 2, true); + WriteLiteral("\r\n"); + Instrumentation.EndContext(); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", false, "test", async() => { + WriteLiteral("\r\n \r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => { + } + , StartTagHelperWritingScope, EndTagHelperWritingScope); + __CatchAllTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__CatchAllTagHelper); + __tagHelperExecutionContext.AddHtmlAttribute("class", Html.Raw("btn")); + __tagHelperExecutionContext.AddMinimizedHtmlAttribute("catchall-unbound-required"); + __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext); + await WriteTagHelperAsync(__tagHelperExecutionContext); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => { + } + , StartTagHelperWritingScope, EndTagHelperWritingScope); + __InputTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper); + __InputTagHelper.BoundRequiredString = "hello"; + __tagHelperExecutionContext.AddTagHelperAttribute("input-bound-required-string", __InputTagHelper.BoundRequiredString); + __CatchAllTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__CatchAllTagHelper); + __tagHelperExecutionContext.AddHtmlAttribute("class", Html.Raw("btn")); + __tagHelperExecutionContext.AddMinimizedHtmlAttribute("catchall-unbound-required"); + __tagHelperExecutionContext.AddMinimizedHtmlAttribute("input-unbound-required"); + __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext); + await WriteTagHelperAsync(__tagHelperExecutionContext); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => { + } + , StartTagHelperWritingScope, EndTagHelperWritingScope); + __InputTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper); + __InputTagHelper.BoundRequiredString = "hello2"; + __tagHelperExecutionContext.AddTagHelperAttribute("input-bound-required-string", __InputTagHelper.BoundRequiredString); + __CatchAllTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__CatchAllTagHelper); + __CatchAllTagHelper.BoundRequiredString = "world"; + __tagHelperExecutionContext.AddTagHelperAttribute("catchall-bound-string", __CatchAllTagHelper.BoundRequiredString); + __tagHelperExecutionContext.AddHtmlAttribute("class", Html.Raw("btn")); + __tagHelperExecutionContext.AddMinimizedHtmlAttribute("catchall-unbound-required"); + __tagHelperExecutionContext.AddMinimizedHtmlAttribute("input-unbound-required"); + __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext); + await WriteTagHelperAsync(__tagHelperExecutionContext); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => { + } + , StartTagHelperWritingScope, EndTagHelperWritingScope); + __InputTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper); + __InputTagHelper.BoundRequiredString = "world"; + __tagHelperExecutionContext.AddTagHelperAttribute("input-bound-required-string", __InputTagHelper.BoundRequiredString); + __CatchAllTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__CatchAllTagHelper); + __tagHelperExecutionContext.AddHtmlAttribute("class", Html.Raw("btn")); + __tagHelperExecutionContext.AddHtmlAttribute("catchall-unbound-required", Html.Raw("hello")); + __tagHelperExecutionContext.AddHtmlAttribute("input-unbound-required", Html.Raw("hello2")); + __tagHelperExecutionContext.AddMinimizedHtmlAttribute("catchall-unbound-required"); + __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext); + await WriteTagHelperAsync(__tagHelperExecutionContext); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n"); + } + , StartTagHelperWritingScope, EndTagHelperWritingScope); + __CatchAllTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__CatchAllTagHelper); + __tagHelperExecutionContext.AddMinimizedHtmlAttribute("catchall-unbound-required"); + __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext); + await WriteTagHelperAsync(__tagHelperExecutionContext); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + } + #pragma warning restore 1998 + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/MinimizedTagHelpers.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/MinimizedTagHelpers.cshtml new file mode 100644 index 0000000000..f0c443cc9e --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/MinimizedTagHelpers.cshtml @@ -0,0 +1,18 @@ +@addTagHelper "something, nice" + +

+ + + + + +

\ No newline at end of file