MVC tag helpers should not override tag name in Razor source
- #1523 - remove `TagHelperOutput.Merge()` extension method entirely - test tag name preservation with all MVC tag helpers - `<input/>` tag helper generation of a checkbox wasn't previously tested nits: - fix argument order in a couple of `Assert.Equal()` calls - remove use of "original tag name"
This commit is contained in:
parent
99a1848598
commit
1b28e19114
|
|
@ -97,8 +97,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
|
||||
if (tagBuilder != null)
|
||||
{
|
||||
// We don't want to do a full merge because we want the TagHelper content to take precedence.
|
||||
output.Merge(tagBuilder);
|
||||
output.MergeAttributes(tagBuilder);
|
||||
output.Content += tagBuilder.InnerHtml;
|
||||
output.SelfClosing = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -199,8 +199,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{
|
||||
// This TagBuilder contains the one <input/> element of interest. Since this is not the "checkbox"
|
||||
// special-case, output is a self-closing element and can merge the TagBuilder in directly.
|
||||
output.MergeAttributes(tagBuilder);
|
||||
output.Content += tagBuilder.InnerHtml;
|
||||
output.SelfClosing = true;
|
||||
output.Merge(tagBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,8 +48,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{
|
||||
output.Content = tagBuilder.InnerHtml;
|
||||
}
|
||||
|
||||
output.TagName = tagBuilder.TagName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,8 +137,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
|
||||
if (tagBuilder != null)
|
||||
{
|
||||
output.MergeAttributes(tagBuilder);
|
||||
output.Content += tagBuilder.InnerHtml;
|
||||
output.SelfClosing = false;
|
||||
output.Merge(tagBuilder);
|
||||
}
|
||||
|
||||
// Whether or not (not being highly unlikely) we generate anything, could update contained <option/>
|
||||
|
|
|
|||
|
|
@ -60,23 +60,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
return prefixedAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the given <paramref name="tagBuilder"/> into the <paramref name="tagHelperOutput"/>.
|
||||
/// </summary>
|
||||
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
|
||||
/// <param name="tagBuilder">The <see cref="TagBuilder"/> to merge.</param>
|
||||
/// <remarks><paramref name="tagHelperOutput"/>'s <see cref="TagHelperOutput.Content"/> has the given
|
||||
/// <paramref name="tagBuilder"/>s <see cref="TagBuilder.InnerHtml"/> appended to it. This is to ensure
|
||||
/// multiple <see cref="ITagHelper"/>s running on the same HTML tag don't overwrite each other; therefore,
|
||||
/// this method may not be appropriate for all <see cref="ITagHelper"/> scenarios.</remarks>
|
||||
public static void Merge(this TagHelperOutput tagHelperOutput, TagBuilder tagBuilder)
|
||||
{
|
||||
tagHelperOutput.TagName = tagBuilder.TagName;
|
||||
tagHelperOutput.Content += tagBuilder.InnerHtml;
|
||||
|
||||
MergeAttributes(tagHelperOutput, tagBuilder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the given <paramref name="tagBuilder"/>'s <see cref="TagBuilder.Attributes"/> into the
|
||||
/// <paramref name="tagHelperOutput"/>.
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
|
||||
output.MergeAttributes(tagBuilder);
|
||||
output.SelfClosing = false;
|
||||
output.TagName = tagBuilder.TagName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
public async Task ProcessAsync_GeneratesExpectedOutput()
|
||||
{
|
||||
// Arrange
|
||||
var expectedTagName = "not-a";
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var anchorTagHelper = new AnchorTagHelper
|
||||
{
|
||||
|
|
@ -40,7 +41,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{ "protocol", "http" }
|
||||
});
|
||||
var output = new TagHelperOutput(
|
||||
"a",
|
||||
expectedTagName,
|
||||
attributes: new Dictionary<string, string>
|
||||
{
|
||||
{ "id", "myanchor" },
|
||||
|
|
@ -75,7 +76,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("href"));
|
||||
Assert.Equal("home/index", attribute.Value);
|
||||
Assert.Equal("Something", output.Content);
|
||||
Assert.Equal("a", output.TagName);
|
||||
Assert.Equal(expectedTagName, output.TagName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
public async Task ProcessAsync_GeneratesExpectedOutput()
|
||||
{
|
||||
// Arrange
|
||||
var expectedTagName = "not-form";
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var formTagHelper = new FormTagHelper
|
||||
{
|
||||
|
|
@ -40,7 +41,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{ "anti-forgery", true }
|
||||
});
|
||||
var output = new TagHelperOutput(
|
||||
"form",
|
||||
expectedTagName,
|
||||
attributes: new Dictionary<string, string>
|
||||
{
|
||||
{ "id", "myform" },
|
||||
|
|
@ -78,7 +79,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("action"));
|
||||
Assert.Equal("home/index", attribute.Value);
|
||||
Assert.Equal(expectedContent, output.Content);
|
||||
Assert.Equal("form", output.TagName);
|
||||
Assert.Equal(expectedTagName, output.TagName);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{ "value", expectedValue },
|
||||
};
|
||||
var expectedContent = "original content";
|
||||
var expectedTagName = "input";
|
||||
var expectedTagName = "not-input";
|
||||
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{
|
||||
{ "class", "form-control" },
|
||||
};
|
||||
var output = new TagHelperOutput("original tag name", htmlAttributes, expectedContent)
|
||||
var output = new TagHelperOutput(expectedTagName, htmlAttributes, expectedContent)
|
||||
{
|
||||
SelfClosing = false,
|
||||
};
|
||||
|
|
@ -145,7 +145,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{ "value", "2014-10-15T23:24:19.000-7:00" },
|
||||
};
|
||||
var expectedContent = "original content";
|
||||
var expectedTagName = "original tag name";
|
||||
var expectedTagName = "input";
|
||||
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
TagHelperOutputContent tagHelperOutputContent)
|
||||
{
|
||||
// Arrange
|
||||
var expectedTagName = "not-label";
|
||||
var expectedAttributes = new Dictionary<string, string>
|
||||
{
|
||||
{ "class", "form-control" },
|
||||
|
|
@ -117,8 +118,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{
|
||||
{ "class", "form-control" },
|
||||
};
|
||||
var output = new TagHelperOutput("A random tag name", htmlAttributes, tagHelperOutputContent.OriginalContent);
|
||||
var expectedTagName = "label";
|
||||
var output = new TagHelperOutput(expectedTagName, htmlAttributes, tagHelperOutputContent.OriginalContent);
|
||||
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
|
||||
var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
|
||||
tagHelper.ViewContext = viewContext;
|
||||
|
|
@ -143,7 +143,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{ "class", "form-control" },
|
||||
};
|
||||
var expectedContent = "original content";
|
||||
var expectedTagName = "original tag name";
|
||||
var expectedTagName = "label";
|
||||
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{
|
||||
{ "label", "my-label" },
|
||||
};
|
||||
var originalTagName = "not-option";
|
||||
var expectedTagName = "not-option";
|
||||
|
||||
var contextAttributes = new Dictionary<string, object>
|
||||
{
|
||||
|
|
@ -137,7 +137,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{ "value", value },
|
||||
};
|
||||
var tagHelperContext = new TagHelperContext(contextAttributes);
|
||||
var output = new TagHelperOutput(originalTagName, originalAttributes, originalContent)
|
||||
var output = new TagHelperOutput(expectedTagName, originalAttributes, originalContent)
|
||||
{
|
||||
SelfClosing = false,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -167,7 +167,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{ "class", "form-control" },
|
||||
};
|
||||
var originalContent = "original content";
|
||||
var originalTagName = "not-select";
|
||||
|
||||
var expectedAttributes = new Dictionary<string, string>(originalAttributes)
|
||||
{
|
||||
|
|
@ -176,7 +175,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{ "valid", "from validation attributes" },
|
||||
};
|
||||
var expectedContent = originalContent;
|
||||
var expectedTagName = "select";
|
||||
var expectedTagName = "not-select";
|
||||
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
|
|
@ -185,7 +184,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
var modelExpression = new ModelExpression(nameAndId.Name, metadata);
|
||||
|
||||
var tagHelperContext = new TagHelperContext(new Dictionary<string, object>());
|
||||
var output = new TagHelperOutput(originalTagName, originalAttributes, expectedContent)
|
||||
var output = new TagHelperOutput(expectedTagName, originalAttributes, expectedContent)
|
||||
{
|
||||
SelfClosing = true,
|
||||
};
|
||||
|
|
@ -238,7 +237,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{ "class", "form-control" },
|
||||
};
|
||||
var originalContent = "original content";
|
||||
var originalTagName = "not-select";
|
||||
|
||||
var expectedAttributes = new Dictionary<string, string>(originalAttributes)
|
||||
{
|
||||
|
|
@ -256,7 +254,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
var modelExpression = new ModelExpression(nameAndId.Name, metadata);
|
||||
|
||||
var tagHelperContext = new TagHelperContext(new Dictionary<string, object>());
|
||||
var output = new TagHelperOutput(originalTagName, originalAttributes, originalContent)
|
||||
var output = new TagHelperOutput(expectedTagName, originalAttributes, originalContent)
|
||||
{
|
||||
SelfClosing = true,
|
||||
};
|
||||
|
|
@ -313,10 +311,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
var originalAttributes = new Dictionary<string, string>();
|
||||
var content = "original content";
|
||||
var propertyName = "Property1";
|
||||
var tagName = "not-select";
|
||||
var expectedTagName = "select";
|
||||
|
||||
var tagHelperContext = new TagHelperContext(contextAttributes);
|
||||
var output = new TagHelperOutput(tagName, originalAttributes, content);
|
||||
var output = new TagHelperOutput(expectedTagName, originalAttributes, content);
|
||||
|
||||
// TODO: In real (model => model) scenario, ModelExpression should have name "" and
|
||||
// TemplateInfo.HtmlFieldPrefix should be "Property1" but empty ModelExpression name is not currently
|
||||
|
|
@ -376,7 +374,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
var originalAttributes = new Dictionary<string, string>();
|
||||
var content = "original content";
|
||||
var propertyName = "Property1";
|
||||
var tagName = "not-select";
|
||||
var tagName = "select";
|
||||
|
||||
var tagHelperContext = new TagHelperContext(contextAttributes);
|
||||
var output = new TagHelperOutput(tagName, originalAttributes, content);
|
||||
|
|
@ -440,7 +438,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
var expectedAttributes = new Dictionary<string, string>(originalAttributes);
|
||||
expectedAttributes[attributeName] = (string)contextAttributes[attributeName];
|
||||
var expectedContent = "original content";
|
||||
var expectedTagName = "not-select";
|
||||
var expectedTagName = "select";
|
||||
|
||||
var tagHelperContext = new TagHelperContext(contextAttributes);
|
||||
var output = new TagHelperOutput(expectedTagName, originalAttributes, expectedContent)
|
||||
|
|
@ -470,11 +468,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
var contextAttributes = new Dictionary<string, object>();
|
||||
var originalAttributes = new Dictionary<string, string>();
|
||||
var content = "original content";
|
||||
var tagName = "not-select";
|
||||
var expectedTagName = "select";
|
||||
var expectedMessage = "Cannot determine body for <select>. 'items' must be null if 'for' is null.";
|
||||
|
||||
var tagHelperContext = new TagHelperContext(contextAttributes);
|
||||
var output = new TagHelperOutput(tagName, originalAttributes, content);
|
||||
var output = new TagHelperOutput(expectedTagName, originalAttributes, content);
|
||||
var tagHelper = new SelectTagHelper
|
||||
{
|
||||
Items = Enumerable.Empty<SelectListItem>(),
|
||||
|
|
@ -500,12 +498,12 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
var contextAttributes = new Dictionary<string, object>();
|
||||
var originalAttributes = new Dictionary<string, string>();
|
||||
var content = "original content";
|
||||
var tagName = "not-select";
|
||||
var expectedTagName = "select";
|
||||
var expectedMessage = "Cannot parse 'multiple' value '" + multiple +
|
||||
"' for <select>. Acceptable values are 'false', 'true' and 'multiple'.";
|
||||
|
||||
var tagHelperContext = new TagHelperContext(contextAttributes);
|
||||
var output = new TagHelperOutput(tagName, originalAttributes, content);
|
||||
var output = new TagHelperOutput(expectedTagName, originalAttributes, content);
|
||||
|
||||
var metadataProvider = new EmptyModelMetadataProvider();
|
||||
string model = null;
|
||||
|
|
|
|||
|
|
@ -275,34 +275,5 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("for"));
|
||||
Assert.Equal(expectedBuilderAttribute.Value, attribute.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Merge_CombinesAllTagHelperOutputAndTagBuilderProperties()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperOutput = new TagHelperOutput(
|
||||
"p",
|
||||
attributes: new Dictionary<string, string>(),
|
||||
content: "Hello from tagHelperOutput");
|
||||
var expectedOutputAttribute = new KeyValuePair<string, string>("class", "btn");
|
||||
tagHelperOutput.Attributes.Add(expectedOutputAttribute);
|
||||
|
||||
var tagBuilder = new TagBuilder("div");
|
||||
var expectedBuilderAttribute = new KeyValuePair<string, string>("for", "hello");
|
||||
tagBuilder.Attributes.Add(expectedBuilderAttribute);
|
||||
tagBuilder.InnerHtml = "Hello from tagBuilder.";
|
||||
|
||||
// Act
|
||||
tagHelperOutput.Merge(tagBuilder);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("div", tagHelperOutput.TagName);
|
||||
Assert.Equal("Hello from tagHelperOutputHello from tagBuilder.", tagHelperOutput.Content);
|
||||
Assert.Equal(tagHelperOutput.Attributes.Count, 2);
|
||||
var attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("class"));
|
||||
Assert.Equal(expectedOutputAttribute.Value, attribute.Value);
|
||||
attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("for"));
|
||||
Assert.Equal(expectedBuilderAttribute.Value, attribute.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{ "name", nameAndId.Name },
|
||||
{ "valid", "from validation attributes" },
|
||||
};
|
||||
var expectedTagName = "textarea";
|
||||
var expectedTagName = "not-textarea";
|
||||
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{
|
||||
{ "class", "form-control" },
|
||||
};
|
||||
var output = new TagHelperOutput("original tag name", htmlAttributes, "original content")
|
||||
var output = new TagHelperOutput(expectedTagName, htmlAttributes, "original content")
|
||||
{
|
||||
SelfClosing = true,
|
||||
};
|
||||
|
|
@ -149,7 +149,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{ "class", "form-control" },
|
||||
};
|
||||
var expectedContent = "original content";
|
||||
var expectedTagName = "original tag name";
|
||||
var expectedTagName = "textarea";
|
||||
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
public async Task ProcessAsync_GeneratesExpectedOutput()
|
||||
{
|
||||
// Arrange
|
||||
var expectedTagName = "not-span";
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var modelExpression = CreateModelExpression("Name");
|
||||
var validationMessageTagHelper = new ValidationMessageTagHelper
|
||||
|
|
@ -34,7 +35,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{ "for", modelExpression },
|
||||
});
|
||||
var output = new TagHelperOutput(
|
||||
"original tag name",
|
||||
expectedTagName,
|
||||
attributes: new Dictionary<string, string>
|
||||
{
|
||||
{ "id", "myvalidationmessage" }
|
||||
|
|
@ -61,7 +62,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-valmsg-replace"));
|
||||
Assert.Equal("true", attribute.Value);
|
||||
Assert.Equal("Something", output.Content);
|
||||
Assert.Equal("original tag name", output.TagName);
|
||||
Assert.Equal(expectedTagName, output.TagName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -134,7 +135,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
await validationMessageTagHelper.ProcessAsync(context: null, output: output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(output.TagName, "span");
|
||||
Assert.Equal("span", output.TagName);
|
||||
Assert.Equal(2, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-foo"));
|
||||
Assert.Equal("bar", attribute.Value);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
public async Task ProcessAsync_GeneratesExpectedOutput()
|
||||
{
|
||||
// Arrange
|
||||
var expectedTagName = "not-div";
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var validationSummaryTagHelper = new ValidationSummaryTagHelper
|
||||
{
|
||||
|
|
@ -29,7 +30,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
|
||||
var tagHelperContext = new TagHelperContext(new Dictionary<string, object>());
|
||||
var output = new TagHelperOutput(
|
||||
"div",
|
||||
expectedTagName,
|
||||
attributes: new Dictionary<string, string>
|
||||
{
|
||||
{ "class", "form-control" }
|
||||
|
|
@ -53,7 +54,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
Assert.Equal("true", attribute.Value);
|
||||
Assert.Equal("Custom Content<ul><li style=\"display:none\"></li>" + Environment.NewLine + "</ul>",
|
||||
output.Content);
|
||||
Assert.Equal("div", output.TagName);
|
||||
Assert.Equal(expectedTagName, output.TagName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -124,7 +125,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
await validationSummaryTagHelper.ProcessAsync(context: null, output: output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(output.TagName, "div");
|
||||
Assert.Equal("div", output.TagName);
|
||||
Assert.Equal(3, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-foo"));
|
||||
Assert.Equal("bar", attribute.Value);
|
||||
|
|
|
|||
Loading…
Reference in New Issue