// 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 Microsoft.AspNetCore.Mvc.ViewFeatures; using Xunit; namespace Microsoft.AspNetCore.Mvc.Rendering { public class HtmlHelperTest { public static TheoryData> IgnoreCaseTestData { get { return new TheoryData> { { new { selected = true, SeLeCtEd = false }, new KeyValuePair("selected", false) }, { new { SeLeCtEd = false, selected = true }, new KeyValuePair("SeLeCtEd", true) }, { new { SelECTeD = false, SeLECTED = true }, new KeyValuePair("SelECTeD", true) } }; } } // value, expectedString public static TheoryData EncodeDynamicTestData { get { var data = new TheoryData { { null, string.Empty }, // Dynamic implementation calls the string overload when possible. { string.Empty, string.Empty }, { "<\">", "HtmlEncode[[<\">]]" }, { "
", "HtmlEncode[[
]]" }, { "bold", "HtmlEncode[[bold]]" }, { new ObjectWithToStringOverride(), "HtmlEncode[[boldFromObject]]" }, }; return data; } } // value, expectedString public static TheoryData EncodeObjectTestData { get { var data = new TheoryData { { null, string.Empty }, { string.Empty, string.Empty }, { "<\">", "HtmlEncode[[<\">]]" }, { "
", "HtmlEncode[[
]]" }, { "bold", "HtmlEncode[[bold]]" }, { new ObjectWithToStringOverride(), "HtmlEncode[[boldFromObject]]" }, }; return data; } } // value, expectedString public static TheoryData EncodeStringTestData { get { return new TheoryData { { null, string.Empty }, // String overload does not encode the empty string. { string.Empty, string.Empty }, { "<\">", "HtmlEncode[[<\">]]" }, { "
", "HtmlEncode[[
]]" }, { "bold", "HtmlEncode[[bold]]" }, }; } } // value, expectedString public static TheoryData RawObjectTestData { get { var data = new TheoryData { { new ObjectWithToStringOverride(), "boldFromObject" }, }; foreach (var item in RawStringTestData) { data.Add(item[0], (string)item[1]); } return data; } } // value, expectedString public static TheoryData RawStringTestData { get { return new TheoryData { { null, string.Empty }, { string.Empty, string.Empty }, { "<\">", "<\">" }, { "
", "
" }, { "bold", "bold" }, }; } } [Theory] [MemberData(nameof(IgnoreCaseTestData))] public void AnonymousObjectToHtmlAttributes_IgnoresPropertyCase( object htmlAttributeObject, KeyValuePair expectedEntry) { // Act var result = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributeObject); // Assert var entry = Assert.Single(result); Assert.Equal(expectedEntry, entry); } [Theory] [MemberData(nameof(EncodeDynamicTestData))] public void EncodeDynamic_ReturnsExpectedString(object value, string expectedString) { // Arrange // Important to preserve these particular variable types. Otherwise may end up testing different runtime // (not compiler) behaviors. dynamic dynamicValue = value; IHtmlHelper helper = DefaultTemplatesUtilities.GetHtmlHelper(); // Act var result = helper.Encode(dynamicValue); // Assert Assert.Equal(expectedString, result); } [Theory] [MemberData(nameof(EncodeDynamicTestData))] public void EncodeDynamic_ReturnsExpectedString_WithBaseHelper(object value, string expectedString) { // Arrange // Important to preserve these particular variable types. Otherwise may end up testing different runtime // (not compiler) behaviors. dynamic dynamicValue = value; IHtmlHelper helper = DefaultTemplatesUtilities.GetHtmlHelper(); // Act var result = helper.Encode(dynamicValue); // Assert Assert.Equal(expectedString, result); } [Theory] [MemberData(nameof(EncodeObjectTestData))] public void EncodeObject_ReturnsExpectedString(object value, string expectedString) { // Arrange // Important to preserve this particular variable type and the (object) type of the value parameter. // Otherwise may end up testing different runtime (not compiler) behaviors. IHtmlHelper helper = DefaultTemplatesUtilities.GetHtmlHelper(); // Act var result = helper.Encode(value); // Assert Assert.Equal(expectedString, result); } [Theory] [MemberData(nameof(EncodeStringTestData))] public void EncodeString_ReturnsExpectedString(string value, string expectedString) { // Arrange // Important to preserve this particular variable type and the (string) type of the value parameter. // Otherwise may end up testing different runtime (not compiler) behaviors. IHtmlHelper helper = DefaultTemplatesUtilities.GetHtmlHelper(); // Act var result = helper.Encode(value); // Assert Assert.Equal(expectedString, result); } [Theory] [MemberData(nameof(RawObjectTestData))] public void RawDynamic_ReturnsExpectedString(object value, string expectedString) { // Arrange // Important to preserve these particular variable types. Otherwise may end up testing different runtime // (not compiler) behaviors. dynamic dynamicValue = value; IHtmlHelper helper = DefaultTemplatesUtilities.GetHtmlHelper(); // Act var result = helper.Raw(dynamicValue); // Assert Assert.Equal(expectedString, result.ToString()); } [Theory] [MemberData(nameof(RawObjectTestData))] public void RawDynamic_ReturnsExpectedString_WithBaseHelper(object value, string expectedString) { // Arrange // Important to preserve these particular variable types. Otherwise may end up testing different runtime // (not compiler) behaviors. dynamic dynamicValue = value; IHtmlHelper helper = DefaultTemplatesUtilities.GetHtmlHelper(); // Act var result = helper.Raw(dynamicValue); // Assert Assert.Equal(expectedString, result.ToString()); } [Theory] [MemberData(nameof(RawObjectTestData))] public void RawObject_ReturnsExpectedString(object value, string expectedString) { // Arrange // Important to preserve this particular variable type and the (object) type of the value parameter. // Otherwise may end up testing different runtime (not compiler) behaviors. IHtmlHelper helper = DefaultTemplatesUtilities.GetHtmlHelper(); // Act var result = helper.Raw(value); // Assert Assert.Equal(expectedString, result.ToString()); } [Theory] [MemberData(nameof(RawStringTestData))] public void RawString_ReturnsExpectedString(string value, string expectedString) { // Arrange // Important to preserve this particular variable type and the (string) type of the value parameter. // Otherwise may end up testing different runtime (not compiler) behaviors. IHtmlHelper helper = DefaultTemplatesUtilities.GetHtmlHelper(); // Act var result = helper.Raw(value); // Assert Assert.Equal(expectedString, result.ToString()); } [Fact] public void Contextualize_WorksWithCovariantViewDataDictionary() { // Arrange var helperToContextualize = DefaultTemplatesUtilities .GetHtmlHelper(model: null); var viewContext = DefaultTemplatesUtilities .GetHtmlHelper(model: null) .ViewContext; // Act helperToContextualize.Contextualize(viewContext); // Assert Assert.IsType>( helperToContextualize.ViewData); Assert.Same(helperToContextualize.ViewContext, viewContext); } [Fact] public void Contextualize_ThrowsIfViewDataDictionariesAreNotCompatible() { // Arrange var helperToContextualize = DefaultTemplatesUtilities .GetHtmlHelper(model: null); var viewContext = DefaultTemplatesUtilities .GetHtmlHelper(model: null) .ViewContext; var expectedMessage = $"Property '{nameof(ViewContext.ViewData)}' is of type " + $"'{typeof(ViewDataDictionary).FullName}'," + $" but this method requires a value of type '{typeof(ViewDataDictionary).FullName}'."; // Act & Assert var exception = Assert.Throws("viewContext", () => helperToContextualize.Contextualize(viewContext)); Assert.Contains(expectedMessage, exception.Message); } [Fact] public void Contextualize_ThrowsForNonGenericViewDataDictionaries() { // Arrange var helperToContextualize = DefaultTemplatesUtilities .GetHtmlHelper(model: null); var viewContext = DefaultTemplatesUtilities .GetHtmlHelper(model: null) .ViewContext; viewContext.ViewData = new ViewDataDictionary(viewContext.ViewData); var expectedMessage = $"Property '{nameof(ViewContext.ViewData)}' is of type " + $"'{typeof(ViewDataDictionary).FullName}'," + $" but this method requires a value of type '{typeof(ViewDataDictionary).FullName}'."; // Act & Assert var exception = Assert.Throws("viewContext", () => helperToContextualize.Contextualize(viewContext)); Assert.Contains(expectedMessage, exception.Message); } private class BaseModel { public string Name { get; set; } } private class DerivedModel : BaseModel { } private class NonDerivedModel { } [Theory] [InlineData("SomeName", "SomeName")] [InlineData("Obj1.Prop1", "Obj1_Prop1")] [InlineData("Obj1.Prop1[0]", "Obj1_Prop1_0_")] [InlineData("Obj1.Prop1[0].Prop2", "Obj1_Prop1_0__Prop2")] public void GenerateIdFromName_ReturnsExpectedValues(string fullname, string expected) { // Arrange var helper = DefaultTemplatesUtilities.GetHtmlHelper(); // Act var result = helper.GenerateIdFromName(fullname); // Assert Assert.Equal(expected, result); } [Theory] [InlineData(FormMethod.Get, "get")] [InlineData(FormMethod.Post, "post")] [InlineData((FormMethod)42, "post")] public void GetFormMethodString_ReturnsExpectedValues(FormMethod method, string expected) { // Act var result = HtmlHelper.GetFormMethodString(method); // Assert Assert.Equal(expected, result); } [Theory] [InlineData("-{0}-", "-boldFromObject-")] [InlineData("-%{0}%-", "-%boldFromObject%-")] [InlineData("-=={0}=={0}==-", "-==boldFromObject==boldFromObject==-")] public void FormatValue_ReturnsExpectedValues(string format, string expected) { // Arrange var value = new ObjectWithToStringOverride(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(); // Act var result = helper.FormatValue(value, format); // Assert Assert.Equal(expected, result); } private class ObjectWithToStringOverride { public override string ToString() { return "boldFromObject"; } } } }