Add tests to validate minimized attributes.
- Added parse level rewriting tests to validate new TagHelper rewritten structures for minimized attributes. - Updated existing parser tests to understand minimized attributes. - Added codegen test to validate understanding of minimized attributes. - Added TagHelperExecutionContext tests to validate maintaining of runtime TagHelperOutput tests. - Refactored part of the TagHelperParseTreeRewriterTest file into a base class file so we can make better rewriting tests. #220
This commit is contained in:
parent
6fa3e405af
commit
0882ff4a13
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<SyntaxTreeNode>(
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,44 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
private static IEnumerable<TagHelperDescriptor> PrefixedPAndInputTagHelperDescriptors
|
||||
=> BuildPAndInputTagHelperDescriptors("THS");
|
||||
|
||||
private static IEnumerable<TagHelperDescriptor> 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<TagHelperDescriptor> 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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html
|
|||
new MarkupBlock(new AttributeBlockCodeGenerator(name: "href", prefix: new LocationTagged<string>(" href=", 2, 0, 2), suffix: new LocationTagged<string>(string.Empty, 11, 0, 11)),
|
||||
Factory.Markup(" href=").With(SpanCodeGenerator.Null),
|
||||
Factory.Markup("Foo").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged<string>(string.Empty, 8, 0, 8), value: new LocationTagged<string>("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]
|
||||
|
|
|
|||
|
|
@ -72,7 +72,11 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html
|
|||
ParseDocumentTest("<div ><p class = 'bar'> Foo </p></div >",
|
||||
new MarkupBlock(
|
||||
BlockFactory.MarkupTagBlock("<div >"),
|
||||
BlockFactory.MarkupTagBlock("<p class = 'bar'>"),
|
||||
new MarkupTagBlock(
|
||||
Factory.Markup("<p"),
|
||||
new MarkupBlock(
|
||||
Factory.Markup(" class")),
|
||||
Factory.Markup(" = 'bar'>")),
|
||||
Factory.Markup(" Foo "),
|
||||
BlockFactory.MarkupTagBlock("</p>"),
|
||||
BlockFactory.MarkupTagBlock("</div >")));
|
||||
|
|
|
|||
|
|
@ -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<string, MarkupBlock, RazorError[]>
|
||||
{
|
||||
{
|
||||
"<input unbound-required />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("unbound-required", null),
|
||||
})),
|
||||
noErrors
|
||||
},
|
||||
{
|
||||
"<p bound-string></p>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-string", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "bound-string", "p", stringType), 3, 0, 3, 12)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input bound-required-string />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-string", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "bound-required-string", "input", stringType), 7, 0, 7, 21)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input bound-required-int />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-int", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "bound-required-int", "input", intType), 7, 0, 7, 18)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<p bound-int></p>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-int", null),
|
||||
})),
|
||||
new[] { new RazorError(string.Format(errorFormat, "bound-int", "p", intType), 3, 0, 3, 9) }
|
||||
},
|
||||
{
|
||||
"<input unbound-required bound-required-string />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("unbound-required", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-string", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "bound-required-string", "input", stringType),
|
||||
absoluteIndex: 24,
|
||||
lineIndex: 0,
|
||||
columnIndex: 24,
|
||||
length: 21)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<p bound-int bound-string></p>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-int", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input bound-required-int unbound-required bound-required-string />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-int", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("unbound-required", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<p bound-int bound-string bound-string></p>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-int", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-string", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input unbound-required class='btn' />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("unbound-required", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", factory.Markup("btn")),
|
||||
})),
|
||||
noErrors
|
||||
},
|
||||
{
|
||||
"<p bound-string class='btn'></p>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-string", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", factory.Markup("btn")),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "bound-string", "p", stringType),
|
||||
absoluteIndex: 3,
|
||||
lineIndex: 0,
|
||||
columnIndex: 3,
|
||||
length: 12)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input class='btn' unbound-required />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", factory.Markup("btn")),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("unbound-required", null),
|
||||
})),
|
||||
noErrors
|
||||
},
|
||||
{
|
||||
"<p class='btn' bound-string></p>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", factory.Markup("btn")),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-string", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "bound-string", "p", stringType),
|
||||
absoluteIndex: 15,
|
||||
lineIndex: 0,
|
||||
columnIndex: 15,
|
||||
length: 12)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input bound-required-string class='btn' />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-string", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", factory.Markup("btn")),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "bound-required-string", "input", stringType),
|
||||
absoluteIndex: 7,
|
||||
lineIndex: 0,
|
||||
columnIndex: 7,
|
||||
length: 21)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input class='btn' bound-required-string />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", factory.Markup("btn")),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-string", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "bound-required-string", "input", stringType),
|
||||
absoluteIndex: 19,
|
||||
lineIndex: 0,
|
||||
columnIndex: 19,
|
||||
length: 21)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input bound-required-int class='btn' />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-int", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", factory.Markup("btn")),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "bound-required-int", "input", intType), 7, 0, 7, 18)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<p bound-int class='btn'></p>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-int", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", factory.Markup("btn")),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(string.Format(errorFormat, "bound-int", "p", intType), 3, 0, 3, 9)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input class='btn' bound-required-int />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", factory.Markup("btn")),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-int", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(string.Format(errorFormat, "bound-required-int", "input", intType), 19, 0, 19, 18)
|
||||
}
|
||||
},
|
||||
{
|
||||
"<p class='btn' bound-int></p>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", factory.Markup("btn")),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-int", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(string.Format(errorFormat, "bound-int", "p", intType), 15, 0, 15, 9)
|
||||
}
|
||||
},
|
||||
{
|
||||
$"<input class='{expressionString}' bound-required-int />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", expression),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-int", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "bound-required-int", "input", intType), 33, 0, 33, 18)
|
||||
}
|
||||
},
|
||||
{
|
||||
$"<p class='{expressionString}' bound-int></p>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", expression),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-int", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(string.Format(errorFormat, "bound-int", "p", intType), 29, 0, 29, 9)
|
||||
}
|
||||
},
|
||||
{
|
||||
$"<input bound-required-int class='{expressionString}' bound-required-string " +
|
||||
$"class='{expressionString}' unbound-required />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-int", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", expression),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-string", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", expression),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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),
|
||||
}
|
||||
},
|
||||
{
|
||||
$"<p bound-int class='{expressionString}' bound-string " +
|
||||
$"class='{expressionString}' bound-string></p>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-int", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", expression),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-string", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", expression),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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<Func<MarkupBlock>, 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<string, MarkupBlock, RazorError[]>
|
||||
{
|
||||
{
|
||||
"<input unbound-required",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("unbound-required", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(string.Format(errorFormatNoCloseAngle, "input"), SourceLocation.Zero),
|
||||
new RazorError(string.Format(errorFormatUnclosed, "input"), SourceLocation.Zero),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input bound-required-string",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input bound-required-int",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input bound-required-int unbound-required bound-required-string",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-int", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("unbound-required", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<p bound-string",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<p bound-int",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<p bound-int bound-string",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-int", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input bound-required-int unbound-required bound-required-string<p bound-int bound-string",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-int", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("unbound-required", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-required-string", null),
|
||||
},
|
||||
children: new MarkupTagHelperBlock(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>()
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound-int", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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<string>()),
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|||
{
|
||||
"@{<!text /}",
|
||||
buildPartialStatementBlock(
|
||||
() => 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>(string.Empty, 16, 0, 16),
|
||||
value: new LocationTagged<string>("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>(string.Empty, 16, 0, 16),
|
||||
value: new LocationTagged<string>("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
|
|||
{
|
||||
"@{<!p /}",
|
||||
buildPartialStatementBlock(
|
||||
() => 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>(string.Empty, 13, 0, 13),
|
||||
value: new LocationTagged<string>("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>(string.Empty, 13, 0, 13),
|
||||
value: new LocationTagged<string>("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
|
|||
"<p foo bar<strong>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("p",
|
||||
new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("foo", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bar", null)
|
||||
},
|
||||
new MarkupTagHelperBlock("strong"))),
|
||||
new []
|
||||
{
|
||||
|
|
@ -3406,7 +3417,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
new KeyValuePair<string, SyntaxTreeNode>(
|
||||
"class",
|
||||
new MarkupBlock(factory.Markup("btn"), factory.Markup(" bar="))),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("foo", factory.Markup(string.Empty))
|
||||
new KeyValuePair<string, SyntaxTreeNode>("foo", null)
|
||||
},
|
||||
new MarkupTagHelperBlock("strong"))),
|
||||
new []
|
||||
|
|
@ -3429,6 +3440,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("class", new MarkupBlock(factory.Markup("btn"), factory.Markup(" bar="))),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("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(" />")))
|
||||
},
|
||||
{
|
||||
"<input <p />",
|
||||
|
|
@ -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<RazorError>(),
|
||||
tagNames: tagNames);
|
||||
}
|
||||
|
||||
private void RunParseTreeRewriterTest(string documentContent,
|
||||
MarkupBlock expectedOutput,
|
||||
IEnumerable<RazorError> 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<TagHelperDescriptor>();
|
||||
|
||||
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<RazorError> 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)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<RazorError>(),
|
||||
tagNames: tagNames);
|
||||
}
|
||||
|
||||
public void RunParseTreeRewriterTest(
|
||||
string documentContent,
|
||||
MarkupBlock expectedOutput,
|
||||
IEnumerable<RazorError> errors,
|
||||
params string[] tagNames)
|
||||
{
|
||||
var providerContext = BuildProviderContext(tagNames);
|
||||
|
||||
EvaluateData(providerContext, documentContent, expectedOutput, errors);
|
||||
}
|
||||
|
||||
public TagHelperDescriptorProvider BuildProviderContext(params string[] tagNames)
|
||||
{
|
||||
var descriptors = new List<TagHelperDescriptor>();
|
||||
|
||||
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<RazorError> 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)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CatchAllTagHelper>();
|
||||
__InputTagHelper = CreateTagHelper<InputTagHelper>();
|
||||
__InputTagHelper.BoundRequiredString = "hello";
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__InputTagHelper = CreateTagHelper<InputTagHelper>();
|
||||
__InputTagHelper.BoundRequiredString = "hello2";
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__CatchAllTagHelper.BoundRequiredString = "world";
|
||||
__InputTagHelper = CreateTagHelper<InputTagHelper>();
|
||||
__InputTagHelper.BoundRequiredString = "world";
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <input nottaghelper />\r\n ");
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__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<InputTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper);
|
||||
__InputTagHelper.BoundRequiredString = "hello";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("input-bound-required-string", __InputTagHelper.BoundRequiredString);
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__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<InputTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper);
|
||||
__InputTagHelper.BoundRequiredString = "hello2";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("input-bound-required-string", __InputTagHelper.BoundRequiredString);
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__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<InputTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper);
|
||||
__InputTagHelper.BoundRequiredString = "world";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("input-bound-required-string", __InputTagHelper.BoundRequiredString);
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__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<CatchAllTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__CatchAllTagHelper);
|
||||
__tagHelperExecutionContext.AddMinimizedHtmlAttribute("catchall-unbound-required");
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
@addTagHelper "something, nice"
|
||||
|
||||
<p catchall-unbound-required>
|
||||
<input nottaghelper />
|
||||
<input class="btn"
|
||||
catchall-unbound-required />
|
||||
<input
|
||||
class="btn" catchall-unbound-required input-unbound-required input-bound-required-string="hello" />
|
||||
<input
|
||||
class="btn"
|
||||
catchall-unbound-required
|
||||
input-unbound-required catchall-bound-string="world" input-bound-required-string="hello2" />
|
||||
<input class="btn"
|
||||
catchall-unbound-required="hello"
|
||||
input-unbound-required="hello2"
|
||||
catchall-unbound-required
|
||||
input-bound-required-string="world" />
|
||||
</p>
|
||||
Loading…
Reference in New Issue