Change TagBuilder Attributes and HTML Helper dependencies to be case insensitive.
- This involved adding the StringComparer.OrdinalIgnoreCase comparer to the TagBuilder's Attributes dictionary. - Added tests to validate that all methods that made use of TagBuilder.Attributes abide by the new ignore case mechanic. - Added two sets of tests to validate the new functionality of Object => Dictionary HTML helper tests. - Modified a functional test that utilizes HTML Helpers to provide same attribute-different case objects. - Fixed existing HTML helper tests to account for new ordering of attrbutes (dictionary no longer adds key value pairs, it sets them). #1328
This commit is contained in:
parent
1e3828eb7d
commit
09af7bb77b
|
|
@ -53,7 +53,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
{
|
||||
foreach (var helper in PropertyHelper.GetProperties(value))
|
||||
{
|
||||
dictionary.Add(helper.Name, helper.GetValue(value));
|
||||
dictionary[helper.Name] = helper.GetValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
{
|
||||
foreach (var helper in HtmlAttributePropertyHelper.GetProperties(htmlAttributes))
|
||||
{
|
||||
dictionary.Add(helper.Name, helper.GetValue(htmlAttributes));
|
||||
dictionary[helper.Name] = helper.GetValue(htmlAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
}
|
||||
|
||||
TagName = tagName;
|
||||
Attributes = new SortedDictionary<string, string>(StringComparer.Ordinal);
|
||||
Attributes = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public IDictionary<string, string> Attributes { get; private set; }
|
||||
|
|
@ -110,7 +110,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
foreach (var attribute in Attributes)
|
||||
{
|
||||
var key = attribute.Key;
|
||||
if (string.Equals(key, "id", StringComparison.Ordinal) && string.IsNullOrEmpty(attribute.Value))
|
||||
if (string.Equals(key, "id", StringComparison.OrdinalIgnoreCase) &&
|
||||
string.IsNullOrEmpty(attribute.Value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Internal
|
||||
{
|
||||
public class TypeHelperTest
|
||||
{
|
||||
public static TheoryData<object, KeyValuePair<string, object>> IgnoreCaseTestData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<object, KeyValuePair<string, object>>
|
||||
{
|
||||
{
|
||||
new
|
||||
{
|
||||
selected = true,
|
||||
SeLeCtEd = false
|
||||
},
|
||||
new KeyValuePair<string, object>("selected", false)
|
||||
},
|
||||
{
|
||||
new
|
||||
{
|
||||
SeLeCtEd = false,
|
||||
selected = true
|
||||
},
|
||||
new KeyValuePair<string, object>("SeLeCtEd", true)
|
||||
},
|
||||
{
|
||||
new
|
||||
{
|
||||
SelECTeD = false,
|
||||
SeLECTED = true
|
||||
},
|
||||
new KeyValuePair<string, object>("SelECTeD", true)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(IgnoreCaseTestData))]
|
||||
public void ObjectToDictionary_IgnoresPropertyCase(object testObject,
|
||||
KeyValuePair<string, object> expectedEntry)
|
||||
{
|
||||
// Act
|
||||
var result = TypeHelper.ObjectToDictionary(testObject);
|
||||
|
||||
// Assert
|
||||
var entry = Assert.Single(result);
|
||||
Assert.Equal(expectedEntry, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -131,9 +131,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
public void CheckBoxReplacesUnderscoresInHtmlAttributesWithDashes()
|
||||
{
|
||||
// Arrange
|
||||
var expected = @"<input Property1-Property3=""Property3ObjValue"" checked=""checked"" id=""Property1"" " +
|
||||
@"name=""Property1"" type=""checkbox"" value=""true"" />" +
|
||||
@"<input name=""Property1"" type=""hidden"" value=""false"" />";
|
||||
var expected = @"<input checked=""checked"" id=""Property1"" name=""Property1"" " +
|
||||
@"Property1-Property3=""Property3ObjValue"" type=""checkbox"" value=""true"" /><input " +
|
||||
@"name=""Property1"" type=""hidden"" value=""false"" />";
|
||||
var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData());
|
||||
var htmlAttributes = new { Property1_Property3 = "Property3ObjValue" };
|
||||
|
||||
|
|
@ -148,8 +148,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
public void CheckBoxWithPrefix_ReplaceDotsInIdByDefaultWithUnderscores()
|
||||
{
|
||||
// Arrange
|
||||
var expected = @"<input Property3=""Property3Value"" id=""MyPrefix_Property1"" " +
|
||||
@"name=""MyPrefix.Property1"" type=""checkbox"" value=""true"" /><input " +
|
||||
var expected = @"<input id=""MyPrefix_Property1"" name=""MyPrefix.Property1"" " +
|
||||
@"Property3=""Property3Value"" type=""checkbox"" value=""true"" /><input " +
|
||||
@"name=""MyPrefix.Property1"" type=""hidden"" value=""false"" />";
|
||||
var dictionary = new RouteValueDictionary(new { Property3 = "Property3Value" });
|
||||
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
|
||||
|
|
@ -166,8 +166,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
public void CheckBoxWithPrefix_ReplacesDotsInIdWithIdDotReplacement()
|
||||
{
|
||||
// Arrange
|
||||
var expected = @"<input Property3=""Property3Value"" id=""MyPrefix!!!Property1"" " +
|
||||
@"name=""MyPrefix.Property1"" type=""checkbox"" value=""true"" /><input " +
|
||||
var expected = @"<input id=""MyPrefix!!!Property1"" name=""MyPrefix.Property1"" " +
|
||||
@"Property3=""Property3Value"" type=""checkbox"" value=""true"" /><input " +
|
||||
@"name=""MyPrefix.Property1"" type=""hidden"" value=""false"" />";
|
||||
var dictionary = new Dictionary<string, object> { { "Property3", "Property3Value" } };
|
||||
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
|
||||
|
|
@ -185,7 +185,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
public void CheckBoxWithPrefixAndEmptyName()
|
||||
{
|
||||
// Arrange
|
||||
var expected = @"<input Property3=""Property3Value"" id=""MyPrefix"" name=""MyPrefix"" " +
|
||||
var expected = @"<input id=""MyPrefix"" name=""MyPrefix"" Property3=""Property3Value"" " +
|
||||
@"type=""checkbox"" value=""true"" /><input name=""MyPrefix"" type=""hidden"" " +
|
||||
@"value=""false"" />";
|
||||
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model: false);
|
||||
|
|
@ -288,9 +288,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
public void CheckBoxForWithObjectAttributeWithUnderscores()
|
||||
{
|
||||
// Arrange
|
||||
var expected = @"<input Property1-Property3=""Property3ObjValue"" checked=""checked"" id=""Property1"" " +
|
||||
@"name=""Property1"" type=""checkbox"" value=""true"" />" +
|
||||
@"<input name=""Property1"" type=""hidden"" value=""false"" />";
|
||||
var expected = @"<input checked=""checked"" id=""Property1"" name=""Property1"" " +
|
||||
@"Property1-Property3=""Property3ObjValue"" type=""checkbox"" value=""true"" /><input " +
|
||||
@"name=""Property1"" type=""hidden"" value=""false"" />";
|
||||
var helper = DefaultTemplatesUtilities.GetHtmlHelperForViewData(GetTestModelViewData());
|
||||
var htmlAttributes = new { Property1_Property3 = "Property3ObjValue" };
|
||||
|
||||
|
|
@ -305,9 +305,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
public void CheckBoxForWithAttributeDictionary()
|
||||
{
|
||||
// Arrange
|
||||
var expected = @"<input Property3=""Property3Value"" checked=""checked"" id=""Property1"" " +
|
||||
@"name=""Property1"" type=""checkbox"" value=""true"" /><input name=""Property1"" " +
|
||||
@"type=""hidden"" value=""false"" />";
|
||||
var expected = @"<input checked=""checked"" id=""Property1"" name=""Property1"" " +
|
||||
@"Property3=""Property3Value"" type=""checkbox"" value=""true"" /><input " +
|
||||
@"name=""Property1"" type=""hidden"" value=""false"" />";
|
||||
var helper = DefaultTemplatesUtilities.GetHtmlHelperForViewData(GetTestModelViewData());
|
||||
var attributes = new Dictionary<string, object> { { "Property3", "Property3Value" } };
|
||||
|
||||
|
|
@ -322,7 +322,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
public void CheckBoxForWithPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var expected = @"<input Property3=""PropValue"" id=""MyPrefix_Property1"" name=""MyPrefix.Property1"" " +
|
||||
var expected = @"<input id=""MyPrefix_Property1"" name=""MyPrefix.Property1"" Property3=""PropValue"" " +
|
||||
@"type=""checkbox"" value=""true"" /><input name=""MyPrefix.Property1"" type=""hidden"" " +
|
||||
@"value=""false"" />";
|
||||
var helper = DefaultTemplatesUtilities.GetHtmlHelperForViewData(GetTestModelViewData());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
{
|
||||
public class HtmlHelperTest
|
||||
{
|
||||
public static TheoryData<object, KeyValuePair<string, object>> IgnoreCaseTestData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<object, KeyValuePair<string, object>>
|
||||
{
|
||||
{
|
||||
new
|
||||
{
|
||||
selected = true,
|
||||
SeLeCtEd = false
|
||||
},
|
||||
new KeyValuePair<string, object>("selected", false)
|
||||
},
|
||||
{
|
||||
new
|
||||
{
|
||||
SeLeCtEd = false,
|
||||
selected = true
|
||||
},
|
||||
new KeyValuePair<string, object>("SeLeCtEd", true)
|
||||
},
|
||||
{
|
||||
new
|
||||
{
|
||||
SelECTeD = false,
|
||||
SeLECTED = true
|
||||
},
|
||||
new KeyValuePair<string, object>("SelECTeD", true)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(IgnoreCaseTestData))]
|
||||
public void AnonymousObjectToHtmlAttributes_IgnoresPropertyCase(object htmlAttributeObject,
|
||||
KeyValuePair<string, object> expectedEntry)
|
||||
{
|
||||
// Act
|
||||
var result = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributeObject);
|
||||
|
||||
// Assert
|
||||
var entry = Assert.Single(result);
|
||||
Assert.Equal(expectedEntry, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Rendering
|
||||
{
|
||||
public class TagBuilderTest
|
||||
{
|
||||
public static TheoryData<TagRenderMode, string> RenderingTestingData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<TagRenderMode, string>
|
||||
{
|
||||
{ TagRenderMode.StartTag, "<p>" },
|
||||
{ TagRenderMode.SelfClosing, "<p />" },
|
||||
{ TagRenderMode.Normal, "<p></p>" }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, "Hello", "World")]
|
||||
[InlineData(true, "Hello", "something else")]
|
||||
public void MergeAttribute_IgnoresCase(bool replaceExisting, string expectedKey, string expectedValue)
|
||||
{
|
||||
// Arrange
|
||||
var tagBuilder = new TagBuilder("p");
|
||||
tagBuilder.Attributes.Add("Hello", "World");
|
||||
|
||||
// Act
|
||||
tagBuilder.MergeAttribute("hello", "something else", replaceExisting);
|
||||
|
||||
// Assert
|
||||
var attribute = Assert.Single(tagBuilder.Attributes);
|
||||
Assert.Equal(new KeyValuePair<string, string>(expectedKey, expectedValue), attribute);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddCssClass_IgnoresCase()
|
||||
{
|
||||
// Arrange
|
||||
var tagBuilder = new TagBuilder("p");
|
||||
tagBuilder.Attributes.Add("ClaSs", "btn");
|
||||
|
||||
// Act
|
||||
tagBuilder.AddCssClass("success");
|
||||
|
||||
// Assert
|
||||
var attribute = Assert.Single(tagBuilder.Attributes);
|
||||
Assert.Equal(new KeyValuePair<string, string>("ClaSs", "success btn"), attribute);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateId_IgnoresCase()
|
||||
{
|
||||
// Arrange
|
||||
var tagBuilder = new TagBuilder("p");
|
||||
tagBuilder.Attributes.Add("ID", "something");
|
||||
|
||||
// Act
|
||||
tagBuilder.GenerateId("else", idAttributeDotReplacement: "-");
|
||||
|
||||
// Assert
|
||||
var attribute = Assert.Single(tagBuilder.Attributes);
|
||||
Assert.Equal(new KeyValuePair<string, string>("ID", "something"), attribute);
|
||||
}
|
||||
|
||||
[MemberData(nameof(RenderingTestingData))]
|
||||
public void ToString_IgnoresIdAttributeCase(TagRenderMode renderingMode, string expectedOutput)
|
||||
{
|
||||
// Arrange
|
||||
var tagBuilder = new TagBuilder("p");
|
||||
|
||||
// An empty value id attribute should not be rendered via ToString.
|
||||
tagBuilder.Attributes.Add("ID", string.Empty);
|
||||
|
||||
// Act
|
||||
var value = tagBuilder.ToString(renderingMode);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedOutput, value);
|
||||
}
|
||||
|
||||
[MemberData(nameof(RenderingTestingData))]
|
||||
public void ToHtmlString_IgnoresIdAttributeCase(TagRenderMode renderingMode, string expectedOutput)
|
||||
{
|
||||
// Arrange
|
||||
var tagBuilder = new TagBuilder("p");
|
||||
|
||||
// An empty value id attribute should not be rendered via ToHtmlString.
|
||||
tagBuilder.Attributes.Add("ID", string.Empty);
|
||||
|
||||
// Act
|
||||
var value = tagBuilder.ToHtmlString(renderingMode);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedOutput, value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,9 +15,9 @@
|
|||
<hr />
|
||||
@Html.ValidationSummary(true)
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
|
||||
@Html.LabelFor(m => m.UserName, new { @class = "col-md-2", ClAsS = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
|
||||
@Html.TextBoxFor(m => m.UserName, new { @class = "...", cLass = "form-control" })
|
||||
@Html.ValidationMessageFor(m => m.UserName)
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue