Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

1175 lines
49 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Web.Routing;
using Microsoft.Web.UnitTestUtil;
using Moq;
using Xunit;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Web.Mvc.Test
{
public class HtmlHelperTest
{
public static readonly RouteValueDictionary AttributesDictionary = new RouteValueDictionary(new { baz = "BazValue" });
public static readonly object AttributesObjectDictionary = new { baz = "BazObjValue" };
public static readonly object AttributesObjectUnderscoresDictionary = new { foo_baz = "BazObjValue" };
// Constructor
[Fact]
public void ConstructorGuardClauses()
{
// Arrange
var viewContext = new Mock<ViewContext>().Object;
var viewDataContainer = MvcHelper.GetViewDataContainer(null);
// Act & Assert
Assert.ThrowsArgumentNull(
() => new HtmlHelper(null, viewDataContainer),
"viewContext"
);
Assert.ThrowsArgumentNull(
() => new HtmlHelper(viewContext, null),
"viewDataContainer"
);
Assert.ThrowsArgumentNull(
() => new HtmlHelper(viewContext, viewDataContainer, null),
"routeCollection"
);
}
[Fact]
public void PropertiesAreSet()
{
// Arrange
var viewContext = new Mock<ViewContext>().Object;
var viewData = new ViewDataDictionary<String>("The Model");
var routes = new RouteCollection();
var mockViewDataContainer = new Mock<IViewDataContainer>();
mockViewDataContainer.Setup(vdc => vdc.ViewData).Returns(viewData);
// Act
var htmlHelper = new HtmlHelper(viewContext, mockViewDataContainer.Object, routes);
// Assert
Assert.Equal(viewContext, htmlHelper.ViewContext);
Assert.Equal(mockViewDataContainer.Object, htmlHelper.ViewDataContainer);
Assert.Equal(routes, htmlHelper.RouteCollection);
Assert.Equal(viewData.Model, htmlHelper.ViewData.Model);
}
[Fact]
public void DefaultRouteCollectionIsRouteTableRoutes()
{
// Arrange
var viewContext = new Mock<ViewContext>().Object;
var viewDataContainer = new Mock<IViewDataContainer>().Object;
// Act
var htmlHelper = new HtmlHelper(viewContext, viewDataContainer);
// Assert
Assert.Equal(RouteTable.Routes, htmlHelper.RouteCollection);
}
// AnonymousObjectToHtmlAttributes tests
[Fact]
public void ConvertsUnderscoresInNamesToDashes()
{
// Arrange
var attributes = new { foo = "Bar", baz_bif = "pow_wow" };
// Act
RouteValueDictionary result = HtmlHelper.AnonymousObjectToHtmlAttributes(attributes);
// Assert
Assert.Equal(2, result.Count);
Assert.Equal("Bar", result["foo"]);
Assert.Equal("pow_wow", result["baz-bif"]);
}
// AttributeEncode
[Fact]
public void AttributeEncodeObject()
{
// Arrange
HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper();
// Act
string encodedHtml = htmlHelper.AttributeEncode((object)@"<"">");
// Assert
Assert.Equal(encodedHtml, "&lt;&quot;>");
}
[Fact]
public void AttributeEncodeObjectNull()
{
// Arrange
HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper();
// Act
string encodedHtml = htmlHelper.AttributeEncode((object)null);
// Assert
Assert.Equal("", encodedHtml);
}
[Fact]
public void AttributeEncodeString()
{
// Arrange
HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper();
// Act
string encodedHtml = htmlHelper.AttributeEncode(@"<"">");
// Assert
Assert.Equal(encodedHtml, "&lt;&quot;>");
}
[Fact]
public void AttributeEncodeStringNull()
{
// Arrange
HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper();
// Act
string encodedHtml = htmlHelper.AttributeEncode((string)null);
// Assert
Assert.Equal("", encodedHtml);
}
// EnableClientValidation
[Fact]
public void EnableClientValidation()
{
// Arrange
var mockViewContext = new Mock<ViewContext>();
var viewDataContainer = new Mock<IViewDataContainer>().Object;
var htmlHelper = new HtmlHelper(mockViewContext.Object, viewDataContainer);
// Act
htmlHelper.EnableClientValidation();
// Act & assert
mockViewContext.VerifySet(vc => vc.ClientValidationEnabled = true);
}
// EnableUnobtrusiveJavaScript
[Fact]
public void EnableUnobtrusiveJavaScript()
{
// Arrange
var mockViewContext = new Mock<ViewContext>();
var viewDataContainer = new Mock<IViewDataContainer>().Object;
var htmlHelper = new HtmlHelper(mockViewContext.Object, viewDataContainer);
// Act
htmlHelper.EnableUnobtrusiveJavaScript();
// Act & assert
mockViewContext.VerifySet(vc => vc.UnobtrusiveJavaScriptEnabled = true);
}
// Encode
[Fact]
public void EncodeObject()
{
// Arrange
HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper();
// Act
string encodedHtml = htmlHelper.Encode((object)"<br />");
// Assert
Assert.Equal(encodedHtml, "&lt;br /&gt;");
}
[Fact]
public void EncodeObjectNull()
{
// Arrange
HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper();
// Act
string encodedHtml = htmlHelper.Encode((object)null);
// Assert
Assert.Equal("", encodedHtml);
}
[Fact]
public void EncodeString()
{
// Arrange
HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper();
// Act
string encodedHtml = htmlHelper.Encode("<br />");
// Assert
Assert.Equal(encodedHtml, "&lt;br /&gt;");
}
[Fact]
public void EncodeStringNull()
{
// Arrange
HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper();
// Act
string encodedHtml = htmlHelper.Encode((string)null);
// Assert
Assert.Equal("", encodedHtml);
}
// GetModelStateValue
[Fact]
public void GetModelStateValueReturnsNullIfModelStateHasNoValue()
{
// Arrange
ViewDataDictionary vdd = new ViewDataDictionary();
vdd.ModelState.AddModelError("foo", "some error text"); // didn't call SetModelValue()
HtmlHelper helper = new HtmlHelper(new ViewContext(), new SimpleViewDataContainer(vdd));
// Act
object retVal = helper.GetModelStateValue("foo", typeof(object));
// Assert
Assert.Null(retVal);
}
[Fact]
public void GetModelStateValueReturnsNullIfModelStateKeyNotPresent()
{
// Arrange
ViewDataDictionary vdd = new ViewDataDictionary();
HtmlHelper helper = new HtmlHelper(new ViewContext(), new SimpleViewDataContainer(vdd));
// Act
object retVal = helper.GetModelStateValue("key_not_present", typeof(object));
// Assert
Assert.Null(retVal);
}
// GenerateIdFromName
[Fact]
public void GenerateIdFromNameTests()
{
// Guard clauses
Assert.ThrowsArgumentNull(
() => HtmlHelper.GenerateIdFromName(null),
"name"
);
Assert.ThrowsArgumentNull(
() => HtmlHelper.GenerateIdFromName(null, "?"),
"name"
);
Assert.ThrowsArgumentNull(
() => HtmlHelper.GenerateIdFromName("?", null),
"idAttributeDotReplacement"
);
// Default replacement tests
Assert.Equal("", HtmlHelper.GenerateIdFromName(""));
Assert.Equal("Foo", HtmlHelper.GenerateIdFromName("Foo"));
Assert.Equal("Foo_Bar", HtmlHelper.GenerateIdFromName("Foo.Bar"));
Assert.Equal("Foo_Bar_Baz", HtmlHelper.GenerateIdFromName("Foo.Bar.Baz"));
Assert.Null(HtmlHelper.GenerateIdFromName("1Foo"));
Assert.Equal("Foo_0_", HtmlHelper.GenerateIdFromName("Foo[0]"));
// Custom replacement tests
Assert.Equal("", HtmlHelper.GenerateIdFromName("", "?"));
Assert.Equal("Foo", HtmlHelper.GenerateIdFromName("Foo", "?"));
Assert.Equal("Foo?Bar", HtmlHelper.GenerateIdFromName("Foo.Bar", "?"));
Assert.Equal("Foo?Bar?Baz", HtmlHelper.GenerateIdFromName("Foo.Bar.Baz", "?"));
Assert.Equal("FooBarBaz", HtmlHelper.GenerateIdFromName("Foo.Bar.Baz", ""));
Assert.Null(HtmlHelper.GenerateIdFromName("1Foo", "?"));
Assert.Equal("Foo?0?", HtmlHelper.GenerateIdFromName("Foo[0]", "?"));
}
// RenderPartialInternal
[Fact]
public void NullPartialViewNameThrows()
{
// Arrange
TestableHtmlHelper helper = TestableHtmlHelper.Create();
ViewDataDictionary viewData = new ViewDataDictionary();
// Act & Assert
Assert.ThrowsArgumentNullOrEmpty(
() => helper.RenderPartialInternal(null /* partialViewName */, null /* viewData */, null /* model */, TextWriter.Null),
"partialViewName");
}
[Fact]
public void EmptyPartialViewNameThrows()
{
// Arrange
TestableHtmlHelper helper = TestableHtmlHelper.Create();
ViewDataDictionary viewData = new ViewDataDictionary();
// Act & Assert
Assert.ThrowsArgumentNullOrEmpty(
() => helper.RenderPartialInternal(String.Empty /* partialViewName */, null /* viewData */, null /* model */, TextWriter.Null),
"partialViewName");
}
[Fact]
public void EngineLookupSuccessCallsRender()
{
// Arrange
TestableHtmlHelper helper = TestableHtmlHelper.Create();
TextWriter writer = helper.ViewContext.Writer;
Mock<IViewEngine> engine = new Mock<IViewEngine>(MockBehavior.Strict);
Mock<IView> view = new Mock<IView>(MockBehavior.Strict);
engine
.Setup(e => e.FindPartialView(It.IsAny<ControllerContext>(), "partial-view", It.IsAny<bool>()))
.Returns(new ViewEngineResult(view.Object, engine.Object))
.Verifiable();
view
.Setup(v => v.Render(It.IsAny<ViewContext>(), writer))
.Callback<ViewContext, TextWriter>(
(viewContext, _) =>
{
Assert.Same(helper.ViewContext.View, viewContext.View);
Assert.Same(helper.ViewContext.TempData, viewContext.TempData);
})
.Verifiable();
// Act
helper.RenderPartialInternal("partial-view", null /* viewData */, null /* model */, writer, engine.Object);
// Assert
engine.Verify();
view.Verify();
}
[Fact]
public void EngineLookupFailureThrows()
{
// Arrange
TestableHtmlHelper helper = TestableHtmlHelper.Create();
Mock<IViewEngine> engine = new Mock<IViewEngine>(MockBehavior.Strict);
engine
.Setup(e => e.FindPartialView(It.IsAny<ControllerContext>(), "partial-view", It.IsAny<bool>()))
.Returns(new ViewEngineResult(new[] { "location1", "location2" }))
.Verifiable();
// Act & Assert
Assert.Throws<InvalidOperationException>(
() => helper.RenderPartialInternal("partial-view", null /* viewData */, null /* model */, TextWriter.Null, engine.Object),
@"The partial view 'partial-view' was not found or no view engine supports the searched locations. The following locations were searched:
location1
location2");
engine.Verify();
}
[Fact]
public void RenderPartialInternalWithNullModelAndNullViewData()
{
// Arrange
object model = new object();
TestableHtmlHelper helper = TestableHtmlHelper.Create();
helper.ViewData["Foo"] = "Bar";
helper.ViewData.Model = model;
Mock<IViewEngine> engine = new Mock<IViewEngine>(MockBehavior.Strict);
Mock<IView> view = new Mock<IView>(MockBehavior.Strict);
engine
.Setup(e => e.FindPartialView(It.IsAny<ControllerContext>(), "partial-view", It.IsAny<bool>()))
.Returns(new ViewEngineResult(view.Object, engine.Object))
.Verifiable();
view
.Setup(v => v.Render(It.IsAny<ViewContext>(), TextWriter.Null))
.Callback<ViewContext, TextWriter>(
(viewContext, writer) =>
{
Assert.NotSame(helper.ViewData, viewContext.ViewData); // New view data instance
Assert.Equal("Bar", viewContext.ViewData["Foo"]); // Copy of the existing view data
Assert.Same(model, viewContext.ViewData.Model); // Keep existing model
})
.Verifiable();
// Act
helper.RenderPartialInternal("partial-view", null /* viewData */, null /* model */, TextWriter.Null, engine.Object);
// Assert
engine.Verify();
view.Verify();
}
[Fact]
public void RenderPartialInternalWithNonNullModelAndNullViewData()
{
// Arrange
object model = new object();
object newModel = new object();
TestableHtmlHelper helper = TestableHtmlHelper.Create();
helper.ViewData["Foo"] = "Bar";
helper.ViewData.Model = model;
Mock<IViewEngine> engine = new Mock<IViewEngine>(MockBehavior.Strict);
Mock<IView> view = new Mock<IView>(MockBehavior.Strict);
engine
.Setup(e => e.FindPartialView(It.IsAny<ControllerContext>(), "partial-view", It.IsAny<bool>()))
.Returns(new ViewEngineResult(view.Object, engine.Object))
.Verifiable();
view
.Setup(v => v.Render(It.IsAny<ViewContext>(), TextWriter.Null))
.Callback<ViewContext, TextWriter>(
(viewContext, writer) =>
{
Assert.NotSame(helper.ViewData, viewContext.ViewData); // New view data instance
Assert.Empty(viewContext.ViewData); // Empty (not copied)
Assert.Same(newModel, viewContext.ViewData.Model); // New model
})
.Verifiable();
// Act
helper.RenderPartialInternal("partial-view", null /* viewData */, newModel, TextWriter.Null, engine.Object);
// Assert
engine.Verify();
view.Verify();
}
[Fact]
public void RenderPartialInternalWithNullModelAndNonNullViewData()
{
// Arrange
object model = new object();
object vddModel = new object();
ViewDataDictionary vdd = new ViewDataDictionary();
vdd["Baz"] = "Biff";
vdd.Model = vddModel;
TestableHtmlHelper helper = TestableHtmlHelper.Create();
helper.ViewData["Foo"] = "Bar";
helper.ViewData.Model = model;
Mock<IViewEngine> engine = new Mock<IViewEngine>(MockBehavior.Strict);
Mock<IView> view = new Mock<IView>(MockBehavior.Strict);
engine
.Setup(e => e.FindPartialView(It.IsAny<ControllerContext>(), "partial-view", It.IsAny<bool>()))
.Returns(new ViewEngineResult(view.Object, engine.Object))
.Verifiable();
view
.Setup(v => v.Render(It.IsAny<ViewContext>(), TextWriter.Null))
.Callback<ViewContext, TextWriter>(
(viewContext, writer) =>
{
Assert.NotSame(helper.ViewData, viewContext.ViewData); // New view data instance
Assert.Single(viewContext.ViewData); // Copy of the passed view data, not original view data
Assert.Equal("Biff", viewContext.ViewData["Baz"]);
Assert.Same(vddModel, viewContext.ViewData.Model); // Keep model from passed view data, not original view data
})
.Verifiable();
// Act
helper.RenderPartialInternal("partial-view", vdd, null /* model */, TextWriter.Null, engine.Object);
// Assert
engine.Verify();
view.Verify();
}
[Fact]
public void RenderPartialInternalWithNonNullModelAndNonNullViewData()
{
// Arrange
object model = new object();
object vddModel = new object();
object newModel = new object();
ViewDataDictionary vdd = new ViewDataDictionary();
vdd["Baz"] = "Biff";
vdd.Model = vddModel;
TestableHtmlHelper helper = TestableHtmlHelper.Create();
helper.ViewData["Foo"] = "Bar";
helper.ViewData.Model = model;
Mock<IViewEngine> engine = new Mock<IViewEngine>(MockBehavior.Strict);
Mock<IView> view = new Mock<IView>(MockBehavior.Strict);
engine
.Setup(e => e.FindPartialView(It.IsAny<ControllerContext>(), "partial-view", It.IsAny<bool>()))
.Returns(new ViewEngineResult(view.Object, engine.Object))
.Verifiable();
view
.Setup(v => v.Render(It.IsAny<ViewContext>(), TextWriter.Null))
.Callback<ViewContext, TextWriter>(
(viewContext, writer) =>
{
Assert.NotSame(helper.ViewData, viewContext.ViewData); // New view data instance
Assert.Single(viewContext.ViewData); // Copy of the passed view data, not original view data
Assert.Equal("Biff", viewContext.ViewData["Baz"]);
Assert.Same(newModel, viewContext.ViewData.Model); // New model
})
.Verifiable();
// Act
helper.RenderPartialInternal("partial-view", vdd, newModel, TextWriter.Null, engine.Object);
// Assert
engine.Verify();
view.Verify();
}
// HttpMethodOverride
[Fact]
public void HttpMethodOverrideGuardClauses()
{
// Arrange
var viewContext = new Mock<ViewContext>().Object;
var viewDataContainer = MvcHelper.GetViewDataContainer(null);
var htmlHelper = new HtmlHelper(viewContext, viewDataContainer);
// Act & Assert
Assert.ThrowsArgumentNullOrEmpty(
() => htmlHelper.HttpMethodOverride(null),
"httpMethod"
);
Assert.Throws<ArgumentException>(
() => htmlHelper.HttpMethodOverride((HttpVerbs)10000),
@"The specified HttpVerbs value is not supported. The supported values are Delete, Head, Put, Options, and Patch.
Parameter name: httpVerb"
);
Assert.Throws<ArgumentException>(
() => htmlHelper.HttpMethodOverride(HttpVerbs.Get),
@"The specified HttpVerbs value is not supported. The supported values are Delete, Head, Put, Options, and Patch.
Parameter name: httpVerb"
);
Assert.Throws<ArgumentException>(
() => htmlHelper.HttpMethodOverride(HttpVerbs.Post),
@"The specified HttpVerbs value is not supported. The supported values are Delete, Head, Put, Options, and Patch.
Parameter name: httpVerb"
);
Assert.Throws<ArgumentException>(
() => htmlHelper.HttpMethodOverride("gEt"),
@"The GET and POST HTTP methods are not supported.
Parameter name: httpMethod"
);
Assert.Throws<ArgumentException>(
() => htmlHelper.HttpMethodOverride("pOsT"),
@"The GET and POST HTTP methods are not supported.
Parameter name: httpMethod"
);
}
[Fact]
public void HttpMethodOverrideWithMethodRendersHiddenField()
{
// Arrange
var viewContext = new Mock<ViewContext>().Object;
var viewDataContainer = MvcHelper.GetViewDataContainer(null);
var htmlHelper = new HtmlHelper(viewContext, viewDataContainer);
// Act
MvcHtmlString hiddenField = htmlHelper.HttpMethodOverride("PUT");
// Assert
Assert.Equal(@"<input name=""X-HTTP-Method-Override"" type=""hidden"" value=""PUT"" />", hiddenField.ToHtmlString());
}
[Fact]
public void HttpMethodOverrideWithVerbRendersHiddenField()
{
// Arrange
var viewContext = new Mock<ViewContext>().Object;
var viewDataContainer = MvcHelper.GetViewDataContainer(null);
var htmlHelper = new HtmlHelper(viewContext, viewDataContainer);
// Act
MvcHtmlString hiddenField = htmlHelper.HttpMethodOverride(HttpVerbs.Delete);
// Assert
Assert.Equal(@"<input name=""X-HTTP-Method-Override"" type=""hidden"" value=""DELETE"" />", hiddenField.ToHtmlString());
}
[Fact]
public void ViewBagProperty_ReflectsViewData()
{
// Arrange
Mock<IViewDataContainer> viewDataContainer = new Mock<IViewDataContainer>();
ViewDataDictionary viewDataDictionary = new ViewDataDictionary() { { "A", 1 } };
viewDataContainer.Setup(container => container.ViewData).Returns(viewDataDictionary);
// Act
HtmlHelper htmlHelper = new HtmlHelper(new Mock<ViewContext>().Object, viewDataContainer.Object);
// Assert
Assert.Equal(1, htmlHelper.ViewBag.A);
}
[Fact]
public void ViewBagProperty_ReflectsNewViewDataContainerInstance()
{
// Arrange
ViewDataDictionary viewDataDictionary = new ViewDataDictionary() { { "A", 1 } };
Mock<IViewDataContainer> viewDataContainer = new Mock<IViewDataContainer>();
viewDataContainer.Setup(container => container.ViewData).Returns(viewDataDictionary);
ViewDataDictionary otherViewDataDictionary = new ViewDataDictionary() { { "A", 2 } };
Mock<IViewDataContainer> otherViewDataContainer = new Mock<IViewDataContainer>();
otherViewDataContainer.Setup(container => container.ViewData).Returns(otherViewDataDictionary);
HtmlHelper htmlHelper = new HtmlHelper(new Mock<ViewContext>().Object, viewDataContainer.Object, new RouteCollection());
// Act
htmlHelper.ViewDataContainer = otherViewDataContainer.Object;
// Assert
Assert.Equal(2, htmlHelper.ViewBag.A);
}
[Fact]
public void ViewBag_PropagatesChangesToViewData()
{
// Arrange
ViewDataDictionary viewDataDictionary = new ViewDataDictionary() { { "A", 1 } };
Mock<IViewDataContainer> viewDataContainer = new Mock<IViewDataContainer>();
viewDataContainer.Setup(container => container.ViewData).Returns(viewDataDictionary);
HtmlHelper htmlHelper = new HtmlHelper(new Mock<ViewContext>().Object, viewDataContainer.Object, new RouteCollection());
// Act
htmlHelper.ViewBag.A = "foo";
htmlHelper.ViewBag.B = 2;
// Assert
Assert.Equal("foo", htmlHelper.ViewData["A"]);
Assert.Equal(2, htmlHelper.ViewData["B"]);
}
// Unobtrusive validation attributes
[Fact]
public void GetUnobtrusiveValidationAttributesReturnsEmptySetWhenClientValidationIsNotEnabled()
{
// Arrange
var formContext = new FormContext();
formContext.RenderedField("foobar", true);
var viewContext = new Mock<ViewContext>();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
var viewDataContainer = MvcHelper.GetViewDataContainer(new ViewDataDictionary());
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
// Act
IDictionary<string, object> result = htmlHelper.GetUnobtrusiveValidationAttributes("foobar");
// Assert
Assert.Empty(result);
}
[Fact]
public void GetUnobtrusiveValidationAttributesReturnsEmptySetWhenUnobtrusiveJavaScriptIsNotEnabled()
{
// Arrange
var formContext = new FormContext();
formContext.RenderedField("foobar", true);
var viewContext = new Mock<ViewContext>();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
viewContext.SetupGet(vc => vc.ClientValidationEnabled).Returns(true);
var viewDataContainer = MvcHelper.GetViewDataContainer(new ViewDataDictionary());
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
// Act
IDictionary<string, object> result = htmlHelper.GetUnobtrusiveValidationAttributes("foobar");
// Assert
Assert.Empty(result);
}
[Fact]
public void GetUnobtrusiveValidationAttributesReturnsEmptySetWhenFieldHasAlreadyBeenRendered()
{
// Arrange
var formContext = new FormContext();
formContext.RenderedField("foobar", true);
var viewContext = new Mock<ViewContext>();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
viewContext.SetupGet(vc => vc.ClientValidationEnabled).Returns(true);
viewContext.SetupGet(vc => vc.UnobtrusiveJavaScriptEnabled).Returns(true);
var viewDataContainer = MvcHelper.GetViewDataContainer(new ViewDataDictionary());
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
// Act
IDictionary<string, object> result = htmlHelper.GetUnobtrusiveValidationAttributes("foobar");
// Assert
Assert.Empty(result);
}
[Fact]
public void GetUnobtrusiveValidationAttributesReturnsEmptySetAndSetsFieldAsRenderedForFieldWithNoClientRules()
{
// Arrange
var formContext = new FormContext();
var viewContext = new Mock<ViewContext>();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
viewContext.SetupGet(vc => vc.ClientValidationEnabled).Returns(true);
viewContext.SetupGet(vc => vc.UnobtrusiveJavaScriptEnabled).Returns(true);
var viewDataContainer = MvcHelper.GetViewDataContainer(new ViewDataDictionary());
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
htmlHelper.ClientValidationRuleFactory = delegate { return Enumerable.Empty<ModelClientValidationRule>(); };
// Act
IDictionary<string, object> result = htmlHelper.GetUnobtrusiveValidationAttributes("foobar");
// Assert
Assert.Empty(result);
Assert.True(formContext.RenderedField("foobar"));
}
[Fact]
public void GetUnobtrusiveValidationAttributesIncludesDataValTrueWithNonEmptyClientRuleList()
{
// Arrange
var formContext = new FormContext();
var viewContext = new Mock<ViewContext>();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
viewContext.SetupGet(vc => vc.ClientValidationEnabled).Returns(true);
viewContext.SetupGet(vc => vc.UnobtrusiveJavaScriptEnabled).Returns(true);
var viewDataContainer = MvcHelper.GetViewDataContainer(new ViewDataDictionary());
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
htmlHelper.ClientValidationRuleFactory = delegate { return new[] { new ModelClientValidationRule { ValidationType = "type" } }; };
// Act
IDictionary<string, object> result = htmlHelper.GetUnobtrusiveValidationAttributes("foobar");
// Assert
Assert.Equal("true", result["data-val"]);
}
[Fact]
public void GetUnobtrusiveValidationAttributesWithEmptyMessage()
{
// Arrange
var formContext = new FormContext();
var viewContext = new Mock<ViewContext>();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
viewContext.SetupGet(vc => vc.ClientValidationEnabled).Returns(true);
viewContext.SetupGet(vc => vc.UnobtrusiveJavaScriptEnabled).Returns(true);
var viewDataContainer = MvcHelper.GetViewDataContainer(new ViewDataDictionary());
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
htmlHelper.ClientValidationRuleFactory = delegate { return new[] { new ModelClientValidationRule { ValidationType = "type" } }; };
// Act
IDictionary<string, object> result = htmlHelper.GetUnobtrusiveValidationAttributes("foobar");
// Assert
Assert.Equal("", result["data-val-type"]);
}
[Fact]
public void GetUnobtrusiveValidationAttributesMessageIsHtmlEncoded()
{
// Arrange
var formContext = new FormContext();
var viewContext = new Mock<ViewContext>();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
viewContext.SetupGet(vc => vc.ClientValidationEnabled).Returns(true);
viewContext.SetupGet(vc => vc.UnobtrusiveJavaScriptEnabled).Returns(true);
var viewDataContainer = MvcHelper.GetViewDataContainer(new ViewDataDictionary());
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
htmlHelper.ClientValidationRuleFactory = delegate { return new[] { new ModelClientValidationRule { ValidationType = "type", ErrorMessage = "<script>alert('xss')</script>" } }; };
// Act
IDictionary<string, object> result = htmlHelper.GetUnobtrusiveValidationAttributes("foobar");
// Assert
Assert.Equal("&lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;", result["data-val-type"]);
}
[Fact]
public void GetUnobtrusiveValidationAttributesWithMessageAndParameters()
{
// Arrange
var formContext = new FormContext();
var viewContext = new Mock<ViewContext>();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
viewContext.SetupGet(vc => vc.ClientValidationEnabled).Returns(true);
viewContext.SetupGet(vc => vc.UnobtrusiveJavaScriptEnabled).Returns(true);
var viewDataContainer = MvcHelper.GetViewDataContainer(new ViewDataDictionary());
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
htmlHelper.ClientValidationRuleFactory = delegate
{
ModelClientValidationRule rule = new ModelClientValidationRule { ValidationType = "type", ErrorMessage = "error" };
rule.ValidationParameters["foo"] = "bar";
rule.ValidationParameters["baz"] = "biff";
return new[] { rule };
};
// Act
IDictionary<string, object> result = htmlHelper.GetUnobtrusiveValidationAttributes("foobar");
// Assert
Assert.Equal("error", result["data-val-type"]);
Assert.Equal("bar", result["data-val-type-foo"]);
Assert.Equal("biff", result["data-val-type-baz"]);
}
[Fact]
public void GetUnobtrusiveValidationAttributesWithTwoClientRules()
{
// Arrange
var formContext = new FormContext();
var viewContext = new Mock<ViewContext>();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
viewContext.SetupGet(vc => vc.ClientValidationEnabled).Returns(true);
viewContext.SetupGet(vc => vc.UnobtrusiveJavaScriptEnabled).Returns(true);
var viewDataContainer = MvcHelper.GetViewDataContainer(new ViewDataDictionary());
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
htmlHelper.ClientValidationRuleFactory = delegate
{
ModelClientValidationRule rule1 = new ModelClientValidationRule { ValidationType = "type", ErrorMessage = "error" };
rule1.ValidationParameters["foo"] = "bar";
rule1.ValidationParameters["baz"] = "biff";
ModelClientValidationRule rule2 = new ModelClientValidationRule { ValidationType = "othertype", ErrorMessage = "othererror" };
rule2.ValidationParameters["true3"] = "false4";
return new[] { rule1, rule2 };
};
// Act
IDictionary<string, object> result = htmlHelper.GetUnobtrusiveValidationAttributes("foobar");
// Assert
Assert.Equal("error", result["data-val-type"]);
Assert.Equal("bar", result["data-val-type-foo"]);
Assert.Equal("biff", result["data-val-type-baz"]);
Assert.Equal("othererror", result["data-val-othertype"]);
Assert.Equal("false4", result["data-val-othertype-true3"]);
}
[Fact]
public void GetUnobtrusiveValidationAttributesUsesShortNameForModelMetadataLookup()
{
// Arrange
string passedName = null;
var formContext = new FormContext();
var viewContext = new Mock<ViewContext>();
var viewData = new ViewDataDictionary();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
viewContext.SetupGet(vc => vc.ClientValidationEnabled).Returns(true);
viewContext.SetupGet(vc => vc.UnobtrusiveJavaScriptEnabled).Returns(true);
viewData.TemplateInfo.HtmlFieldPrefix = "Prefix";
var viewDataContainer = MvcHelper.GetViewDataContainer(viewData);
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
htmlHelper.ClientValidationRuleFactory = (name, _) =>
{
passedName = name;
return Enumerable.Empty<ModelClientValidationRule>();
};
// Act
htmlHelper.GetUnobtrusiveValidationAttributes("foobar");
// Assert
Assert.Equal("foobar", passedName);
}
[Fact]
public void GetUnobtrusiveValidationAttributeUsesViewDataForModelMetadataLookup()
{
// Arrange
var formContext = new FormContext();
var viewContext = new Mock<ViewContext>();
var viewData = new ViewDataDictionary<MyModel>();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
viewContext.SetupGet(vc => vc.ClientValidationEnabled).Returns(true);
viewContext.SetupGet(vc => vc.UnobtrusiveJavaScriptEnabled).Returns(true);
viewData.TemplateInfo.HtmlFieldPrefix = "Prefix";
var viewDataContainer = MvcHelper.GetViewDataContainer(viewData);
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
// Act
IDictionary<string, object> result = htmlHelper.GetUnobtrusiveValidationAttributes("MyProperty");
// Assert
Assert.Equal(2, result.Count);
Assert.Equal("true", result["data-val"]);
Assert.Equal("My required message", result["data-val-required"]);
}
class MyModel
{
[Required(ErrorMessage = "My required message")]
public object MyProperty { get; set; }
}
[Fact]
public void GetUnobtrusiveValidationAttributesMarksRenderedFieldsWithFullName()
{
// Arrange
var formContext = new FormContext();
var viewContext = new Mock<ViewContext>();
var viewData = new ViewDataDictionary();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
viewContext.SetupGet(vc => vc.ClientValidationEnabled).Returns(true);
viewContext.SetupGet(vc => vc.UnobtrusiveJavaScriptEnabled).Returns(true);
viewData.TemplateInfo.HtmlFieldPrefix = "Prefix";
var viewDataContainer = MvcHelper.GetViewDataContainer(viewData);
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
// Act
htmlHelper.GetUnobtrusiveValidationAttributes("foobar");
// Assert
Assert.False(formContext.RenderedField("foobar"));
Assert.True(formContext.RenderedField("Prefix.foobar"));
}
[Fact]
public void GetUnobtrusiveValidationAttributesGuardClauses()
{
// Arrange
var formContext = new FormContext();
var viewContext = new Mock<ViewContext>();
viewContext.SetupGet(vc => vc.FormContext).Returns(formContext);
viewContext.SetupGet(vc => vc.ClientValidationEnabled).Returns(true);
viewContext.SetupGet(vc => vc.UnobtrusiveJavaScriptEnabled).Returns(true);
var viewDataContainer = MvcHelper.GetViewDataContainer(new ViewDataDictionary());
var htmlHelper = new HtmlHelper(viewContext.Object, viewDataContainer);
// Act & Assert
AssertBadClientValidationRule(htmlHelper, "Validation type names in unobtrusive client validation rules cannot be empty. Client rule type: System.Web.Mvc.ModelClientValidationRule", new ModelClientValidationRule());
AssertBadClientValidationRule(htmlHelper, "Validation type names in unobtrusive client validation rules must consist of only lowercase letters. Invalid name: \"OnlyLowerCase\", client rule type: System.Web.Mvc.ModelClientValidationRule", new ModelClientValidationRule { ValidationType = "OnlyLowerCase" });
AssertBadClientValidationRule(htmlHelper, "Validation type names in unobtrusive client validation rules must consist of only lowercase letters. Invalid name: \"nonumb3rs\", client rule type: System.Web.Mvc.ModelClientValidationRule", new ModelClientValidationRule { ValidationType = "nonumb3rs" });
AssertBadClientValidationRule(htmlHelper, "Validation type names in unobtrusive client validation rules must be unique. The following validation type was seen more than once: rule", new ModelClientValidationRule { ValidationType = "rule" }, new ModelClientValidationRule { ValidationType = "rule" });
var emptyParamName = new ModelClientValidationRule { ValidationType = "type" };
emptyParamName.ValidationParameters[""] = "foo";
AssertBadClientValidationRule(htmlHelper, "Validation parameter names in unobtrusive client validation rules cannot be empty. Client rule type: System.Web.Mvc.ModelClientValidationRule", emptyParamName);
var paramNameMixedCase = new ModelClientValidationRule { ValidationType = "type" };
paramNameMixedCase.ValidationParameters["MixedCase"] = "foo";
AssertBadClientValidationRule(htmlHelper, "Validation parameter names in unobtrusive client validation rules must start with a lowercase letter and consist of only lowercase letters or digits. Validation parameter name: MixedCase, client rule type: System.Web.Mvc.ModelClientValidationRule", paramNameMixedCase);
var paramNameStartsWithNumber = new ModelClientValidationRule { ValidationType = "type" };
paramNameStartsWithNumber.ValidationParameters["2112"] = "foo";
AssertBadClientValidationRule(htmlHelper, "Validation parameter names in unobtrusive client validation rules must start with a lowercase letter and consist of only lowercase letters or digits. Validation parameter name: 2112, client rule type: System.Web.Mvc.ModelClientValidationRule", paramNameStartsWithNumber);
}
[Fact]
public void RawReturnsWrapperMarkup()
{
// Arrange
var viewContext = new Mock<ViewContext>().Object;
var viewDataContainer = new Mock<IViewDataContainer>().Object;
var htmlHelper = new HtmlHelper(viewContext, viewDataContainer);
string markup = "<b>bold</b>";
// Act
IHtmlString markupHtml = htmlHelper.Raw(markup);
// Assert
Assert.Equal("<b>bold</b>", markupHtml.ToString());
Assert.Equal("<b>bold</b>", markupHtml.ToHtmlString());
}
[Fact]
public void RawAllowsNullValue()
{
// Arrange
var viewContext = new Mock<ViewContext>().Object;
var viewDataContainer = new Mock<IViewDataContainer>().Object;
var htmlHelper = new HtmlHelper(viewContext, viewDataContainer);
// Act
IHtmlString markupHtml = htmlHelper.Raw(null);
// Assert
Assert.Equal(null, markupHtml.ToString());
Assert.Equal(null, markupHtml.ToHtmlString());
}
[Fact]
public void RawAllowsNullObjectValue()
{
// Arrange
var viewContext = new Mock<ViewContext>().Object;
var viewDataContainer = new Mock<IViewDataContainer>().Object;
var htmlHelper = new HtmlHelper(viewContext, viewDataContainer);
// Act
IHtmlString markupHtml = htmlHelper.Raw((object)null);
// Assert
Assert.Equal(null, markupHtml.ToString());
Assert.Equal(null, markupHtml.ToHtmlString());
}
[Fact]
public void RawAllowsEmptyValue()
{
// Arrange
var viewContext = new Mock<ViewContext>().Object;
var viewDataContainer = new Mock<IViewDataContainer>().Object;
var htmlHelper = new HtmlHelper(viewContext, viewDataContainer);
// Act
IHtmlString markupHtml = htmlHelper.Raw("");
// Assert
Assert.Equal("", markupHtml.ToString());
Assert.Equal("", markupHtml.ToHtmlString());
}
[Fact]
public void RawReturnsWrapperMarkupOfObject()
{
// Arrange
var viewContext = new Mock<ViewContext>().Object;
var viewDataContainer = new Mock<IViewDataContainer>().Object;
var htmlHelper = new HtmlHelper(viewContext, viewDataContainer);
ObjectWithWrapperMarkup obj = new ObjectWithWrapperMarkup();
// Act
IHtmlString markupHtml = htmlHelper.Raw(obj);
// Assert
Assert.Equal("<b>boldFromObject</b>", markupHtml.ToString());
Assert.Equal("<b>boldFromObject</b>", markupHtml.ToHtmlString());
}
[Fact]
public void EvalStringAndFormatValueWithNullValueReturnsEmptyString()
{
// Arrange
var htmlHelper = MvcHelper.GetHtmlHelper(new ViewDataDictionary());
// Act & Assert
Assert.Equal(String.Empty, htmlHelper.FormatValue(null, "-{0}-"));
Assert.Equal(String.Empty, htmlHelper.EvalString("nonExistant"));
Assert.Equal(String.Empty, htmlHelper.EvalString("nonExistant", "-{0}-"));
}
[Fact]
public void EvalStringAndFormatValueUseCurrentCulture()
{
// Arrange
DateTime dt = new DateTime(1900, 1, 1, 0, 0, 0);
HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(new ViewDataDictionary() { { "date", dt } });
string expectedFormattedDate = "-1900/01/01 12:00:00 AM-";
// Act && Assert
using (ReplaceCulture("en-ZA", "en-US"))
{
Assert.Equal(expectedFormattedDate, htmlHelper.FormatValue(dt, "-{0}-"));
Assert.Equal(expectedFormattedDate, htmlHelper.EvalString("date", "-{0}-"));
}
}
[Fact]
public void EvalStringAndFormatValueWithEmptyFormatConvertsValueToString()
{
// Arrange
DateTime dt = new DateTime(1900, 1, 1, 0, 0, 0);
HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(new ViewDataDictionary() { { "date", dt } });
string expectedUnformattedDate = "1900/01/01 12:00:00 AM";
// Act && Assert
using (ReplaceCulture("en-ZA", "en-US"))
{
Assert.Equal(expectedUnformattedDate, htmlHelper.FormatValue(dt, String.Empty));
Assert.Equal(expectedUnformattedDate, htmlHelper.EvalString("date", String.Empty));
Assert.Equal(expectedUnformattedDate, htmlHelper.EvalString("date"));
}
}
private class ObjectWithWrapperMarkup
{
public override string ToString()
{
return "<b>boldFromObject</b>";
}
}
// Helpers
private static void AssertBadClientValidationRule(HtmlHelper htmlHelper, string expectedMessage, params ModelClientValidationRule[] rules)
{
htmlHelper.ClientValidationRuleFactory = delegate { return rules; };
Assert.Throws<InvalidOperationException>(
() => htmlHelper.GetUnobtrusiveValidationAttributes(Guid.NewGuid().ToString()),
expectedMessage
);
}
internal static ValueProviderResult GetValueProviderResult(object rawValue, string attemptedValue)
{
return new ValueProviderResult(rawValue, attemptedValue, CultureInfo.InvariantCulture);
}
internal static IDisposable ReplaceCulture(string currentCulture, string currentUICulture)
{
CultureInfo newCulture = CultureInfo.GetCultureInfo(currentCulture);
CultureInfo newUICulture = CultureInfo.GetCultureInfo(currentUICulture);
CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture;
CultureInfo originalUICulture = Thread.CurrentThread.CurrentUICulture;
Thread.CurrentThread.CurrentCulture = newCulture;
Thread.CurrentThread.CurrentUICulture = newUICulture;
return new CultureReplacement { OriginalCulture = originalCulture, OriginalUICulture = originalUICulture };
}
private class CultureReplacement : IDisposable
{
public CultureInfo OriginalCulture;
public CultureInfo OriginalUICulture;
public void Dispose()
{
Thread.CurrentThread.CurrentCulture = OriginalCulture;
Thread.CurrentThread.CurrentUICulture = OriginalUICulture;
}
}
private class TestableHtmlHelper : HtmlHelper
{
TestableHtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer)
: base(viewContext, viewDataContainer)
{
}
public static TestableHtmlHelper Create()
{
ViewDataDictionary viewData = new ViewDataDictionary();
Mock<ViewContext> mockViewContext = new Mock<ViewContext>() { DefaultValue = DefaultValue.Mock };
mockViewContext.Setup(c => c.HttpContext.Response.Output).Throws(new Exception("Response.Output should never be called."));
mockViewContext.Setup(c => c.ViewData).Returns(viewData);
mockViewContext.Setup(c => c.Writer).Returns(new StringWriter());
Mock<IViewDataContainer> container = new Mock<IViewDataContainer>();
container.Setup(c => c.ViewData).Returns(viewData);
return new TestableHtmlHelper(mockViewContext.Object, container.Object);
}
public void RenderPartialInternal(string partialViewName,
ViewDataDictionary viewData,
object model,
TextWriter writer,
params IViewEngine[] engines)
{
base.RenderPartialInternal(partialViewName, viewData, model, writer, new ViewEngineCollection(engines));
}
}
}
}